91 lines
3.9 KiB
C#
91 lines
3.9 KiB
C#
|
|
using System.Text.Json.Serialization;
|
|||
|
|
|
|||
|
|
namespace Theriapolis.Core.Data;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Phase 6 M0 — describes how to lay buildings inside a settlement footprint.
|
|||
|
|
///
|
|||
|
|
/// Two flavours:
|
|||
|
|
/// 1. **Hand-authored preset** — `kind == "preset"`. The <see cref="Buildings"/>
|
|||
|
|
/// array specifies each building by template id and offset from the
|
|||
|
|
/// settlement centre. Used for narrative anchors (Millhaven, Thornfield).
|
|||
|
|
/// 2. **Procedural rule-based** — `kind == "procedural"`. The <see cref="Roles"/>
|
|||
|
|
/// array specifies a mix of category weights ("inn" 0.1, "shop" 0.3,
|
|||
|
|
/// "house" 0.6) and a target building count; <see cref="World.Settlements.SettlementStamper"/>
|
|||
|
|
/// rolls templates from the matching categories until the target count is
|
|||
|
|
/// met or no more building slots fit inside the plaza radius.
|
|||
|
|
///
|
|||
|
|
/// Bound to a settlement either by anchor name (preset) or by tier
|
|||
|
|
/// (procedural fallback for any non-anchor settlement of that tier).
|
|||
|
|
/// </summary>
|
|||
|
|
public sealed record SettlementLayoutDef
|
|||
|
|
{
|
|||
|
|
[JsonPropertyName("id")]
|
|||
|
|
public string Id { get; init; } = "";
|
|||
|
|
|
|||
|
|
/// <summary>"preset" or "procedural".</summary>
|
|||
|
|
[JsonPropertyName("kind")]
|
|||
|
|
public string Kind { get; init; } = "procedural";
|
|||
|
|
|
|||
|
|
/// <summary>For preset layouts: matches Settlement.Anchor.ToString() (e.g. "Millhaven").</summary>
|
|||
|
|
[JsonPropertyName("anchor")]
|
|||
|
|
public string Anchor { get; init; } = "";
|
|||
|
|
|
|||
|
|
/// <summary>For procedural layouts: matches Settlement.Tier (1–5).</summary>
|
|||
|
|
[JsonPropertyName("tier")]
|
|||
|
|
public int Tier { get; init; } = 0;
|
|||
|
|
|
|||
|
|
// ── Preset payload ─────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
/// <summary>Building placements for preset layouts. Ignored when kind == "procedural".</summary>
|
|||
|
|
[JsonPropertyName("buildings")]
|
|||
|
|
public SettlementBuildingPlacement[] Buildings { get; init; } = Array.Empty<SettlementBuildingPlacement>();
|
|||
|
|
|
|||
|
|
// ── Procedural payload ─────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
/// <summary>Category mix for procedural layouts. Ignored when kind == "preset".</summary>
|
|||
|
|
[JsonPropertyName("category_weights")]
|
|||
|
|
public Dictionary<string, float> CategoryWeights { get; init; } = new();
|
|||
|
|
|
|||
|
|
/// <summary>Target building count for procedural layouts.</summary>
|
|||
|
|
[JsonPropertyName("target_building_count")]
|
|||
|
|
public int TargetBuildingCount { get; init; } = 5;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Plaza radius in tactical tiles to search for building slots (procedural).
|
|||
|
|
/// If 0, the stamper picks one based on tier.
|
|||
|
|
/// </summary>
|
|||
|
|
[JsonPropertyName("plaza_radius_tiles")]
|
|||
|
|
public int PlazaRadiusTiles { get; init; } = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public sealed record SettlementBuildingPlacement
|
|||
|
|
{
|
|||
|
|
/// <summary>BuildingTemplateDef.Id to stamp.</summary>
|
|||
|
|
[JsonPropertyName("template")]
|
|||
|
|
public string Template { get; init; } = "";
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Offset from settlement centre in tactical tiles. (0,0) places the
|
|||
|
|
/// building's centre on the settlement centre tile.
|
|||
|
|
/// </summary>
|
|||
|
|
[JsonPropertyName("offset")]
|
|||
|
|
public int[] Offset { get; init; } = new[] { 0, 0 };
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Optional rotation: 0 / 90 / 180 / 270. Phase 6 M0 ignores; reserved
|
|||
|
|
/// so layouts don't have to be re-authored when rotation lands.
|
|||
|
|
/// </summary>
|
|||
|
|
[JsonPropertyName("rotation_deg")]
|
|||
|
|
public int RotationDeg { get; init; } = 0;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Override the role tag for one or more roles in this building. E.g.
|
|||
|
|
/// the innkeeper template has role "innkeeper"; this preset assigns
|
|||
|
|
/// it the named role "millhaven.innkeeper" so quest scripts can
|
|||
|
|
/// reference the specific NPC.
|
|||
|
|
/// </summary>
|
|||
|
|
[JsonPropertyName("role_overrides")]
|
|||
|
|
public Dictionary<string, string> RoleOverrides { get; init; } = new();
|
|||
|
|
}
|