using BrewMonster.Network; using System; using System.Collections; using System.Collections.Generic; using EditorAttributes; using TMPro; using UnityEngine; using UnityEngine.UI; using BrewMonster.Scripts.UI; namespace BrewMonster.Scripts { public class DlgConsole : MonoBehaviour { [SerializeField] GameObject _panelConsole; [Header("Debug Cmd Input (new UI)")] [SerializeField] TMP_InputField _inputHeader; [SerializeField] TMP_InputField _inputParam; [SerializeField] TMP_InputField _inputTime; [SerializeField] Toggle _toggleLoop; [SerializeField] TMP_InputField _inputDescribe; [Space(6)] [Header("Legacy Console Input")] [SerializeField] TMP_InputField _consoleInput; //send button [SerializeField] Button _btnSend; [SerializeField] Button _btnCancelAllLoops; [SerializeField] Button _btnOpen; [SerializeField] Button _btnClose; [Space(10)] [Header("Command Buttons")] [SerializeField] Button _btnNoCooldown; [SerializeField] Button _btnToggleAutoWrath; [SerializeField] Button _btnToggleTaskNpcTeleport; private bool _isAutoAddWrathEnabled = false; private Coroutine _autoAddWrathCoroutine; private readonly Dictionary _loopCmds = new Dictionary(); public event Action LoopCommandsChanged; public event Action HistoryChanged; private List _history; private void Awake() { _history = DebugCmdHistoryStore.Load(); _btnSend?.onClick.AddListener(OnBtnSendClicked); _btnCancelAllLoops?.onClick.AddListener(CancelAllLoopCommands); _btnOpen?.onClick.AddListener(OnBtnOpenClicked); //_btnClose?.onClick.AddListener(OnBtnCloseClicked); _btnNoCooldown?.onClick.AddListener(OnBtnNoCooldownClicked); _btnToggleAutoWrath?.onClick.AddListener(OnBtnToggleAutoWrathClicked); _btnToggleTaskNpcTeleport?.onClick.AddListener(OnBtnToggleTaskNpcTeleportClicked); DontDestroyOnLoad(this); } public void OnBtnToggleTaskNpcTeleportClicked() { bool next = !CECUIHelper.DebugInstantTeleportToTaskNpc; CECUIHelper.SetDebugInstantTeleportToTaskNpc(next); Debug.Log($"[DlgConsole] Task NPC teleport cheat: {(next ? "ON" : "OFF")}"); } private void OnDestroy() { CancelAllLoopCommands(); // Stop coroutine if running when object is destroyed // 对象销毁时如果协程正在运行则停止它 if (_autoAddWrathCoroutine != null) { StopCoroutine(_autoAddWrathCoroutine); _autoAddWrathCoroutine = null; } } #region button events private void OnBtnSendClicked() { if (TryReadCmdFromNewInputs(out int header, out int param, out bool hasParam, out float intervalSeconds, out bool loopEnabled)) { UpsertHistoryFromInputs(header, param, hasParam); SendCmdDebug(header, param, hasParam); if (loopEnabled) { string describe = _inputDescribe != null ? _inputDescribe.text : string.Empty; StartOrRestartLoopCommand(header, param, hasParam, intervalSeconds, describe); } return; } // Fallback: legacy console style: "d
[param]" if (TryReadCmdFromLegacyConsole(out header, out param, out hasParam)) { UpsertHistory(header, param, hasParam, string.Empty); SendCmdDebug(header, param, hasParam); } } bool isOpen = false; private void OnBtnOpenClicked() { isOpen = !isOpen; _panelConsole.SetActive(isOpen); } /* private void OnBtnCloseClicked() { _panelConsole.SetActive(false); }*/ public void OnBtnNoCooldownClicked() { UnityGameSession.c2s_CmdDebug(8903, 73125); } /// 8903 73125 private void OnBtnAddWrathClicked() { UnityGameSession.c2s_CmdDebug(1992); } //1992 #endregion [ContextMenu("Toggle Auto-Add Wrath")] public void OnBtnToggleAutoWrathClicked() { ToggleAutoAddWrath(); } public void ToggleAutoAddWrath() { _isAutoAddWrathEnabled = !_isAutoAddWrathEnabled; if (_isAutoAddWrathEnabled) { // Start the coroutine if not already running // 如果尚未运行,则启动协程 if (_autoAddWrathCoroutine == null) { _autoAddWrathCoroutine = StartCoroutine(AutoAddWrathCoroutine()); } } else { if (_autoAddWrathCoroutine != null) { StopCoroutine(_autoAddWrathCoroutine); _autoAddWrathCoroutine = null; } } } private IEnumerator AutoAddWrathCoroutine() { while (_isAutoAddWrathEnabled) { OnBtnAddWrathClicked(); yield return new WaitForSeconds(3.0f); } _autoAddWrathCoroutine = null; } private void SendCmdDebug(int header, int param, bool hasParam) { //BMLogger.LogError($"[DlgConsole] Sending Debug Cmd: {(hasParam ? $"{header} {param}" : $"{header}")}"); if (hasParam) UnityGameSession.c2s_CmdDebug((ushort)header, param); else UnityGameSession.c2s_CmdDebug((ushort)header); } private void UpsertHistoryFromInputs(int header, int param, bool hasParam) { string describe = _inputDescribe != null ? _inputDescribe.text : string.Empty; UpsertHistory(header, param, hasParam, describe); } private void UpsertHistory(int header, int param, bool hasParam, string describe) { _history ??= new List(); bool changed = DebugCmdHistoryStore.Upsert(_history, header, param, hasParam, describe); if (changed) { DebugCmdHistoryStore.Save(_history); HistoryChanged?.Invoke(); } else { // Still persist last-used ordering (Upsert may have moved it) even if describe unchanged. DebugCmdHistoryStore.Save(_history); HistoryChanged?.Invoke(); } } public IReadOnlyList GetHistory() { _history ??= DebugCmdHistoryStore.Load(); return _history; } public void ClearHistory() { DebugCmdHistoryStore.Clear(); _history = new List(); HistoryChanged?.Invoke(); } public void RemoveHistoryItem(int header, int param, bool hasParam) { _history ??= DebugCmdHistoryStore.Load(); bool removed = DebugCmdHistoryStore.Remove(_history, header, param, hasParam); if (!removed) return; DebugCmdHistoryStore.Save(_history); HistoryChanged?.Invoke(); } public void ApplyHistoryItemToInputs(int header, int param, bool hasParam, string describe) { if (_inputHeader != null) _inputHeader.text = header.ToString(); if (_inputParam != null) _inputParam.text = hasParam ? param.ToString() : string.Empty; if (_inputDescribe != null) _inputDescribe.text = describe ?? string.Empty; } private bool TryReadCmdFromNewInputs(out int header, out int param, out bool hasParam, out float intervalSeconds, out bool loopEnabled) { header = 0; param = 0; hasParam = false; intervalSeconds = 1f; loopEnabled = _toggleLoop != null && _toggleLoop.isOn; // If these inputs aren't wired up in inspector, bail to legacy mode. if (_inputHeader == null && _inputParam == null && _inputTime == null && _toggleLoop == null && _inputDescribe == null) return false; var headerText = _inputHeader != null ? _inputHeader.text : string.Empty; var paramText = _inputParam != null ? _inputParam.text : string.Empty; var timeText = _inputTime != null ? _inputTime.text : string.Empty; if (string.IsNullOrWhiteSpace(headerText)) return false; if (!int.TryParse(headerText.Trim(), out header)) return false; if (!string.IsNullOrWhiteSpace(paramText) && int.TryParse(paramText.Trim(), out param)) hasParam = true; if (!string.IsNullOrWhiteSpace(timeText) && float.TryParse(timeText.Trim(), out float parsed)) intervalSeconds = Mathf.Max(0.05f, parsed); return true; } private bool TryReadCmdFromLegacyConsole(out int header, out int param, out bool hasParam) { header = 0; param = 0; hasParam = false; if (_consoleInput == null) return false; string input = _consoleInput.text; if (string.IsNullOrWhiteSpace(input)) return false; input = input.Trim(); if (!input.StartsWith("d ", StringComparison.OrdinalIgnoreCase)) return false; input = input.Substring(2).Trim(); if (string.IsNullOrWhiteSpace(input)) return false; string[] cmdParams = input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (cmdParams.Length < 1) return false; if (!int.TryParse(cmdParams[0], out header)) return false; if (cmdParams.Length >= 2 && int.TryParse(cmdParams[1], out param)) hasParam = true; return true; } private void StartOrRestartLoopCommand(int header, int param, bool hasParam, float intervalSeconds, string describe) { var key = new LoopCmdKey(header, param, hasParam); if (_loopCmds.TryGetValue(key, out var existing) && existing.Coroutine != null) { StopCoroutine(existing.Coroutine); _loopCmds.Remove(key); } var co = StartCoroutine(LoopSendCoroutine(key, intervalSeconds)); _loopCmds[key] = new LoopCmdInfo(co, intervalSeconds, describe); Debug.Log($"[DlgConsole] Loop ON: {key} every {intervalSeconds:0.###}s"); LoopCommandsChanged?.Invoke(); } public void CancelLoopCommand(int header, int param, bool hasParam) { var key = new LoopCmdKey(header, param, hasParam); if (_loopCmds.TryGetValue(key, out var info) && info.Coroutine != null) { StopCoroutine(info.Coroutine); _loopCmds.Remove(key); Debug.Log($"[DlgConsole] Loop OFF: {key}"); LoopCommandsChanged?.Invoke(); } } public void CancelAllLoopCommands() { if (_loopCmds.Count == 0) return; foreach (var kv in _loopCmds) { if (kv.Value.Coroutine != null) StopCoroutine(kv.Value.Coroutine); } _loopCmds.Clear(); Debug.Log("[DlgConsole] Loop OFF: ALL"); LoopCommandsChanged?.Invoke(); } public string GetActiveLoopCommandsDebugString() { if (_loopCmds.Count == 0) return "(none)"; var parts = new List(_loopCmds.Count); foreach (var key in _loopCmds.Keys) parts.Add(key.ToString()); return string.Join(", ", parts); } public IReadOnlyList GetActiveLoopCommands() { if (_loopCmds.Count == 0) return Array.Empty(); var list = new List(_loopCmds.Count); foreach (var kv in _loopCmds) { list.Add(new ActiveLoopCommand( kv.Key.Header, kv.Key.Param, kv.Key.HasParam, kv.Value.IntervalSeconds, kv.Value.Describe )); } return list; } private IEnumerator LoopSendCoroutine(LoopCmdKey key, float intervalSeconds) { // Send immediately already happened in OnBtnSendClicked; wait interval then keep sending. var wait = new WaitForSeconds(intervalSeconds); while (true) { yield return wait; SendCmdDebug(key.Header, key.Param, key.HasParam); } } private readonly struct LoopCmdInfo { public readonly Coroutine Coroutine; public readonly float IntervalSeconds; public readonly string Describe; public LoopCmdInfo(Coroutine coroutine, float intervalSeconds, string describe) { Coroutine = coroutine; IntervalSeconds = intervalSeconds; Describe = describe ?? string.Empty; } } public readonly struct ActiveLoopCommand { public readonly int Header; public readonly int Param; public readonly bool HasParam; public readonly float IntervalSeconds; public readonly string Describe; public ActiveLoopCommand(int header, int param, bool hasParam, float intervalSeconds, string describe) { Header = header; Param = param; HasParam = hasParam; IntervalSeconds = intervalSeconds; Describe = describe ?? string.Empty; } public override string ToString() { var cmd = HasParam ? $"{Header} {Param}" : $"{Header}"; if (!string.IsNullOrWhiteSpace(Describe)) return $"{Describe} ({cmd}) @ {IntervalSeconds:0.###}s"; return $"{cmd} @ {IntervalSeconds:0.###}s"; } } private readonly struct LoopCmdKey : IEquatable { public readonly int Header; public readonly int Param; public readonly bool HasParam; public LoopCmdKey(int header, int param, bool hasParam) { Header = header; Param = param; HasParam = hasParam; } public bool Equals(LoopCmdKey other) => Header == other.Header && Param == other.Param && HasParam == other.HasParam; public override bool Equals(object obj) => obj is LoopCmdKey other && Equals(other); public override int GetHashCode() { unchecked { int hash = 17; hash = (hash * 31) + Header; hash = (hash * 31) + Param; hash = (hash * 31) + (HasParam ? 1 : 0); return hash; } } public override string ToString() => HasParam ? $"{Header} {Param}" : $"{Header}"; } } } /// d 2000 la kn /// d 1988 + so tien