acdream/src/AcDream.App/Studio/SampleData.cs

248 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AcDream.App.UI.Layout;
using AcDream.Core.Items;
namespace AcDream.App.Studio;
/// <summary>
/// Static sample data for the UI Studio fixture provider.
/// Provides a pre-built <see cref="ClientObjectTable"/> populated with a
/// representative player, their main-pack items, side bags, and equipped gear,
/// so the 2-D inventory / paperdoll panels render populated when previewed in
/// the studio without a live game session.
///
/// Icon ids used (all real 0x06xxxxxx RenderSurface ids confirmed in the dats):
/// Inventory empty-slot sprite : 0x06004D20
/// Generic "misc" item : 0x060011D4 (icon_misc_underlay / fallback)
/// Iron Sword (melee weapon) : 0x060011CBu (acclient default weapon underlay)
/// Leather Breastplate (armor) : 0x060011CFu (acclient default armor underlay)
/// Leather Gloves : 0x060011F3u (acclient default clothing underlay)
/// Steel Ring : 0x060011D5u (acclient default jewelry underlay)
/// Healing Kit : 0x060011D4u (generic misc fallback)
/// Spell components : 0x060011D4u (generic misc fallback)
/// Side-bag 1 (Container) : 0x06004D20u
/// Side-bag 2 (Container) : 0x06004D20u
/// Equipped helm (HeadWear) : 0x060011F3u
/// Equipped chest armor : 0x060011CFu
/// Equipped melee weapon : 0x060011CBu
///
/// These are the icon *base* RenderSurface ids — the same ids GameWindow passes
/// as `iconId` into the iconIds lambda. FixtureProvider resolves them via
/// <see cref="Rendering.RenderStack.ResolveChrome"/> and returns the raw GL handle.
/// </summary>
public static class SampleData
{
// ── Guids ────────────────────────────────────────────────────────────────
/// <summary>Fake server guid for the studio's synthetic player.</summary>
public const uint PlayerGuid = 0x50000001u;
// Items in main pack (slots 05).
private const uint SwordGuid = 0x50000010u;
private const uint ChestGuid = 0x50000011u;
private const uint GlovesGuid = 0x50000012u;
private const uint RingGuid = 0x50000013u;
private const uint HealKitGuid = 0x50000014u;
private const uint CompGuid = 0x50000015u;
// Side bags (also in main pack, ContainerId = PlayerGuid; slots 6 & 7).
private const uint Bag1Guid = 0x50000020u;
private const uint Bag2Guid = 0x50000021u;
// Equipped items (ContainerId = PlayerGuid, CurrentlyEquippedLocation set).
private const uint HelmGuid = 0x50000030u;
private const uint ChestEqGuid = 0x50000031u;
private const uint WeaponEqGuid = 0x50000032u;
// ── Icon ids (0x06xxxxxx RenderSurface dat ids) ───────────────────────────
// These are the same underlay/fallback icon ids the IconComposer tests pin.
private const uint IconWeapon = 0x060011CBu; // weapon underlay
private const uint IconArmor = 0x060011CFu; // armor underlay
private const uint IconClothing = 0x060011F3u; // clothing underlay
private const uint IconJewelry = 0x060011D5u; // jewelry underlay
private const uint IconMisc = 0x060011D4u; // misc / fallback underlay
// ── Public API ──────────────────────────────────────────────────────────
/// <summary>
/// Build a fresh <see cref="ClientObjectTable"/> populated with the
/// studio's sample player + a realistic inventory snapshot.
/// The table is owned by the caller and should be kept alive for the
/// window's lifetime.
/// </summary>
public static ClientObjectTable BuildObjectTable()
{
var t = new ClientObjectTable();
// ── Player object ─────────────────────────────────────────────────
t.AddOrUpdate(new ClientObject
{
ObjectId = PlayerGuid,
Name = "Studio Player",
Type = ItemType.Creature,
ItemsCapacity = 102,
ContainersCapacity = 7,
});
// ── Loose items in main pack (slots 05) ──────────────────────────
AddItem(t, SwordGuid, ItemType.MeleeWeapon, IconWeapon, "Iron Sword", PlayerGuid, 0, burden: 60);
AddItem(t, ChestGuid, ItemType.Armor, IconArmor, "Leather Breastplate", PlayerGuid, 1, burden: 200);
AddItem(t, GlovesGuid, ItemType.Clothing, IconClothing, "Leather Gloves", PlayerGuid, 2, burden: 50);
AddItem(t, RingGuid, ItemType.Jewelry, IconJewelry, "Steel Ring", PlayerGuid, 3, burden: 10);
AddItem(t, HealKitGuid, ItemType.Misc, IconMisc, "Healing Kit", PlayerGuid, 4, burden: 30);
AddItem(t, CompGuid, ItemType.SpellComponents, IconMisc, "Spell Comps", PlayerGuid, 5, burden: 25, stackSize: 50, stackMax: 100);
// ── Side bags (Container items in main pack, slots 6 & 7) ─────────
AddItem(t, Bag1Guid, ItemType.Container, IconMisc, "Small Pack 1",
containerId: PlayerGuid, slot: 6, burden: 20, itemsCapacity: 24);
AddItem(t, Bag2Guid, ItemType.Container, IconMisc, "Small Pack 2",
containerId: PlayerGuid, slot: 7, burden: 20, itemsCapacity: 24);
// ── Equipped items (ContainerId = PlayerGuid, CurrentlyEquippedLocation set) ──
AddEquipped(t, HelmGuid, ItemType.Armor, IconClothing, "Tin Helm", EquipMask.HeadWear);
AddEquipped(t, ChestEqGuid, ItemType.Armor, IconArmor, "Chain Coat", EquipMask.ChestArmor);
AddEquipped(t, WeaponEqGuid, ItemType.MeleeWeapon, IconWeapon, "Wooden Sword", EquipMask.MeleeWeapon);
return t;
}
// ── Sample vital constants (used by FixtureProvider) ────────────────────
public const float HealthPct = 0.8f;
public const float StaminaPct = 0.6f;
public const float ManaPct = 0.9f;
// ── Sample character sheet (used by CharacterController in the Studio) ───
/// <summary>
/// Returns a representative <see cref="CharacterSheet"/> for the studio's
/// synthetic character. Values are plausible retail-scale numbers so the
/// report renders with well-proportioned text in all sections.
/// </summary>
public static CharacterSheet SampleCharacter() => new()
{
Name = "Studio Player",
Level = 126,
Race = "Aluvian",
Heritage = "Aluvian Heritage",
Title = "the Adventurer",
BirthDate = "January 5, 2001",
PlayTime = "2 years, 114 days, 4 hours",
Deaths = 42,
PkStatus = "Non-Player Killer",
TotalXp = 1_250_000_000,
XpToNextLevel = 42_000_000,
XpFraction = 0.63f,
// Vitals: retail screenshot spec (Pass 1 acceptance criteria §Goal).
HealthCurrent = 5, HealthMax = 5,
StaminaCurrent = 10, StaminaMax = 10,
ManaCurrent = 10, ManaMax = 10,
// Attributes: Strength + Quickness = 200; all others = 10 (retail screenshot spec §Goal).
Strength = 200,
Endurance = 10,
Quickness = 200,
Coordination = 10,
Focus = 10,
Self = 10,
UnspentSkillCredits = 12,
SpecializedSkillCredits = 4,
// Available skill credits (retail InqInt(0x18)); shown in Attributes tab footer State-A.
SkillCredits = 96,
// Unassigned (banked) XP (retail InqInt64(2)); footer State-A line-2 value.
UnassignedXp = 87_757_321_741L,
// Raise costs in retail display order (Strength, Endurance, Coordination, Quickness,
// Focus, Self, Health, Stamina, Mana).
// Str@200 = maxed → 0 (disabled). Quickness@200 = maxed → 0. Others @10 → affordable.
// Focus@10 → 110 matches the authoritative retail screenshot (spec §4).
// Formula bracket at value=10: ExperienceToAttributeLevel(11) ExperienceToAttributeLevel(10).
AttributeRaiseCosts = new long[] { 0L, 95L, 100L, 0L, 110L, 105L, 90L, 88L, 112L },
// Real SkillTable icon IDs and train/specialize costs from client_portal.dat
// SkillTable 0x0E000004. gmSkillUI groups/sorts these at bind time.
Skills = new CharacterSkill[]
{
new( 6, "Melee Defense", 0x06000165u, CharacterSkillAdvancementClass.Specialized, 350, 354, false, 10, 20, 18_250_000L, 182_500_000L),
new(34, "War Magic", 0x06001365u, CharacterSkillAdvancementClass.Specialized, 280, 285, false, 16, 28, 11_100_000L, 111_000_000L),
new(14, "Arcane Lore", 0x0600016Eu, CharacterSkillAdvancementClass.Trained, 260, 269, false, 4, 6, 7_500_000L, 75_000_000L),
new(33, "Life Magic", 0x06001364u, CharacterSkillAdvancementClass.Trained, 250, 252, false, 12, 20, 6_800_000L, 68_000_000L),
new(47, "Missile Weapons", 0x0600015Fu, CharacterSkillAdvancementClass.Trained, 220, 221, false, 6, 12, 5_250_000L, 52_500_000L),
new(21, "Healing", 0x06000133u, CharacterSkillAdvancementClass.Untrained, 10, 10, true, 6, 10, 0L),
new(22, "Jump", 0x0600016Bu, CharacterSkillAdvancementClass.Untrained, 210, 210, true, 0, 4, 0L),
new(36, "Loyalty", 0x06001367u, CharacterSkillAdvancementClass.Untrained, 10, 10, true, 0, 2, 0L),
new(24, "Run", 0x06000173u, CharacterSkillAdvancementClass.Untrained, 390, 390, true, 0, 4, 0L),
new(38, "Alchemy", 0x060019E4u, CharacterSkillAdvancementClass.Untrained, 10, 10, false, 6, 12, 0L),
new(39, "Cooking", 0x06001A54u, CharacterSkillAdvancementClass.Untrained, 10, 10, false, 4, 8, 0L),
new(37, "Fletching", 0x06001A55u, CharacterSkillAdvancementClass.Untrained, 10, 10, false, 4, 8, 0L),
},
AugmentationName = "Swords",
BurdenCurrent = 1200,
BurdenMax = 4500,
};
// ── Helpers ─────────────────────────────────────────────────────────────
private static void AddItem(
ClientObjectTable t,
uint guid,
ItemType type,
uint iconId,
string name,
uint containerId,
int slot,
int burden = 0,
int stackSize = 1,
int stackMax = 1,
int itemsCapacity = 0)
{
t.AddOrUpdate(new ClientObject
{
ObjectId = guid,
Name = name,
Type = type,
IconId = iconId,
Burden = burden,
StackSize = stackSize,
StackSizeMax = stackMax,
ItemsCapacity = itemsCapacity,
});
t.MoveItem(guid, containerId, slot);
}
private static void AddEquipped(
ClientObjectTable t,
uint guid,
ItemType type,
uint iconId,
string name,
EquipMask equipMask)
{
t.AddOrUpdate(new ClientObject
{
ObjectId = guid,
Name = name,
Type = type,
IconId = iconId,
ValidLocations = equipMask,
CurrentlyEquippedLocation = equipMask,
ContainerId = PlayerGuid,
});
// Update the equip location on the object via MoveItem (sets ContainerId +
// CurrentlyEquippedLocation via the equip overload).
t.MoveItem(guid, PlayerGuid, newSlot: -1, newEquipLocation: equipMask);
}
}