namespace Theriapolis.Core.Persistence;
///
/// Plain-data snapshot of a for
/// the save layer. Kept as primitive fields + arrays so the persistence
/// codec doesn't pull MessagePack attributes onto the live model.
///
/// Items / equipped slots reference content by id (string); the loader
/// re-resolves them against the current set
/// after world content reloads.
///
public sealed class PlayerCharacterState
{
public string CladeId { get; set; } = "";
public string SpeciesId { get; set; } = "";
public string ClassId { get; set; } = "";
public string BackgroundId { get; set; } = "";
// Final ability scores (post clade + species mods).
public byte STR { get; set; }
public byte DEX { get; set; }
public byte CON { get; set; }
public byte INT { get; set; }
public byte WIS { get; set; }
public byte CHA { get; set; }
public int Level { get; set; } = 1;
public int Xp { get; set; } = 0;
public int MaxHp { get; set; }
public int CurrentHp { get; set; }
public int ExhaustionLevel { get; set; } = 0;
// Phase 5 M6 additions — backward-compat handled by EndOfStream check in SaveCodec.ReadCharacter.
public string FightingStyle { get; set; } = "";
public int RageUsesRemaining { get; set; } = 2;
// Phase 6 M3 — coin balance (Fangs are Theriapolis's universal currency).
public int CurrencyFang { get; set; } = 0;
// Phase 6.5 M0 — levelling state.
///
/// Subclass id chosen at level 3 (or whatever
/// is). Empty pre-L3.
///
public string SubclassId { get; set; } = "";
/// Feature ids learned across all level-ups, in unlock order.
public string[] LearnedFeatureIds { get; set; } = Array.Empty();
/// Append-only per-level-up history (deltas, not post-state).
public LevelUpRecordState[] LevelUpHistory { get; set; } = Array.Empty();
// Phase 6.5 M4 — hybrid-character genealogy. Null for purebred PCs.
public HybridStateSnapshot? Hybrid { get; set; }
/// Skill ids stored as enum byte values for compactness.
public byte[] SkillProficiencies { get; set; } = Array.Empty();
/// Active conditions stored as enum byte values.
public byte[] Conditions { get; set; } = Array.Empty();
/// Inventory stacks — references to ItemDef.Id with quantity + condition + optional equip slot.
public InventoryItemState[] Inventory { get; set; } = Array.Empty();
}
///
/// Phase 6.5 M0 — one entry in .
/// Plain-data round-trip of .
///
public sealed class LevelUpRecordState
{
public int Level { get; set; }
public int HpGained { get; set; }
public bool HpWasAveraged { get; set; }
public int HpHitDieResult { get; set; }
/// Empty when no subclass was chosen at this level (i.e. anything but L3).
public string SubclassChosen { get; set; } = "";
/// ASI ability ids stored as enum byte values; same length as .
public byte[] AsiKeys { get; set; } = Array.Empty();
public int[] AsiValues { get; set; } = Array.Empty();
public string[] FeaturesUnlocked { get; set; } = Array.Empty();
}
///
/// Phase 6.5 M4 — flat round-trip record for .
///
public sealed class HybridStateSnapshot
{
public string SireClade { get; set; } = "";
public string SireSpecies { get; set; } = "";
public string DamClade { get; set; } = "";
public string DamSpecies { get; set; } = "";
/// 0 = Sire, 1 = Dam (matches byte values).
public byte DominantParent { get; set; } = 0;
public bool PassingActive { get; set; } = false;
/// Per-NPC discovery list — populated in Phase 6.5 M5.
public int[] NpcsWhoKnow { get; set; } = Array.Empty();
/// Active scent-mask tier (Phase 6.5 M5). 0 = none.
public byte ActiveMaskTier { get; set; } = 0;
}
/// One stack in .
public sealed class InventoryItemState
{
public string ItemId { get; set; } = "";
public int Qty { get; set; } = 1;
public int Condition { get; set; } = 100;
/// Null if this stack is bagged. Otherwise the EquipSlot enum byte value.
public byte? EquippedAt { get; set; }
}