Files
TheriapolisV3/Theriapolis.Core/Data/BiomeDef.cs
T

81 lines
3.7 KiB
C#
Raw Normal View History

using System.Text.Json.Serialization;
namespace Theriapolis.Core.Data;
/// <summary>
/// Immutable biome definition loaded from biomes.json.
/// Defines the biome's identity, visual representation, and the (e,m,t) ranges
/// that can produce it during BiomeAssign.
/// </summary>
public sealed record BiomeDef
{
[JsonPropertyName("id")]
public string Id { get; init; } = "";
[JsonPropertyName("display_name")]
public string DisplayName { get; init; } = "";
/// <summary>Single capital letter used in placeholder tile rendering.</summary>
[JsonPropertyName("letter")]
public char Letter { get; init; } = '?';
/// <summary>Hex color string (#RRGGBB) for the placeholder tile background.</summary>
[JsonPropertyName("color")]
public string Color { get; init; } = "#888888";
[JsonPropertyName("placeholder_sprite")]
public string PlaceholderSprite { get; init; } = "";
// ── Assignment thresholds ────────────────────────────────────────────────
// These are used only for "natural" biome assignment.
// Ocean is handled separately (elevation < sea_level).
[JsonPropertyName("elevation_min")] public float ElevationMin { get; init; } = 0f;
[JsonPropertyName("elevation_max")] public float ElevationMax { get; init; } = 1f;
[JsonPropertyName("moisture_min")] public float MoistureMin { get; init; } = 0f;
[JsonPropertyName("moisture_max")] public float MoistureMax { get; init; } = 1f;
[JsonPropertyName("temp_min")] public float TempMin { get; init; } = 0f;
[JsonPropertyName("temp_max")] public float TempMax { get; init; } = 1f;
/// <summary>Priority — higher-priority biomes win when multiple match.</summary>
[JsonPropertyName("priority")]
public int Priority { get; init; } = 0;
/// <summary>True if this is a transition/mixed biome (not assignable from base rules).</summary>
[JsonPropertyName("is_transition")]
public bool IsTransition { get; init; } = false;
// ── Parsed color cache ───────────────────────────────────────────────────
private (byte R, byte G, byte B)? _parsedColor;
public (byte R, byte G, byte B) ParsedColor()
{
if (_parsedColor.HasValue) return _parsedColor.Value;
string hex = Color.TrimStart('#');
byte r = Convert.ToByte(hex[..2], 16);
byte g = Convert.ToByte(hex[2..4], 16);
byte b = Convert.ToByte(hex[4..6], 16);
_parsedColor = (r, g, b);
return _parsedColor.Value;
}
/// <summary>How well this biome matches the given (e, m, t) values. Returns 0 if outside range.</summary>
public float Score(float e, float m, float t)
{
if (e < ElevationMin || e > ElevationMax) return 0f;
if (m < MoistureMin || m > MoistureMax) return 0f;
if (t < TempMin || t > TempMax) return 0f;
// Score = how close the values are to the center of the range (prefer tighter fits)
float eMid = (ElevationMin + ElevationMax) * 0.5f;
float mMid = (MoistureMin + MoistureMax) * 0.5f;
float tMid = (TempMin + TempMax) * 0.5f;
float eHalf = (ElevationMax - ElevationMin) * 0.5f + 0.001f;
float mHalf = (MoistureMax - MoistureMin) * 0.5f + 0.001f;
float tHalf = (TempMax - TempMin) * 0.5f + 0.001f;
float closeness = 1f - (MathF.Abs(e - eMid)/eHalf + MathF.Abs(m - mMid)/mHalf + MathF.Abs(t - tMid)/tHalf) / 3f;
return closeness + Priority * 0.5f;
}
}