feat(render): implement Campaign AR and terrain fidelity

This commit is contained in:
Erik 2026-08-22 13:13:29 +02:00
parent 99cf26e00c
commit 7a5f96ede5
368 changed files with 50611 additions and 950 deletions

View file

@ -14,6 +14,117 @@ public enum ParticleRange
Extended = 1,
}
/// <summary>
/// Immutable, value-equal user overrides for one selected render pack. Keys
/// are stable setting IDs and compare case-insensitively; values remain the
/// declaration's invariant string representation until descriptor validation.
/// </summary>
public sealed class RenderPackSettingOverrides :
IReadOnlyDictionary<string, string>,
IEquatable<RenderPackSettingOverrides>
{
private readonly SortedDictionary<string, string> _values;
public static RenderPackSettingOverrides Empty { get; } = new([]);
public RenderPackSettingOverrides(
IEnumerable<KeyValuePair<string, string>> values)
{
ArgumentNullException.ThrowIfNull(values);
_values = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach ((string key, string value) in values)
{
ArgumentNullException.ThrowIfNull(key);
ArgumentNullException.ThrowIfNull(value);
_values[key] = value;
}
}
public int Count => _values.Count;
public IEnumerable<string> Keys => _values.Keys;
public IEnumerable<string> Values => _values.Values;
public string this[string key] => _values[key];
public bool ContainsKey(string key) => _values.ContainsKey(key);
public bool TryGetValue(string key, out string value) =>
_values.TryGetValue(key, out value!);
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() =>
_values.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() =>
GetEnumerator();
public RenderPackSettingOverrides Set(string settingId, string value)
{
ArgumentException.ThrowIfNullOrWhiteSpace(settingId);
ArgumentNullException.ThrowIfNull(value);
var next = new SortedDictionary<string, string>(
_values,
StringComparer.OrdinalIgnoreCase)
{
[settingId] = value,
};
return new RenderPackSettingOverrides(next);
}
public bool Equals(RenderPackSettingOverrides? other) =>
other is not null
&& _values.Count == other._values.Count
&& _values.All(pair => other._values.TryGetValue(pair.Key, out string? value)
&& string.Equals(pair.Value, value, StringComparison.Ordinal));
public override bool Equals(object? obj) =>
obj is RenderPackSettingOverrides other && Equals(other);
public override int GetHashCode()
{
var hash = new HashCode();
foreach ((string key, string value) in _values)
{
hash.Add(key, StringComparer.OrdinalIgnoreCase);
hash.Add(value, StringComparer.Ordinal);
}
return hash.ToHashCode();
}
}
/// <summary>
/// Stable, user-authored selection of one optional render pack. Logical ids
/// are persisted instead of menu indexes so discovery order can never select a
/// different pack or preset after an install/update. The renderer normalizes a
/// missing, malformed, unavailable, or incompatible selection back to
/// <see cref="Retail"/> and retains the precise reason for diagnostics.
/// </summary>
public sealed record RenderPackSelectionSettings(
string PackId,
string? PackVersion,
string PresetId)
{
public const string RetailPackId = "retail";
public const string RetailPresetId = "off";
public static RenderPackSelectionSettings Retail { get; } = new(
RetailPackId,
PackVersion: null,
RetailPresetId);
/// <summary>
/// User-authored values keyed by the selected pack's stable setting IDs.
/// Empty by default so pre-render-pack settings files upgrade without a
/// migration write.
/// </summary>
public RenderPackSettingOverrides SettingOverrides { get; init; } =
RenderPackSettingOverrides.Empty;
public bool IsRetail =>
string.Equals(PackId, RetailPackId, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Display-related preferences persisted to <c>settings.json</c>.
/// Originally documented as "no retail equivalent for FOV / vsync etc" —
@ -77,6 +188,16 @@ public sealed record DisplaySettings(
bool BuildingDetailTextures = true,
bool MultiPassAlpha = false)
{
/// <summary>
/// Opt-in graphics enhancement selection. This is deliberately separate
/// from <see cref="Quality"/>, which remains the authoritative retail
/// renderer/streaming quality preset. Keeping the default here means an
/// upgraded settings file that predates shader packs deserializes to the
/// exact retail path without a migration write.
/// </summary>
public RenderPackSelectionSettings RenderPack { get; init; } =
RenderPackSelectionSettings.Retail;
/// <summary>Values used on first launch / when settings.json is absent.
/// Geometry defaults preserve the pre-L.0 runtime state: Resolution
/// matches the WindowOptions startup size (1280×720). FieldOfView is