Files
Christopher Wiebe b451f83174 Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback
anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md).
All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:40:51 -07:00

75 lines
2.5 KiB
C#

namespace Theriapolis.Core.Rules.Reputation;
/// <summary>
/// Phase 6 M2 — append-only event log surfaced by the reputation screen
/// ("why does so-and-so hate me?"). Bounded to a reasonable tail so the
/// save file stays small even after a 100-hour playthrough.
///
/// Phase 6 M5 layers propagation on top: each entry will be re-walked
/// per game-day to fan out into other settlements with distance/time
/// decay.
/// </summary>
public sealed class RepLedger
{
public const int MaxEntries = 256;
private readonly List<RepEvent> _entries = new();
private int _nextSeq = 1;
public IReadOnlyList<RepEvent> Entries => _entries;
public int Count => _entries.Count;
/// <summary>
/// Append <paramref name="ev"/> to the ledger. If <see cref="RepEvent.SequenceId"/>
/// is 0, a fresh monotone id is assigned; otherwise the supplied id
/// is preserved (used by the save-restore path).
/// </summary>
public RepEvent Append(RepEvent ev)
{
if (ev.SequenceId == 0)
ev = ev with { SequenceId = _nextSeq++ };
else if (ev.SequenceId >= _nextSeq)
_nextSeq = ev.SequenceId + 1;
_entries.Add(ev);
if (_entries.Count > MaxEntries) _entries.RemoveAt(0);
return ev;
}
public void Clear()
{
_entries.Clear();
_nextSeq = 1;
}
/// <summary>Largest <see cref="RepEvent.SequenceId"/> issued so far. 0 = empty ledger.</summary>
public int HighestSequenceId => _nextSeq - 1;
/// <summary>Most recent N events affecting <paramref name="factionId"/>.</summary>
public IEnumerable<RepEvent> ForFaction(string factionId, int count = 8)
{
int yielded = 0;
for (int i = _entries.Count - 1; i >= 0 && yielded < count; i--)
{
if (string.Equals(_entries[i].FactionId, factionId, System.StringComparison.OrdinalIgnoreCase))
{
yielded++;
yield return _entries[i];
}
}
}
/// <summary>Most recent N events affecting <paramref name="roleTag"/>.</summary>
public IEnumerable<RepEvent> ForRole(string roleTag, int count = 8)
{
int yielded = 0;
for (int i = _entries.Count - 1; i >= 0 && yielded < count; i--)
{
if (string.Equals(_entries[i].RoleTag, roleTag, System.StringComparison.OrdinalIgnoreCase))
{
yielded++;
yield return _entries[i];
}
}
}
}