using System; namespace AcDream.App.UI.Layout; /// /// Snapshot of all character data the Character window report uses. /// Passed to as a provider delegate /// so the window can be rebound to live data in GameWindow or a static /// fixture in the UI Studio. /// /// Field names and retail property ids confirmed from /// gmCharacterInfoUI::UpdatePlayerBirthAgeDeaths (0x004b8cb0), /// UpdateEnduranceInfo (0x004b8eb0), /// UpdateInnateAttributeInfo (0x004b87e0), /// UpdateFakeSkills (0x004b8930), /// UpdateAugmentations (0x004b9000), and /// UpdateLoad (0x004b8a20). /// public sealed class CharacterSheet { // ── Identity ────────────────────────────────────────────────────────────── /// Character name (first line of the report). public string Name { get; init; } = string.Empty; /// Character level. public int Level { get; init; } /// Race string, e.g. "Aluvian". Null = omit. public string? Race { get; init; } /// Heritage string, e.g. "Aluvian Heritage". Null = omit. public string? Heritage { get; init; } /// Title string, e.g. "the Adventurer". Null = omit. public string? Title { get; init; } // ── Experience / PK (gmStatManagementUI::UpdateExperience 0x004f0a70, // UpdatePKStatus 0x004f00a0) — the Attributes-tab header strip ────────── /// Total accrued experience (retail PropertyInt64 1). Header value /// element 0x10000235 (m_pTotalXPText). public long TotalXp { get; init; } /// Experience remaining to the next level. Header value element /// 0x10000238 (m_pXPToLevelText). public long XpToNextLevel { get; init; } /// XP-to-next-level meter fill, 0..1 (retail (cur−base)/(cap−base)). /// Drives the header meter 0x10000236 (m_pXPToLevelMeter). public float XpFraction { get; init; } /// PK status display string, e.g. "Non-Player Killer". Header element /// 0x10000233 (m_pPKStatusText). Null = omit. public string? PkStatus { get; init; } // ── Birth / age / deaths (UpdatePlayerBirthAgeDeaths 0x004b8cb0) ───────── /// Formatted birth date string (retail InqInt(0x62) → strftime). /// Null = omit the birth line. public string? BirthDate { get; init; } /// Formatted play-time duration (retail InqInt(0x7d) → QueryDuration). /// Null = omit the age line. public string? PlayTime { get; init; } /// Total deaths (retail InqInt(0x2b) = NumDeaths). public int Deaths { get; init; } // ── Vitals (UpdateEnduranceInfo 0x004b8eb0) ───────────────────────────── public int HealthCurrent { get; init; } public int HealthMax { get; init; } public int StaminaCurrent { get; init; } public int StaminaMax { get; init; } public int ManaCurrent { get; init; } public int ManaMax { get; init; } // ── Innate attributes (UpdateInnateAttributeInfo 0x004b87e0) ──────────── // InqAttribute order: 1,2,4,3,5,6 = Strength, Endurance, Quickness, Coordination, Focus, Self. public int Strength { get; init; } public int Endurance { get; init; } public int Quickness { get; init; } public int Coordination { get; init; } public int Focus { get; init; } public int Self { get; init; } // ── Skills (UpdateFakeSkills 0x004b8930) ──────────────────────────────── // Retail InqInt(0xb5) = SkillCredits; InqInt(0xc0) = AvailableSkillCredits. // InqInt(0x18) = available skill credits — footer 0x10000245 in the Attributes tab. public int UnspentSkillCredits { get; init; } public int SpecializedSkillCredits { get; init; } /// /// Available (unspent) skill credits shown in the Attributes tab footer State-A. /// Retail InqInt(0x18) — gmStatManagementUI::DisplayDefaultFooter (0x0049cde0). /// Element 0x10000243 (footer line-1 value in the studio's 3-line layout). /// public int SkillCredits { get; init; } /// /// Unassigned (banked) experience points. /// Retail InqInt64(2) — shown in footer line-2 in State-A display. /// Element 0x10000245 (footer line-2 value). /// public long UnassignedXp { get; init; } // ── Attribute raise costs (ExperienceToAttributeLevel, gmAttributeUI::PostInit) ── // Retail formula: ExperienceToAttributeLevel(value + 1) − ExperienceToAttributeLevel(value). // We store pre-computed per-attribute costs for the fixture; cost == 0 → attribute is // at max or not trainable (raise button ghosted/hidden). Ordered to match AttrRows // (Strength, Endurance, Coordination, Quickness, Focus, Self, Health, Stamina, Mana). // Source: gmAttributeUI::AttributeInfoRegion::Update (0x004f1910) + CM_Train::Event_TrainAttribute. /// /// XP cost to raise each of the 9 attributes/vitals by 1, in retail display order: /// [0]=Strength, [1]=Endurance, [2]=Coordination, [3]=Quickness, [4]=Focus, [5]=Self, /// [6]=Health, [7]=Stamina, [8]=Mana. /// Cost 0 means the attribute is at max (raise button ghosted). /// public long[] AttributeRaiseCosts { get; init; } = Array.Empty(); // ── Augmentations (UpdateAugmentations 0x004b9000) ───────────────────── // Retail InqInt(0x162) = AugmentationStat; string-switch 1..0xb. /// Augmentation name from the switch in UpdateAugmentations (0x004b9000), /// e.g. "Swords", "Two Handed Weapons". Null = "None". public string? AugmentationName { get; init; } // ── Burden / load (UpdateLoad 0x004b8a20) ─────────────────────────────── // Retail InqLoad + EncumbranceCapacity(Strength, AugEncumbrance). public int BurdenCurrent { get; init; } public int BurdenMax { get; init; } }