@using Sandbox @using System @using Sandbox.UI @namespace Sandbox @inherits PanelComponent @implements Component.INetworkListener
@foreach ( var entry in Entries ) { }
@code { public static Chat Instance; public Chat() => Instance = this; public static bool IsActive = false; public enum MessageType { Player, System, Admin, Notification } public record Entry { public Entry( Chat.MessageType type, ulong steamID, string author, string message, RealTimeSince timeSinceAdded, bool isTemporary ) { Type = type; SteamID = steamID; Author = author; Message = message; TimeSinceAdded = timeSinceAdded; IsTemporary = isTemporary; } public Chat.MessageType Type { get; internal set; } public ulong SteamID { get; internal set; } public string Author { get; internal set; } public string Message { get; internal set; } public RealTimeSince TimeSinceAdded { get; internal set; } public bool IsTemporary { get; internal set; } } List Entries = new(); public TextEntry InputBox; public event Action OnChat; protected override void OnUpdate() { if ( InputBox is null ) return; Panel.AcceptsFocus = false; if ( Input.Pressed( "chat" ) ) Open(); if ( InputBox.HasFocus && Input.EscapePressed ) { Input.EscapePressed = false; ChatClosed(); } SetClass( "open", InputBox.HasFocus ); } public static void Open() { IsActive = true; Instance.InputBox?.Focus(); foreach ( var entry in Instance.Entries ) entry.IsTemporary = false; Instance.StateHasChanged(); } public static void AddMessage( MessageType type, string message, ulong steamId = 0, string author = "" ) => Instance?.AddMessageInternal( type, message, steamId, author ); [ConCmd( "say" )] public static void Say( string message ) { Instance?.AddTextInternal( message ); } [Rpc.Broadcast] public void AddTextInternal( string message ) { if ( string.IsNullOrWhiteSpace( message ) ) return; AddMessageInternal( MessageType.Player, message.Truncate( 300 ), Rpc.Caller.SteamId, Rpc.Caller.DisplayName ); } void AddMessageInternal( MessageType type, string message, ulong steamId = 0, string author = "" ) { Entries.Add( new Entry( type, steamId, author, message, 0, !IsActive ) ); StateHasChanged(); Log.Info( $"[{type}] {author}: {message}" ); } void ScrollToBottom() { var panel = Panel.Children.First(); panel.ScrollVelocity = 0; panel.ScrollOffset = 0; } void ChatFinished() { IsActive = false; var text = InputBox.Text; InputBox.Text = ""; if ( !string.IsNullOrWhiteSpace( text ) ) { OnChat?.Invoke( text ); AddTextInternal( text ); } ScrollToBottom(); OnChat = null; } void ChatClosed() { IsActive = false; InputBox.Text = ""; ScrollToBottom(); OnChat = null; } void Component.INetworkListener.OnConnected( Connection channel ) { if ( IsProxy ) return; AddMessageInternal( MessageType.System, $"{channel.DisplayName} has joined the game" ); } void Component.INetworkListener.OnDisconnected( Connection channel ) { if ( IsProxy ) return; AddMessageInternal( MessageType.System, $"{channel.DisplayName} has left the game" ); } }