namespace AcDream.Core.Journal; /// /// One page of retail's per-character journal. /// /// /// Entirely client-authored: no wire, no server, no dat. The player writes it. /// Retail's own PageInfo (64 bytes, g_JournalPages). /// /// Short name, shown in the Page List. Authored max 16 characters. /// Page title. Authored max 32. /// Free-form body. Authored max 2048. /// Countdown days. /// Countdown hours. /// Countdown minutes. /// /// Recorded location, in retail's own north-south / east-west units — what the /// "Record" button stamps and the page shows as "00.0S, 00.0W". /// /// See . /// /// Whether a location was ever recorded. Distinct from (0, 0), which is a real /// place. /// /// /// The running countdown's remaining seconds, or 0 when the timer is not /// running. Retail's <TIME>. /// public sealed record JournalPage( string Label = "", string Title = "", string Notes = "", int TimerDays = 0, int TimerHours = 0, int TimerMinutes = 0, float LocationX = 0f, float LocationY = 0f, bool HasLocation = false, double RunningTimerSeconds = 0d) { /// Authored 0x1E on the label edit box. public const int MaxLabelLength = 16; /// Authored 0x1E on the title edit box. public const int MaxTitleLength = 32; /// Authored 0x1E on the notes edit box. public const int MaxNotesLength = 2048; /// An untouched page — what "New" produces. public static readonly JournalPage Empty = new(); /// Whether the timer fields describe any duration at all. public bool HasTimer => TimerDays != 0 || TimerHours != 0 || TimerMinutes != 0; /// The timer fields as a single duration. public TimeSpan TimerDuration => new(TimerDays, TimerHours, TimerMinutes, 0); /// Whether a countdown is currently running on this page. public bool IsTimerRunning => RunningTimerSeconds > 0d; /// /// The page with every field clipped to its authored maximum. Applied at /// the seams that accept outside text — a load from disk, or a paste. /// public JournalPage Clipped() => this with { Label = Clip(Label, MaxLabelLength), Title = Clip(Title, MaxTitleLength), Notes = Clip(Notes, MaxNotesLength), }; private static string Clip(string value, int max) => value.Length <= max ? value : value[..max]; }