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,
}