Code/V2/Application/CharacterCreationContracts.cs

Domain contract types for character creation and deletion. Defines request/context records, planning types for initial character state, item and inventory grant plans, initializer interface, model catalog interface, and event/receipt records used by the creation pipeline.

#nullable enable

using System;
using System.Collections.Generic;
using Hexagon.V2.Domain;
using Hexagon.V2.Kernel;
using Hexagon.V2.Persistence;

namespace Hexagon.V2.Application;

public sealed record CharacterCreationContext(
	AccountId AccountId,
	CharacterCreationRequest Request,
	int Slot,
	DateTimeOffset NowUtc );

public sealed record CharacterStatePlan( TypedPayload State, long StartingBalance );

/// <summary>
/// Computes schema-owned state from authenticated context and registered creation
/// values. The request never contains framework-managed rank, balance or private state.
/// </summary>
public interface ICharacterStateFactory
{
	OperationResult<CharacterStatePlan> Create( CharacterCreationContext context );
}

public sealed record ItemGrantPlan
{
	public required DefinitionId Definition { get; init; }
	public IReadOnlyDictionary<string, TypedPayload> Traits { get; init; } =
		new Dictionary<string, TypedPayload>( StringComparer.Ordinal );
	public BagInventoryPlan? Bag { get; init; }
}

public sealed record BagInventoryPlan
{
	public required int Width { get; init; }
	public required int Height { get; init; }
	public IReadOnlyList<ItemGrantPlan> Items { get; init; } = Array.Empty<ItemGrantPlan>();
}

public sealed record UniqueReservationPlan( string Namespace, string Value );

public sealed record CharacterInitializerContribution
{
	public IReadOnlyList<ItemGrantPlan> Items { get; init; } = Array.Empty<ItemGrantPlan>();
	public IReadOnlyList<UniqueReservationPlan> Reservations { get; init; } =
		Array.Empty<UniqueReservationPlan>();
}

/// <summary>
/// Initializers are pure planners. Persistence, networking and other side effects
/// are deliberately unavailable at this boundary.
/// </summary>
public interface ICharacterInitializer
{
	string Id { get; }
	int Order { get; }
	OperationResult<CharacterInitializerContribution> Build( CharacterCreationContext context, CharacterStatePlan state );
}

public interface ICharacterModelCatalog
{
	bool IsAllowed( DefinitionId model, FactionId faction, ClassId? characterClass );
}

public sealed record CharacterCreatedEvent(
	CharacterRecord Character,
	InventoryRecord MainInventory,
	IReadOnlyList<InventoryRecord> Inventories,
	IReadOnlyList<ItemRecord> Items,
	CommitReceipt Commit );

public sealed record CharacterDeletedEvent(
	CharacterId CharacterId,
	AccountId AccountId,
	CommitReceipt Commit );

public sealed record CharacterCreationReceipt(
	CharacterRecord Character,
	InventoryRecord MainInventory,
	IReadOnlyList<InventoryRecord> Inventories,
	IReadOnlyList<ItemRecord> Items,
	CommitReceipt Commit );

public sealed record CharacterDeletionReceipt(
	CharacterId CharacterId,
	AccountId AccountId,
	CommitReceipt Commit );

public sealed record CharacterDeletionContext( AccountId ActorAccountId, CharacterRecord Character );