IRuntimeFellowshipView.GetMembers() gives the fellowship panel a way to enumerate the whole roster (TryGetMember alone needs the guid first, which a UI roster build doesn't have yet). Implemented on RuntimeFellowshipState.FellowshipView as a materialized snapshot under the same lock every other read there uses. SelectionChangeSource.Social covers a fellowship-roster row click (gmFellowshipUI's list-selection arm calls the same ACCWeenieObject::SetSelectedObject primitive every other selection origin uses -- lane B docs/research/2026-08-11-fa-panel-structure.md §6.2/§2.8). Runtime tests: +4 (RuntimeFellowshipStateTests.GetMembers_*). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
225 lines
6.5 KiB
C#
225 lines
6.5 KiB
C#
using AcDream.Runtime.Gameplay;
|
|
|
|
namespace AcDream.Runtime;
|
|
|
|
public readonly record struct RuntimePendingInventoryRequestSnapshot(
|
|
ulong Token,
|
|
int Kind,
|
|
uint ItemId,
|
|
bool Dispatched);
|
|
|
|
public readonly record struct RuntimeInventoryStateSnapshot(
|
|
uint RequestedExternalContainerId,
|
|
uint CurrentExternalContainerId,
|
|
int BusyCount,
|
|
bool CanBeginRequest,
|
|
RuntimePendingInventoryRequestSnapshot? PendingRequest,
|
|
int ShortcutCount,
|
|
long ShortcutRevision,
|
|
int ItemManaCount,
|
|
long ItemManaRevision);
|
|
|
|
public readonly record struct RuntimeShortcutSnapshot(
|
|
int Index,
|
|
uint ObjectId,
|
|
uint SpellId);
|
|
|
|
public interface IRuntimeInventoryStateView
|
|
{
|
|
RuntimeInventoryStateSnapshot Snapshot { get; }
|
|
|
|
bool TryGetShortcut(int index, out RuntimeShortcutSnapshot shortcut);
|
|
|
|
bool TryGetItemMana(uint objectId, out float fraction);
|
|
}
|
|
|
|
public readonly record struct RuntimeCharacterSnapshot(
|
|
long CharacterRevision,
|
|
long SpellbookRevision,
|
|
RuntimeCharacterOptionsSnapshot Options,
|
|
RuntimeMovementSkillSnapshot MovementSkills,
|
|
int LearnedSpellCount,
|
|
int ActiveEnchantmentCount,
|
|
int DesiredComponentCount,
|
|
int SkillCount,
|
|
uint SpellbookFilters);
|
|
|
|
public readonly record struct RuntimeVitalSnapshot(
|
|
int Kind,
|
|
uint Ranks,
|
|
uint Start,
|
|
uint Experience,
|
|
uint Current,
|
|
uint Maximum);
|
|
|
|
public readonly record struct RuntimeAttributeSnapshot(
|
|
int Kind,
|
|
uint Ranks,
|
|
uint Start,
|
|
uint Experience,
|
|
uint Current);
|
|
|
|
public readonly record struct RuntimeSkillSnapshot(
|
|
uint SkillId,
|
|
uint Ranks,
|
|
uint Status,
|
|
uint Experience,
|
|
uint Initial,
|
|
uint Resistance,
|
|
double LastUsed,
|
|
uint FormulaBonus,
|
|
uint CurrentLevel);
|
|
|
|
public interface IRuntimeCharacterView
|
|
{
|
|
RuntimeCharacterSnapshot Snapshot { get; }
|
|
|
|
bool TryGetVital(int kind, out RuntimeVitalSnapshot vital);
|
|
|
|
bool TryGetAttribute(int kind, out RuntimeAttributeSnapshot attribute);
|
|
|
|
bool TryGetSkill(uint skillId, out RuntimeSkillSnapshot skill);
|
|
|
|
bool KnowsSpell(uint spellId);
|
|
|
|
bool TryGetFavorite(int tabIndex, int position, out uint spellId);
|
|
|
|
bool TryGetDesiredComponent(uint componentId, out uint amount);
|
|
}
|
|
|
|
public readonly record struct RuntimeSocialSnapshot(
|
|
long FriendsRevision,
|
|
int FriendCount,
|
|
long SquelchRevision,
|
|
int SquelchedAccountCount,
|
|
int SquelchedCharacterCount,
|
|
int GlobalSquelchTypeCount,
|
|
int NegotiatedChatRoomCount);
|
|
|
|
public readonly record struct RuntimeFriendSnapshot(
|
|
uint Id,
|
|
string Name,
|
|
bool Online,
|
|
bool AppearOffline);
|
|
|
|
public interface IRuntimeSocialView
|
|
{
|
|
RuntimeSocialSnapshot Snapshot { get; }
|
|
|
|
bool TryGetFriend(uint characterId, out RuntimeFriendSnapshot friend);
|
|
}
|
|
|
|
// ── Fellowship / Allegiance (Campaign FA slice FA2, 2026-08-12) ────────────
|
|
// D2: two sibling J-owners, session-scoped Fellowship vs reconnect-surviving
|
|
// Allegiance (docs/research/2026-08-11-fa-acdream-seams.md §1.3). Consumers
|
|
// poll Snapshot.Revision — no IRuntimeEventObserver member is added (would
|
|
// break all 5 bot policies + the trace recorder, per the same doc §1.3).
|
|
|
|
public readonly record struct RuntimeFellowMemberSnapshot(
|
|
uint Guid,
|
|
string Name,
|
|
uint Level,
|
|
uint MaxHealth,
|
|
uint MaxStamina,
|
|
uint MaxMana,
|
|
uint CurrentHealth,
|
|
uint CurrentStamina,
|
|
uint CurrentMana,
|
|
bool ShareLoot);
|
|
|
|
public readonly record struct RuntimeFellowshipSnapshot(
|
|
long Revision,
|
|
bool IsInFellowship,
|
|
string Name,
|
|
uint LeaderGuid,
|
|
bool ShareXp,
|
|
bool EvenXpSplit,
|
|
bool IsOpen,
|
|
bool Locked,
|
|
int MemberCount)
|
|
{
|
|
// FA2 fix-round SHOULD-FIX 6 (blast review): an explicit parameterless
|
|
// constructor gives `new()` (used as RuntimeStateCheckpoint's default,
|
|
// GameRuntimeViews.cs) a non-null Name instead of `default`'s bitwise
|
|
// zero-init (structs never run field initializers or this constructor
|
|
// for `default(T)` — only `new S()` invokes it).
|
|
public RuntimeFellowshipSnapshot()
|
|
: this(0, false, string.Empty, 0u, false, false, false, false, 0)
|
|
{
|
|
}
|
|
}
|
|
|
|
public interface IRuntimeFellowshipView
|
|
{
|
|
RuntimeFellowshipSnapshot Snapshot { get; }
|
|
|
|
bool TryGetMember(uint guid, out RuntimeFellowMemberSnapshot member);
|
|
|
|
/// <summary>
|
|
/// Campaign FA slice FA4: the complete member roster, for the
|
|
/// fellowship page's row-per-fellow list. <see cref="TryGetMember"/>
|
|
/// alone cannot build a roster — it needs the guid first. Order is
|
|
/// insertion order (the last <c>0x02BE FellowshipFullUpdate</c>'s
|
|
/// wire order, then whatever order incremental <c>0x02C0</c> upserts
|
|
/// added new members) — retail's own hash-table iteration order is not
|
|
/// a stable contract either (lane B §3.9), so no ordering guarantee
|
|
/// beyond "stable across repeated calls between mutations" is made.
|
|
/// </summary>
|
|
IEnumerable<RuntimeFellowMemberSnapshot> GetMembers();
|
|
}
|
|
|
|
public readonly record struct RuntimeAllegianceMemberSnapshot(
|
|
uint CharacterId,
|
|
uint ParentGuid,
|
|
bool IsLoggedIn,
|
|
string Name,
|
|
ushort Rank,
|
|
uint Level,
|
|
ushort Loyalty,
|
|
ushort Leadership,
|
|
uint CpCached,
|
|
uint CpTithed,
|
|
byte Gender,
|
|
byte HeritageGroup,
|
|
bool MayPassupExperience);
|
|
|
|
public readonly record struct RuntimeAllegianceSnapshot(
|
|
long Revision,
|
|
bool HasServerSeed,
|
|
bool HasProfile,
|
|
uint Rank,
|
|
uint TotalMembers,
|
|
uint TotalVassals,
|
|
string AllegianceName,
|
|
uint MonarchGuid,
|
|
int RecordCount)
|
|
{
|
|
public bool HasMonarch => MonarchGuid != 0u;
|
|
|
|
// FA2 fix-round SHOULD-FIX 6 (blast review): see
|
|
// RuntimeFellowshipSnapshot's parameterless constructor — same
|
|
// non-null-default reasoning, for AllegianceName.
|
|
public RuntimeAllegianceSnapshot()
|
|
: this(0, false, false, 0u, 0u, 0u, string.Empty, 0u, 0)
|
|
{
|
|
}
|
|
}
|
|
|
|
public interface IRuntimeAllegianceView
|
|
{
|
|
RuntimeAllegianceSnapshot Snapshot { get; }
|
|
|
|
bool TryGetMonarch(out RuntimeAllegianceMemberSnapshot monarch);
|
|
|
|
bool TryGetMember(uint guid, out RuntimeAllegianceMemberSnapshot member);
|
|
|
|
bool TryGetPatron(uint guid, out RuntimeAllegianceMemberSnapshot patron);
|
|
|
|
/// <summary>
|
|
/// Port of retail's <c>GetFirstVassal</c>/<c>GetNextVassal</c> walk —
|
|
/// reverse wire order, exactly like <c>AllegianceProfileLookups.
|
|
/// FindVassals</c> (lane C §4.4 point 3;
|
|
/// <c>ClientCommandResponses.cs:280-294</c>).
|
|
/// </summary>
|
|
IEnumerable<RuntimeAllegianceMemberSnapshot> GetVassals(uint guid);
|
|
}
|