- PortalTestDestination practice range: Valorant-style hitscan rifle (recoil, spray/bloom, reload, controller support), debug HUD, practice-dummy targets with head/body hitboxes, hit/death/respawn animations, procedural SFX/FX - AK47 model (Quaternius, CC0) held via Animator IK, with fire/reload/equip anims - Intro cinematic (camera shots, title card, target beam-in, GO hand-off) - Scene dressing editor pass: CC0 textures, props, warehouse roof, lighting and post-processing; custom stylized skybox shader - CharacterMovement: camera recoil, shoulder offset, cinematic control lock - Unity CLI pipeline package; plus in-progress chat/Rive/player changes Asset credits: Assets/Models/Props/PROPS_CREDITS.txt (3 CC-BY 3.0 props), Assets/Textures/Range/TEXTURES_LICENSE.txt, Assets/Resources/*/..._LICENSE.txt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
134 lines
3.6 KiB
C#
134 lines
3.6 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
|
|
// Client-only UI. DO NOT add a NetworkIdentity to this prefab.
|
|
public class ChatUI : MonoBehaviour
|
|
{
|
|
[Header("UI Elements")]
|
|
[SerializeField] private TextMeshProUGUI chatHistory;
|
|
[SerializeField] private Scrollbar scrollbar;
|
|
[SerializeField] private TMP_InputField chatMessage;
|
|
[SerializeField] private Button sendButton;
|
|
|
|
internal static string localPlayerName;
|
|
|
|
// Instance reference so static callers can check focus state safely
|
|
internal static ChatUI Instance;
|
|
|
|
private readonly List<string> lines = new();
|
|
private Player localPlayer;
|
|
|
|
void Awake()
|
|
{
|
|
Instance = this;
|
|
|
|
if (chatMessage != null)
|
|
{
|
|
chatMessage.onValueChanged.AddListener(ToggleButton);
|
|
chatMessage.onSubmit.AddListener(OnInputSubmit);
|
|
}
|
|
}
|
|
|
|
// Expose whether the chat input is focused so other systems can disable input/movement
|
|
public static bool IsInputFieldFocused => Instance != null && Instance.chatMessage != null && Instance.chatMessage.isFocused;
|
|
|
|
// Called by PlayerScript when Escape is pressed while chat is focused, so mouse-look/
|
|
// cursor lock can resume immediately instead of waiting for a manual click-away.
|
|
public void ExitChatFocus()
|
|
{
|
|
if (chatMessage != null)
|
|
chatMessage.DeactivateInputField();
|
|
|
|
if (EventSystem.current != null)
|
|
EventSystem.current.SetSelectedGameObject(null);
|
|
}
|
|
|
|
// Called by Player.OnStartLocalPlayer to connect the UI to the player's network methods.
|
|
public void Initialize(Player player)
|
|
{
|
|
localPlayer = player;
|
|
}
|
|
|
|
public void SetHistory(string[] history)
|
|
{
|
|
lines.Clear();
|
|
if (history != null && history.Length > 0)
|
|
lines.AddRange(history);
|
|
|
|
RefreshVisual();
|
|
}
|
|
|
|
// Called by Player.RpcReceiveChat / Player.TargetReceiveHistory
|
|
public void AddMessage(string message)
|
|
{
|
|
lines.Add(message);
|
|
if (chatHistory != null)
|
|
chatHistory.text += message + "\n";
|
|
|
|
StartCoroutine(ScrollNextFrame());
|
|
}
|
|
|
|
IEnumerator ScrollNextFrame()
|
|
{
|
|
yield return null;
|
|
yield return null;
|
|
if (scrollbar != null)
|
|
scrollbar.value = 0;
|
|
}
|
|
|
|
void RefreshVisual()
|
|
{
|
|
if (chatHistory == null) return;
|
|
|
|
chatHistory.text = string.Empty;
|
|
foreach (var l in lines)
|
|
chatHistory.text += l + "\n";
|
|
|
|
StartCoroutine(ScrollNextFrame());
|
|
}
|
|
|
|
public void ToggleButton(string input)
|
|
{
|
|
if (sendButton != null)
|
|
sendButton.interactable = !string.IsNullOrWhiteSpace(input);
|
|
}
|
|
|
|
private void OnInputSubmit(string input)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(input))
|
|
SendMessage();
|
|
}
|
|
|
|
public void SendMessage()
|
|
{
|
|
if (localPlayer == null)
|
|
{
|
|
Debug.LogWarning("ChatUI: local player not set; cannot send chat.");
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(chatMessage?.text)) return;
|
|
|
|
localPlayer.SendChatToServer(chatMessage.text.Trim());
|
|
|
|
chatMessage.text = string.Empty;
|
|
chatMessage.ActivateInputField();
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
if (chatMessage != null)
|
|
{
|
|
chatMessage.onValueChanged.RemoveListener(ToggleButton);
|
|
chatMessage.onSubmit.RemoveListener(OnInputSubmit);
|
|
}
|
|
|
|
// Clear static instance reference
|
|
if (Instance == this) Instance = null;
|
|
}
|
|
}
|