namespace AcDream.Core.Chat; /// /// Runtime state for retail's TurbineChat (0xF7DE) global chat rooms — /// the General / Trade / LFG / Roleplay / Society / Olthoi pipeline. /// /// /// Lifecycle: /// /// Pre-login: false, all room ids 0, context counter 1. /// Server fires SetTurbineChatChannels (0x0295) shortly after EnterWorld /// → populates the room ids and flips /// on. /// Outbound chat: caller asks for a fresh context id via /// , looks up the room id by /// , and feeds the pair to /// . /// /// /// /// /// Mirrors holtburger's TurbineChatState /// (references/holtburger/crates/holtburger-core/src/client/types.rs /// lines 657-672). Cookie counter starts at 1 (not 0) per holtburger: /// next_context_id.wrapping_add(1).max(1) keeps 0 reserved for /// "no context" / response cookies. /// /// public sealed class TurbineChatState { /// True after the first SetTurbineChatChannels arrives. public bool Enabled { get; private set; } /// Allegiance Turbine room (0 if player has no allegiance). public uint AllegianceRoom { get; private set; } public uint GeneralRoom { get; private set; } public uint TradeRoom { get; private set; } public uint LfgRoom { get; private set; } public uint RoleplayRoom { get; private set; } public uint OlthoiRoom { get; private set; } /// Top-level Society room (0 if player has no society). public uint SocietyRoom { get; private set; } public uint SocietyCelestialHandRoom { get; private set; } public uint SocietyEldrytchWebRoom { get; private set; } public uint SocietyRadiantBloodRoom { get; private set; } private uint _nextContextId = 1; /// /// Get-and-increment the per-session context cookie. Wraps to 1 (not /// 0) so 0 stays reserved for "no context" / Response cookies. /// Mirrors holtburger's wrapping_add(1).max(1). /// public uint NextContextId() { uint cookie = _nextContextId; unchecked { _nextContextId += 1; if (_nextContextId == 0) _nextContextId = 1; } return cookie; } /// /// Absorb a parsed SetTurbineChatChannels payload — flips /// on and fills in the per-channel room ids. /// Takes raw u32s (rather than the parser's struct directly) so /// AcDream.Core stays free of an AcDream.Core.Net dependency. /// public void OnChannelsReceived( uint allegianceRoom, uint generalRoom, uint tradeRoom, uint lfgRoom, uint roleplayRoom, uint olthoiRoom, uint societyRoom, uint societyCelestialHandRoom, uint societyEldrytchWebRoom, uint societyRadiantBloodRoom) { Enabled = true; AllegianceRoom = allegianceRoom; GeneralRoom = generalRoom; TradeRoom = tradeRoom; LfgRoom = lfgRoom; RoleplayRoom = roleplayRoom; OlthoiRoom = olthoiRoom; SocietyRoom = societyRoom; SocietyCelestialHandRoom = societyCelestialHandRoom; SocietyEldrytchWebRoom = societyEldrytchWebRoom; SocietyRadiantBloodRoom = societyRadiantBloodRoom; } /// /// Look up the runtime room id for a Turbine /// . Returns 0 if the channel is not /// available (server hasn't populated it / player not in society / etc). /// public uint RoomFor(ChatChannelKindLite kind) => kind switch { ChatChannelKindLite.Allegiance => AllegianceRoom, ChatChannelKindLite.General => GeneralRoom, ChatChannelKindLite.Trade => TradeRoom, ChatChannelKindLite.Lfg => LfgRoom, ChatChannelKindLite.Roleplay => RoleplayRoom, ChatChannelKindLite.Olthoi => OlthoiRoom, ChatChannelKindLite.Society => SocietyRoom, ChatChannelKindLite.SocietyCelestialHand => SocietyCelestialHandRoom, ChatChannelKindLite.SocietyEldrytchWeb => SocietyEldrytchWebRoom, ChatChannelKindLite.SocietyRadiantBlood => SocietyRadiantBloodRoom, _ => 0u, }; } /// /// Coarse Turbine channel selector usable from /// without dragging in the /// AcDream.UI.Abstractions ChatChannelKind (which lives in a /// different layer). Maps 1:1 onto the chat-type-id values from /// holtburger turbine.rs (TurbineChatType, lines 31-45). /// public enum ChatChannelKindLite { Allegiance, General, Trade, Lfg, Roleplay, Society, SocietyCelestialHand, SocietyEldrytchWeb, SocietyRadiantBlood, Olthoi, }