352 lines
15 KiB
C#
352 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace MSCO
|
|
{
|
|
/// <summary>Spieler im Admin-Menü.</summary>
|
|
public class AdminPlayer
|
|
{
|
|
public int Id;
|
|
public string Name;
|
|
public string SteamId;
|
|
public bool IsAdmin;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fenster über dem Spiel: F8-Konsole (alle MSCO-Befehle), Chat (T) und Admin-Menü (/admin).
|
|
/// Solange eins davon offen ist, sind Spielerbewegung und Kamera gesperrt (InputLock).
|
|
/// </summary>
|
|
public class Overlays
|
|
{
|
|
const int MaxConsoleLines = 300;
|
|
const int MaxChatLines = 60;
|
|
const float ChatFadeSeconds = 15f;
|
|
const string ConsoleControl = "msco_console";
|
|
const string ChatControl = "msco_chat";
|
|
|
|
class ChatLine
|
|
{
|
|
public string Name, Text;
|
|
public bool System;
|
|
public float Time;
|
|
}
|
|
|
|
// Konsole
|
|
public bool ConsoleOpen { get; private set; }
|
|
readonly List<string> consoleLines = new List<string>();
|
|
readonly List<string> history = new List<string>();
|
|
int historyIndex = -1;
|
|
string consoleInput = "";
|
|
Vector2 consoleScroll;
|
|
int consoleOpenedFrame = -1;
|
|
bool consoleScrollToEnd;
|
|
|
|
// Chat
|
|
public bool ChatOpen { get; private set; }
|
|
readonly List<ChatLine> chat = new List<ChatLine>();
|
|
string chatInput = "";
|
|
int chatOpenedFrame = -1;
|
|
|
|
// Admin
|
|
public bool AdminOpen { get; private set; }
|
|
List<AdminPlayer> adminPlayers;
|
|
string adminReason = "";
|
|
int confirmBanId = -1;
|
|
Vector2 adminScroll;
|
|
|
|
public bool AnyOpen { get { return ConsoleOpen || ChatOpen || AdminOpen; } }
|
|
|
|
// Stile
|
|
bool stylesReady;
|
|
GUIStyle panel, rowStyle, field, logText, caption, title, primary, secondary, danger, chatName, chatText, chatSystem, small;
|
|
|
|
// ---------------------------------------------------------------- Öffnen/Schließen
|
|
|
|
public void ToggleConsole()
|
|
{
|
|
ConsoleOpen = !ConsoleOpen;
|
|
if (ConsoleOpen)
|
|
{
|
|
ChatOpen = false;
|
|
consoleOpenedFrame = Time.frameCount;
|
|
consoleScrollToEnd = true;
|
|
}
|
|
}
|
|
|
|
public void OpenChat()
|
|
{
|
|
if (AnyOpen) return;
|
|
ChatOpen = true;
|
|
chatOpenedFrame = Time.frameCount;
|
|
chatInput = "";
|
|
}
|
|
|
|
public void OpenAdmin(List<AdminPlayer> players)
|
|
{
|
|
AdminOpen = true;
|
|
ConsoleOpen = false;
|
|
ChatOpen = false;
|
|
adminPlayers = players;
|
|
confirmBanId = -1;
|
|
}
|
|
|
|
public void SetAdminPlayers(List<AdminPlayer> players) { adminPlayers = players; }
|
|
|
|
public void CloseAll()
|
|
{
|
|
ConsoleOpen = ChatOpen = AdminOpen = false;
|
|
}
|
|
|
|
// ---------------------------------------------------------------- Inhalte
|
|
|
|
public void Log(string line)
|
|
{
|
|
foreach (string part in (line ?? "").Split('\n'))
|
|
{
|
|
consoleLines.Add(part);
|
|
if (consoleLines.Count > MaxConsoleLines) consoleLines.RemoveAt(0);
|
|
}
|
|
consoleScrollToEnd = true;
|
|
}
|
|
|
|
public void AddChat(string name, string text, bool system = false)
|
|
{
|
|
chat.Add(new ChatLine { Name = name, Text = text, System = system, Time = Time.realtimeSinceStartup });
|
|
if (chat.Count > MaxChatLines) chat.RemoveAt(0);
|
|
}
|
|
|
|
// ---------------------------------------------------------------- Zeichnen (OnGUI)
|
|
|
|
public void Draw(MultiplayerManager mp, bool inGame)
|
|
{
|
|
EnsureStyles();
|
|
if (inGame) DrawChat(mp);
|
|
if (ConsoleOpen) DrawConsole();
|
|
if (AdminOpen) DrawAdmin(mp);
|
|
}
|
|
|
|
void DrawConsole()
|
|
{
|
|
float width = Mathf.Min(Screen.width - 40, 960);
|
|
var rect = new Rect((Screen.width - width) / 2f, 20, width, Mathf.Min(340, Screen.height * 0.5f));
|
|
GUI.Box(rect, GUIContent.none, panel);
|
|
|
|
GUI.Label(new Rect(rect.x + 16, rect.y + 10, 300, 20), "MSCO CONSOLE", caption);
|
|
GUI.Label(new Rect(rect.xMax - 216, rect.y + 10, 200, 20), "type 'help' - F8 to close", small);
|
|
|
|
// Tasten: Enter ausführen, Esc schließen, Hoch/Runter Verlauf
|
|
Event e = Event.current;
|
|
if (e.type == EventType.KeyDown && GUI.GetNameOfFocusedControl() == ConsoleControl)
|
|
{
|
|
if (e.keyCode == KeyCode.Return || e.keyCode == KeyCode.KeypadEnter)
|
|
{
|
|
RunConsoleLine(consoleInput);
|
|
consoleInput = "";
|
|
e.Use();
|
|
}
|
|
else if (e.keyCode == KeyCode.Escape) { ConsoleOpen = false; e.Use(); return; }
|
|
else if (e.keyCode == KeyCode.UpArrow && history.Count > 0)
|
|
{
|
|
historyIndex = historyIndex < 0 ? history.Count - 1 : Mathf.Max(0, historyIndex - 1);
|
|
consoleInput = history[historyIndex];
|
|
e.Use();
|
|
}
|
|
else if (e.keyCode == KeyCode.DownArrow && historyIndex >= 0)
|
|
{
|
|
historyIndex++;
|
|
consoleInput = historyIndex < history.Count ? history[historyIndex] : "";
|
|
if (historyIndex >= history.Count) historyIndex = -1;
|
|
e.Use();
|
|
}
|
|
}
|
|
|
|
GUILayout.BeginArea(new Rect(rect.x + 14, rect.y + 34, rect.width - 28, rect.height - 84));
|
|
if (consoleScrollToEnd && e.type == EventType.Repaint) { consoleScroll.y = float.MaxValue; consoleScrollToEnd = false; }
|
|
consoleScroll = GUILayout.BeginScrollView(consoleScroll);
|
|
foreach (string line in consoleLines) GUILayout.Label(line, logText);
|
|
GUILayout.EndScrollView();
|
|
GUILayout.EndArea();
|
|
|
|
// Taste, mit der die Konsole geöffnet wurde, nicht als Zeichen übernehmen
|
|
if (Time.frameCount == consoleOpenedFrame && e.type == EventType.KeyDown) e.Use();
|
|
GUI.SetNextControlName(ConsoleControl);
|
|
consoleInput = GUI.TextField(new Rect(rect.x + 14, rect.yMax - 46, rect.width - 28, 34), consoleInput, 200, field);
|
|
GUI.FocusControl(ConsoleControl);
|
|
}
|
|
|
|
void RunConsoleLine(string line)
|
|
{
|
|
line = (line ?? "").Trim();
|
|
if (line.Length == 0) return;
|
|
history.Add(line);
|
|
if (history.Count > 50) history.RemoveAt(0);
|
|
historyIndex = -1;
|
|
Log("> " + line);
|
|
|
|
if (line == "clear") { consoleLines.Clear(); return; }
|
|
string[] args = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (args.Length > 0 && args[0] == "mp") // "mp connect ..." geht auch
|
|
{
|
|
var rest = new string[args.Length - 1];
|
|
Array.Copy(args, 1, rest, 0, rest.Length);
|
|
args = rest;
|
|
}
|
|
Commands.Execute(args, Log);
|
|
}
|
|
|
|
void DrawChat(MultiplayerManager mp)
|
|
{
|
|
float now = Time.realtimeSinceStartup;
|
|
float width = Mathf.Min(460, Screen.width - 40);
|
|
float lineHeight = 20;
|
|
int maxLines = ChatOpen ? 12 : 6;
|
|
|
|
// Sichtbare Zeilen: offen = die letzten, geschlossen = nur neue (blenden aus)
|
|
var visible = new List<ChatLine>();
|
|
for (int i = chat.Count - 1; i >= 0 && visible.Count < maxLines; i--)
|
|
if (ChatOpen || now - chat[i].Time < ChatFadeSeconds) visible.Insert(0, chat[i]);
|
|
|
|
float bottom = Screen.height * 0.62f;
|
|
float height = visible.Count * lineHeight + (ChatOpen ? 52 : 12);
|
|
var rect = new Rect(20, bottom - height, width, height);
|
|
if (ChatOpen || visible.Count > 0) GUI.Box(rect, GUIContent.none, ChatOpen ? panel : rowStyle);
|
|
|
|
Color old = GUI.color;
|
|
float y = rect.y + 6;
|
|
foreach (ChatLine line in visible)
|
|
{
|
|
float age = now - line.Time;
|
|
float alpha = ChatOpen ? 1f : Mathf.Clamp01((ChatFadeSeconds - age) / 3f);
|
|
GUI.color = new Color(1f, 1f, 1f, alpha);
|
|
if (line.System)
|
|
{
|
|
GUI.Label(new Rect(rect.x + 12, y, width - 24, lineHeight), line.Text, chatSystem);
|
|
}
|
|
else
|
|
{
|
|
Vector2 size = chatName.CalcSize(new GUIContent(line.Name + ":"));
|
|
GUI.Label(new Rect(rect.x + 12, y, size.x, lineHeight), line.Name + ":", chatName);
|
|
GUI.Label(new Rect(rect.x + 16 + size.x, y, width - 28 - size.x, lineHeight), line.Text, chatText);
|
|
}
|
|
y += lineHeight;
|
|
}
|
|
GUI.color = old;
|
|
|
|
if (!ChatOpen) return;
|
|
|
|
Event e = Event.current;
|
|
if (e.type == EventType.KeyDown && GUI.GetNameOfFocusedControl() == ChatControl)
|
|
{
|
|
if (e.keyCode == KeyCode.Return || e.keyCode == KeyCode.KeypadEnter)
|
|
{
|
|
string text = chatInput.Trim();
|
|
if (text.Length > 0)
|
|
{
|
|
if (text.StartsWith("/")) RunConsoleLine(text.Substring(1)); // z.B. /admin
|
|
else mp.SendChat(text);
|
|
}
|
|
chatInput = "";
|
|
ChatOpen = false;
|
|
e.Use();
|
|
return;
|
|
}
|
|
if (e.keyCode == KeyCode.Escape) { ChatOpen = false; e.Use(); return; }
|
|
}
|
|
|
|
// Taste, mit der der Chat geöffnet wurde (T), nicht als Zeichen übernehmen
|
|
if (Time.frameCount == chatOpenedFrame && e.type == EventType.KeyDown) e.Use();
|
|
GUI.SetNextControlName(ChatControl);
|
|
chatInput = GUI.TextField(new Rect(rect.x + 10, rect.yMax - 40, width - 20, 30), chatInput, 150, field);
|
|
GUI.FocusControl(ChatControl);
|
|
}
|
|
|
|
void DrawAdmin(MultiplayerManager mp)
|
|
{
|
|
float width = Mathf.Min(600, Screen.width - 40), height = Mathf.Min(460, Screen.height - 40);
|
|
var rect = new Rect((Screen.width - width) / 2f, (Screen.height - height) / 2f, width, height);
|
|
GUI.Box(rect, GUIContent.none, panel);
|
|
|
|
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
|
|
{
|
|
AdminOpen = false;
|
|
Event.current.Use();
|
|
return;
|
|
}
|
|
|
|
GUILayout.BeginArea(new Rect(rect.x + 20, rect.y + 16, rect.width - 40, rect.height - 32));
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.Label("Admin", title, GUILayout.ExpandWidth(true)); // nicht von den Buttons zusammendrücken lassen
|
|
if (GUILayout.Button("REFRESH", secondary, GUILayout.Height(30), GUILayout.Width(100))) mp.RequestAdminList();
|
|
GUILayout.Space(8);
|
|
if (GUILayout.Button("CLOSE", secondary, GUILayout.Height(30), GUILayout.Width(90))) AdminOpen = false;
|
|
GUILayout.EndHorizontal();
|
|
GUILayout.Label(mp.IsDedicated ? "Players on this server. Banned players cannot join again." : "Players in your game.", small);
|
|
GUILayout.Space(10);
|
|
|
|
GUILayout.Label("REASON (optional)", caption);
|
|
adminReason = GUILayout.TextField(adminReason, 100, field, GUILayout.Height(32));
|
|
GUILayout.Space(12);
|
|
|
|
adminScroll = GUILayout.BeginScrollView(adminScroll);
|
|
if (adminPlayers == null) GUILayout.Label("Loading players...", small);
|
|
else if (adminPlayers.Count == 0) GUILayout.Label("No players.", small);
|
|
else
|
|
{
|
|
foreach (AdminPlayer p in adminPlayers)
|
|
{
|
|
GUILayout.BeginHorizontal(rowStyle, GUILayout.Height(44));
|
|
GUILayout.BeginVertical();
|
|
bool you = p.Id == mp.LocalId;
|
|
GUILayout.Label(p.Name + (you ? " (you)" : "") + (p.IsAdmin ? " (admin)" : ""), chatName);
|
|
GUILayout.Label(string.IsNullOrEmpty(p.SteamId) ? "id " + p.Id : p.SteamId.StartsWith("name:") ? "no Steam" : "SteamID " + p.SteamId, small);
|
|
GUILayout.EndVertical();
|
|
GUILayout.FlexibleSpace();
|
|
|
|
bool self = p.Id == mp.LocalId;
|
|
GUI.enabled = !self && !p.IsAdmin;
|
|
if (GUILayout.Button("KICK", secondary, GUILayout.Height(30), GUILayout.Width(80)))
|
|
mp.AdminAction(false, p.Id, adminReason);
|
|
GUILayout.Space(6);
|
|
bool confirming = confirmBanId == p.Id;
|
|
if (GUILayout.Button(confirming ? "SURE?" : "BAN", danger, GUILayout.Height(30), GUILayout.Width(80)))
|
|
{
|
|
if (confirming) { mp.AdminAction(true, p.Id, adminReason); confirmBanId = -1; }
|
|
else confirmBanId = p.Id;
|
|
}
|
|
GUI.enabled = true;
|
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(4);
|
|
}
|
|
}
|
|
GUILayout.EndScrollView();
|
|
GUILayout.EndArea();
|
|
}
|
|
|
|
// ---------------------------------------------------------------- Stile
|
|
|
|
void EnsureStyles()
|
|
{
|
|
if (stylesReady && panel.normal.background != null) return;
|
|
panel = UiTheme.Panel9(UiTheme.Background);
|
|
rowStyle = UiTheme.Panel9(new Color(0.06f, 0.06f, 0.07f, 0.72f));
|
|
rowStyle.padding = new RectOffset(12, 12, 6, 6);
|
|
field = UiTheme.Field(14);
|
|
logText = UiTheme.Label(12, UiTheme.Text);
|
|
logText.wordWrap = true;
|
|
logText.margin = new RectOffset(0, 0, 0, 0);
|
|
caption = UiTheme.Label(11, UiTheme.Muted, FontStyle.Bold);
|
|
title = UiTheme.Label(22, UiTheme.Text, FontStyle.Bold);
|
|
title.wordWrap = false;
|
|
small = UiTheme.Label(11, UiTheme.Muted);
|
|
primary = UiTheme.Button(UiTheme.Accent, UiTheme.AccentHover, Color.white);
|
|
secondary = UiTheme.Button(UiTheme.Secondary, UiTheme.SecondaryHover, UiTheme.Text, 12);
|
|
danger = UiTheme.Button(UiTheme.Danger, UiTheme.DangerHover, Color.white, 12);
|
|
chatName = UiTheme.Label(13, UiTheme.AccentHover, FontStyle.Bold, TextAnchor.MiddleLeft);
|
|
chatText = UiTheme.Label(13, UiTheme.Text, FontStyle.Normal, TextAnchor.MiddleLeft);
|
|
chatSystem = UiTheme.Label(12, UiTheme.Muted, FontStyle.Italic, TextAnchor.MiddleLeft);
|
|
stylesReady = true;
|
|
}
|
|
}
|
|
}
|