using UnityEngine;
|
using UnityEditor;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.IO;
|
using System.Reflection;
|
|
public class GMQuickPlayingEditor : EditorWindow
|
{
|
private string searchText = "";
|
private string commandText = "";
|
private Vector2 scrollPosition;
|
private Vector2 reportScrollPosition;
|
private Vector2 battleInfoScrollPosition;
|
private Vector2 gmMessageScrollPosition; // GM消息滚动位置
|
private Vector2 gmHistoryScrollPosition; // GM历史命令滚动位置
|
private List<ItemConfig> searchResults = new List<ItemConfig>();
|
private int itemCount = 1;
|
private bool isBattlePaused = false;
|
private bool showBattleInfo = false;
|
private bool showGmPanel = true; // 显示GM面板(左侧整个区域)
|
private List<string> gmMessages = new List<string>(); // 存储GM消息
|
private List<string> gmCommandHistory = new List<string>(); // 存储历史GM命令
|
private const int maxGmMessages = 50; // 最多保存50条消息
|
private const int maxGmHistory = 30; // 最多保存30条历史命令
|
|
// 模拟发包工具相关
|
private bool showPacketSimulator = false; // 显示模拟发包工具
|
private Vector2 packetScrollPosition; // 包字段滚动位置
|
private string packetClassName = ""; // 输入的包类名
|
private System.Type currentPacketType = null; // 当前包类型
|
private object currentPacketInstance = null; // 当前包实例
|
private Dictionary<FieldInfo, object> fieldValues = new Dictionary<FieldInfo, object>(); // 存储字段值
|
|
// 战报相关
|
private string reportFilePath = "";
|
private string[] reportFiles = new string[0];
|
private int selectedReportIndex = 0;
|
|
[MenuItem("Tools/GM快捷工具 &9")]
|
public static void ShowWindow()
|
{
|
GetWindow<GMQuickPlayingEditor>("GM快捷工具");
|
}
|
|
private void OnEnable()
|
{
|
RefreshReportList();
|
LoadGmCommandHistory();
|
// 订阅GM消息刷新事件
|
if (Application.isPlaying)
|
{
|
ServerTipDetails.gmMessageRefresh += OnGmMessageReceived;
|
}
|
}
|
|
private void OnDisable()
|
{
|
SaveGmCommandHistory();
|
// 取消订阅
|
if (Application.isPlaying)
|
{
|
ServerTipDetails.gmMessageRefresh -= OnGmMessageReceived;
|
}
|
}
|
|
private void Update()
|
{
|
// 每帧更新战场信息
|
Repaint();
|
}
|
|
private void OnGUI()
|
{
|
// ========== 顶部功能开关区域 ==========
|
GUILayout.BeginVertical("box");
|
GUILayout.Label("功能面板", EditorStyles.boldLabel);
|
|
GUILayout.BeginHorizontal();
|
GUILayout.FlexibleSpace();
|
|
// 显示战场信息开关
|
GUILayout.Label("显示战场信息", GUILayout.Width(100));
|
showBattleInfo = EditorGUILayout.Toggle(showBattleInfo, GUILayout.Width(20));
|
|
GUILayout.Space(20);
|
|
// 显示GM面板开关
|
GUILayout.Label("显示GM", GUILayout.Width(100));
|
showGmPanel = EditorGUILayout.Toggle(showGmPanel, GUILayout.Width(20));
|
|
GUILayout.Space(20);
|
|
// 显示模拟发包工具开关
|
GUILayout.Label("模拟发包工具", GUILayout.Width(100));
|
showPacketSimulator = EditorGUILayout.Toggle(showPacketSimulator, GUILayout.Width(20));
|
|
GUILayout.FlexibleSpace();
|
GUILayout.EndHorizontal();
|
|
GUILayout.EndVertical();
|
|
GUILayout.Space(5);
|
|
GUILayout.BeginHorizontal();
|
|
// ========== 左侧面板 - GM工具 ==========
|
if (showGmPanel)
|
{
|
float leftWidth = showBattleInfo ? position.width * 0.6f : position.width - 20;
|
GUILayout.BeginVertical("box", GUILayout.Width(leftWidth));
|
|
// 搜索栏
|
GUILayout.Label("物品搜索", EditorStyles.boldLabel);
|
GUILayout.BeginHorizontal();
|
searchText = EditorGUILayout.TextField("搜索:", searchText);
|
if (GUILayout.Button("查询物品", GUILayout.Width(80)))
|
{
|
OnSearchItem();
|
}
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(10);
|
|
// 搜索结果显示区域
|
GUILayout.BeginHorizontal();
|
GUILayout.Label($"搜索结果 ({searchResults.Count})", EditorStyles.boldLabel);
|
GUILayout.FlexibleSpace();
|
if (GUILayout.Button("清空列表", GUILayout.Width(80)))
|
{
|
searchResults.Clear();
|
}
|
GUILayout.EndHorizontal();
|
|
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Height(200));
|
GUILayout.BeginVertical("box");
|
|
if (searchResults.Count > 0)
|
{
|
foreach (var item in searchResults)
|
{
|
GUILayout.BeginHorizontal("box");
|
GUILayout.Label($"ID: {item.ID}", GUILayout.Width(80));
|
GUILayout.Label($"名称: {item.ItemName}", GUILayout.Width(150));
|
GUILayout.Label($"等级: {item.LV}", GUILayout.Width(60));
|
if (GUILayout.Button("选择", GUILayout.Width(60)))
|
{
|
OnSelectItem(item);
|
}
|
GUILayout.EndHorizontal();
|
}
|
}
|
else
|
{
|
GUILayout.Label("搜索结果将在这里显示...");
|
}
|
|
GUILayout.EndVertical();
|
GUILayout.EndScrollView();
|
|
GUILayout.Space(10);
|
|
// 数量输入
|
GUILayout.BeginHorizontal();
|
GUILayout.Label("物品数量:", GUILayout.Width(80));
|
itemCount = EditorGUILayout.IntField(itemCount, GUILayout.Width(100));
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(10);
|
|
// GM命令编辑区域
|
GUILayout.Label("GM命令", EditorStyles.boldLabel);
|
GUILayout.BeginVertical("box");
|
commandText = EditorGUILayout.TextArea(commandText, GUILayout.Height(60));
|
GUILayout.EndVertical();
|
|
GUILayout.Space(5);
|
|
// 发送命令按钮
|
GUILayout.BeginHorizontal();
|
GUILayout.FlexibleSpace();
|
if (GUILayout.Button("发送命令", GUILayout.Width(100), GUILayout.Height(30)))
|
{
|
OnSendCommand();
|
}
|
if (GUILayout.Button("清空", GUILayout.Width(60), GUILayout.Height(30)))
|
{
|
commandText = "";
|
}
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(5);
|
|
// 提示信息
|
GUIStyle hintStyle = new GUIStyle(EditorStyles.helpBox);
|
hintStyle.normal.textColor = new Color(0.5f, 0.8f, 1f);
|
hintStyle.fontSize = 10;
|
EditorGUILayout.LabelField("💡 支持多行命令,每行一条指令", hintStyle);
|
|
GUILayout.Space(10);
|
|
// ========== GM历史命令与消息输出区域(左右分栏)==========
|
GUILayout.BeginHorizontal();
|
|
// 左侧:历史命令
|
GUILayout.BeginVertical(GUILayout.Width(leftWidth * 0.5f - 10));
|
|
GUILayout.BeginHorizontal();
|
GUILayout.Label("历史命令", EditorStyles.boldLabel);
|
GUILayout.FlexibleSpace();
|
if (GUILayout.Button("清空", GUILayout.Width(60)))
|
{
|
if (EditorUtility.DisplayDialog("确认清空", "确定要清空所有历史命令吗?", "确定", "取消"))
|
{
|
gmCommandHistory.Clear();
|
SaveGmCommandHistory();
|
}
|
}
|
GUILayout.EndHorizontal();
|
|
gmHistoryScrollPosition = GUILayout.BeginScrollView(gmHistoryScrollPosition, "box", GUILayout.Height(150));
|
|
if (gmCommandHistory.Count > 0)
|
{
|
for (int i = gmCommandHistory.Count - 1; i >= 0; i--)
|
{
|
GUILayout.BeginHorizontal("box");
|
|
// 显示命令(截断长文本)
|
string displayCmd = gmCommandHistory[i];
|
if (displayCmd.Length > 30)
|
{
|
displayCmd = displayCmd.Substring(0, 27) + "...";
|
}
|
|
if (GUILayout.Button(displayCmd, EditorStyles.label))
|
{
|
OnClickHistoryCommand(gmCommandHistory[i]);
|
}
|
|
// 删除按钮
|
if (GUILayout.Button("×", GUILayout.Width(20)))
|
{
|
gmCommandHistory.RemoveAt(i);
|
SaveGmCommandHistory();
|
}
|
|
GUILayout.EndHorizontal();
|
}
|
}
|
else
|
{
|
GUILayout.Label("历史命令将在这里显示...", EditorStyles.centeredGreyMiniLabel);
|
}
|
|
GUILayout.EndScrollView();
|
|
GUILayout.EndVertical();
|
|
// 中间分割线
|
GUILayout.Box("", GUILayout.Width(2), GUILayout.Height(150));
|
|
// 右侧:消息输出
|
GUILayout.BeginVertical(GUILayout.ExpandWidth(true));
|
|
GUILayout.BeginHorizontal();
|
GUILayout.Label("GM消息输出", EditorStyles.boldLabel);
|
GUILayout.FlexibleSpace();
|
if (GUILayout.Button("清空", GUILayout.Width(60)))
|
{
|
gmMessages.Clear();
|
}
|
GUILayout.EndHorizontal();
|
|
gmMessageScrollPosition = GUILayout.BeginScrollView(gmMessageScrollPosition, "box", GUILayout.Height(150));
|
|
if (gmMessages.Count > 0)
|
{
|
for (int i = gmMessages.Count - 1; i >= 0; i--)
|
{
|
GUILayout.Label(gmMessages[i], EditorStyles.wordWrappedLabel);
|
}
|
}
|
else
|
{
|
GUILayout.Label("GM消息将在这里显示...", EditorStyles.centeredGreyMiniLabel);
|
}
|
|
GUILayout.EndScrollView();
|
|
GUILayout.EndVertical();
|
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(10);
|
|
// 提示信息
|
GUIStyle warningStyle = new GUIStyle(EditorStyles.helpBox);
|
warningStyle.normal.textColor = new Color(1f, 0.6f, 0f);
|
warningStyle.fontSize = 11;
|
EditorGUILayout.LabelField("⚠ 添加物品最好跟服务器确认过物品的存在后再添加", warningStyle);
|
|
GUILayout.EndVertical();
|
}
|
|
// ========== 中间面板 - 模拟发包工具 ==========
|
if (showPacketSimulator)
|
{
|
// 分割线
|
if (showGmPanel)
|
{
|
GUILayout.Box("", GUILayout.Width(2), GUILayout.ExpandHeight(true));
|
}
|
|
float packetWidth = showBattleInfo ? position.width * 0.35f : (showGmPanel ? position.width * 0.4f : position.width - 20);
|
GUILayout.BeginVertical("box", GUILayout.Width(packetWidth));
|
|
DrawPacketSimulator();
|
|
GUILayout.EndVertical();
|
}
|
|
// ========== 右侧面板 - 战场信息 ==========
|
if (showBattleInfo)
|
{
|
// 分割线
|
GUILayout.Box("", GUILayout.Width(2), GUILayout.ExpandHeight(true));
|
|
GUILayout.BeginVertical("box", GUILayout.ExpandWidth(true));
|
|
GUILayout.Label("战场信息", EditorStyles.boldLabel);
|
|
// 暂停/开始战斗按钮
|
GUILayout.BeginHorizontal();
|
GUILayout.FlexibleSpace();
|
string buttonText = isBattlePaused ? "开始当前战斗" : "暂停当前战斗";
|
if (GUILayout.Button(buttonText, GUILayout.Width(120), GUILayout.Height(30)))
|
{
|
OnPauseBattle();
|
}
|
GUILayout.FlexibleSpace();
|
GUILayout.EndHorizontal();
|
|
// 添加跳过战斗按钮
|
GUILayout.BeginHorizontal();
|
GUILayout.FlexibleSpace();
|
BattleField bf = GetActiveBattleField();
|
bool canSkip = bf != null && !string.IsNullOrEmpty(bf.guid);
|
GUI.enabled = canSkip;
|
if (GUILayout.Button("跳过当前战斗", GUILayout.Width(120), GUILayout.Height(30)))
|
{
|
OnSkipBattle();
|
}
|
GUI.enabled = true;
|
GUILayout.FlexibleSpace();
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(10);
|
|
battleInfoScrollPosition = GUILayout.BeginScrollView(battleInfoScrollPosition);
|
|
DrawBattleFieldInfo();
|
|
GUILayout.Space(20);
|
|
// 战报浏览区域
|
GUILayout.Label("战报浏览", EditorStyles.boldLabel);
|
GUILayout.BeginVertical("box");
|
|
GUILayout.BeginHorizontal();
|
if (GUILayout.Button("刷新战报列表", GUILayout.Width(120)))
|
{
|
RefreshReportList();
|
}
|
if (GUILayout.Button("打开战报文件夹", GUILayout.Width(120)))
|
{
|
OpenReportFolder();
|
}
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(5);
|
|
if (reportFiles.Length > 0)
|
{
|
reportScrollPosition = GUILayout.BeginScrollView(reportScrollPosition, GUILayout.Height(200));
|
|
for (int i = 0; i < reportFiles.Length; i++)
|
{
|
GUILayout.BeginHorizontal("box");
|
string fileName = Path.GetFileName(reportFiles[i]);
|
GUILayout.Label(fileName, GUILayout.ExpandWidth(true));
|
|
if (GUILayout.Button("加载", GUILayout.Width(60)))
|
{
|
LoadReport(reportFiles[i]);
|
}
|
|
if (GUILayout.Button("删除", GUILayout.Width(60)))
|
{
|
DeleteReport(reportFiles[i]);
|
}
|
|
GUILayout.EndHorizontal();
|
}
|
|
GUILayout.EndScrollView();
|
}
|
else
|
{
|
GUILayout.Label("未找到战报文件(.tfr)", EditorStyles.centeredGreyMiniLabel);
|
}
|
|
GUILayout.EndVertical();
|
|
GUILayout.EndScrollView();
|
|
GUILayout.EndVertical();
|
}
|
|
GUILayout.EndHorizontal();
|
}
|
|
private void DrawBattleFieldInfo()
|
{
|
BattleField battleField = GetActiveBattleField();
|
|
if (battleField == null)
|
{
|
GUILayout.Label("暂无战场", EditorStyles.centeredGreyMiniLabel);
|
return;
|
}
|
|
// 战场基本信息
|
GUILayout.BeginVertical("box");
|
GUILayout.Label($"战场类型: {battleField.GetType().Name}", EditorStyles.boldLabel);
|
GUILayout.Label($"GUID: {battleField.guid}");
|
GUILayout.Label($"地图ID: {battleField.MapID}");
|
GUILayout.Label($"暂停状态: {battleField.IsPause}");
|
GUILayout.EndVertical();
|
|
GUILayout.Space(10);
|
|
// 红蓝双方队伍信息(横向排列)
|
GUILayout.BeginHorizontal();
|
|
// 红方队伍(左侧)
|
GUILayout.BeginVertical("box", GUILayout.ExpandWidth(true));
|
DrawTeamInfo("红方队伍", battleField.battleObjMgr.redCampList, new Color(1f, 0.5f, 0.5f));
|
GUILayout.EndVertical();
|
|
GUILayout.Space(10);
|
|
// 蓝方队伍(右侧)
|
GUILayout.BeginVertical("box", GUILayout.ExpandWidth(true));
|
DrawTeamInfo("蓝方队伍", battleField.battleObjMgr.blueCampList, new Color(0.5f, 0.5f, 1f));
|
GUILayout.EndVertical();
|
|
GUILayout.EndHorizontal();
|
}
|
|
private void DrawTeamInfo(string teamName, List<BattleObject> team, Color headerColor)
|
{
|
GUIStyle headerStyle = new GUIStyle(EditorStyles.boldLabel);
|
headerStyle.normal.textColor = headerColor;
|
|
GUILayout.Label(teamName, headerStyle);
|
|
if (team == null || team.Count == 0)
|
{
|
GUILayout.Label(" 无角色", EditorStyles.miniLabel);
|
return;
|
}
|
|
foreach (var obj in team)
|
{
|
if (obj == null || !(obj is HeroBattleObject heroBattleObject))
|
continue;
|
|
GUILayout.BeginVertical("box");
|
|
// 角色名称和ID
|
GUILayout.BeginHorizontal();
|
GUILayout.Label($"ID: {obj.ObjID}", GUILayout.Width(80));
|
GUILayout.Label($"名称: {heroBattleObject.teamHero.name}", GUILayout.ExpandWidth(true));
|
GUILayout.EndHorizontal();
|
|
// 血量信息
|
long curHp = heroBattleObject.teamHero.curHp;
|
long maxHp = heroBattleObject.teamHero.maxHp;
|
float hpPercent = maxHp > 0 ? (float)curHp / maxHp : 0f;
|
|
GUILayout.BeginHorizontal();
|
GUILayout.Label("血量:", GUILayout.Width(50));
|
|
// 血条
|
Rect hpBarRect = GUILayoutUtility.GetRect(100, 18);
|
EditorGUI.DrawRect(hpBarRect, new Color(0.3f, 0.3f, 0.3f));
|
|
Rect hpFillRect = new Rect(hpBarRect.x, hpBarRect.y, hpBarRect.width * hpPercent, hpBarRect.height);
|
Color hpColor = hpPercent > 0.5f ? Color.green : hpPercent > 0.2f ? Color.yellow : Color.red;
|
EditorGUI.DrawRect(hpFillRect, hpColor);
|
|
GUILayout.Label($"{curHp}/{maxHp} ({(hpPercent * 100f):F1}%)", GUILayout.Width(150));
|
GUILayout.EndHorizontal();
|
|
// 怒气信息
|
long curMp = heroBattleObject.teamHero.rage;
|
long maxMp = 100;
|
float mpPercent = maxMp > 0 ? (float)curMp / maxMp : 0f;
|
|
GUILayout.BeginHorizontal();
|
GUILayout.Label("怒气:", GUILayout.Width(50));
|
|
// 怒气条
|
Rect mpBarRect = GUILayoutUtility.GetRect(100, 18);
|
EditorGUI.DrawRect(mpBarRect, new Color(0.3f, 0.3f, 0.3f));
|
|
Rect mpFillRect = new Rect(mpBarRect.x, mpBarRect.y, mpBarRect.width * mpPercent, mpBarRect.height);
|
EditorGUI.DrawRect(mpFillRect, new Color(1f, 0.5f, 0f)); // 橙色
|
|
GUILayout.Label($"{curMp}/{maxMp} ({(mpPercent * 100f):F1}%)", GUILayout.Width(150));
|
GUILayout.EndHorizontal();
|
|
// Buff信息(只有 Hero 有 buff)
|
var buffMgr = heroBattleObject.GetBuffMgr();
|
if (buffMgr != null)
|
{
|
var buffList = buffMgr.GetBuffDataList();
|
if (buffList != null && buffList.Count > 0)
|
{
|
GUILayout.Space(5);
|
GUILayout.Label("Buff列表:", EditorStyles.miniBoldLabel);
|
|
foreach (var buffData in buffList)
|
{
|
if (buffData != null)
|
{
|
string buffName = SkillConfig.Get((int)buffData.SkillID).SkillName;
|
int layer = buffData.Layer;
|
|
GUILayout.BeginHorizontal();
|
GUILayout.Space(20);
|
GUILayout.Label($"• {buffName} x{layer}", EditorStyles.miniLabel);
|
GUILayout.EndHorizontal();
|
}
|
}
|
}
|
}
|
|
GUILayout.EndVertical();
|
}
|
}
|
|
private BattleField GetActiveBattleField()
|
{
|
if (!Application.isPlaying)
|
return null;
|
|
for (int i = 0; i < BattleConst.BattleWindows.Count; i++)
|
{
|
var win = UIManager.Instance.GetUI(BattleConst.BattleWindows[i].Name);
|
if (win != null && win.IsActive())
|
{
|
// 使用反射获取 battleField 字段
|
var battleFieldField = win.GetType().GetField("battleField",
|
System.Reflection.BindingFlags.NonPublic |
|
System.Reflection.BindingFlags.Instance);
|
|
if (battleFieldField != null)
|
{
|
var battleField = battleFieldField.GetValue(win) as BattleField;
|
if (battleField != null)
|
{
|
return battleField;
|
}
|
}
|
}
|
}
|
|
return null;
|
}
|
|
private void RefreshReportList()
|
{
|
string reportFolder = Application.dataPath + "/../BattleReport/";
|
if (Directory.Exists(reportFolder))
|
{
|
reportFiles = Directory.GetFiles(reportFolder, "*.tfr");
|
Debug.Log($"找到 {reportFiles.Length} 个战报文件");
|
}
|
else
|
{
|
Directory.CreateDirectory(reportFolder);
|
reportFiles = new string[0];
|
Debug.Log("创建战报文件夹: " + reportFolder);
|
}
|
}
|
|
private void OpenReportFolder()
|
{
|
string reportFolder = Application.dataPath + "/../BattleReport/";
|
if (!Directory.Exists(reportFolder))
|
{
|
Directory.CreateDirectory(reportFolder);
|
}
|
EditorUtility.RevealInFinder(reportFolder);
|
}
|
|
private void LoadReport(string filePath)
|
{
|
if (!File.Exists(filePath))
|
{
|
Debug.LogError("战报文件不存在: " + filePath);
|
return;
|
}
|
|
reportFilePath = filePath;
|
Debug.Log("已加载战报: " + Path.GetFileName(filePath));
|
|
HB430_tagSCTurnFightReport hB430_TagSCTurnFightReport = new HB430_tagSCTurnFightReport();
|
hB430_TagSCTurnFightReport.isRecord = true;
|
byte[] vBytes = File.ReadAllBytes(filePath);
|
hB430_TagSCTurnFightReport.ReadFromBytes(vBytes);
|
PackageRegedit.Distribute(hB430_TagSCTurnFightReport);
|
}
|
|
private void DeleteReport(string filePath)
|
{
|
if (!File.Exists(filePath))
|
{
|
Debug.LogError("战报文件不存在: " + filePath);
|
return;
|
}
|
|
if (EditorUtility.DisplayDialog("确认删除",
|
$"确定要删除战报文件吗?\n{Path.GetFileName(filePath)}",
|
"删除", "取消"))
|
{
|
try
|
{
|
File.Delete(filePath);
|
Debug.Log("已删除战报: " + Path.GetFileName(filePath));
|
RefreshReportList();
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError("删除战报文件失败: " + e.Message);
|
}
|
}
|
}
|
|
private string ExtractGuidFromFileName(string fileName)
|
{
|
// 从文件名中提取 GUID
|
// 格式: B430_ReportBytes_{GUID}_{DateTime}
|
string[] parts = fileName.Split('_');
|
if (parts.Length >= 3)
|
{
|
return parts[2]; // 返回 GUID 部分
|
}
|
return "unknown_guid";
|
}
|
|
private void OnSearchItem()
|
{
|
searchResults.Clear();
|
|
if (string.IsNullOrEmpty(searchText))
|
{
|
Debug.Log("搜索文本为空");
|
return;
|
}
|
|
// 遍历 ItemConfig.dic 进行模糊搜索
|
foreach (var kvp in ItemConfig.dic)
|
{
|
var item = kvp.Value;
|
if (!string.IsNullOrEmpty(item.ItemName) &&
|
item.ItemName.Contains(searchText))
|
{
|
searchResults.Add(item);
|
}
|
}
|
|
// 按 ID 排序
|
searchResults = searchResults.OrderBy(x => x.ID).ToList();
|
|
Debug.Log($"搜索物品: {searchText}, 找到 {searchResults.Count} 个结果");
|
}
|
|
private void OnSelectItem(ItemConfig item)
|
{
|
Debug.Log($"选择物品: {item.ItemName} (ID: {item.ID})");
|
commandText = $"MakeItemCount {item.ID} {itemCount}";
|
}
|
|
private void OnGmMessageReceived(string message)
|
{
|
// 添加时间戳
|
string timeStamp = System.DateTime.Now.ToString("HH:mm:ss");
|
string formattedMessage = $"[{timeStamp}] {message}";
|
|
gmMessages.Add(formattedMessage);
|
|
// 限制消息数量
|
if (gmMessages.Count > maxGmMessages)
|
{
|
gmMessages.RemoveAt(0);
|
}
|
|
// 自动滚动到底部
|
gmMessageScrollPosition.y = float.MaxValue;
|
|
Repaint();
|
}
|
|
private void OnClickHistoryCommand(string command)
|
{
|
// 智能换行:如果当前有内容且不是以换行结尾,就添加换行
|
if (!string.IsNullOrEmpty(commandText) && !commandText.EndsWith("\n"))
|
{
|
commandText += "\n";
|
}
|
|
commandText += command;
|
|
Debug.Log($"已添加历史命令: {command}");
|
}
|
|
private void OnSendCommand()
|
{
|
if (string.IsNullOrEmpty(commandText))
|
{
|
Debug.LogWarning("命令为空,无法发送");
|
return;
|
}
|
|
// 按换行符分割命令
|
string[] commands = commandText.Split(new[] { "\r\n", "\r", "\n" }, System.StringSplitOptions.RemoveEmptyEntries);
|
|
if (commands.Length == 0)
|
{
|
Debug.LogWarning("没有有效的命令");
|
return;
|
}
|
|
// 发送每条命令并添加到历史记录
|
foreach (string cmd in commands)
|
{
|
string trimmedCmd = cmd.Trim();
|
if (!string.IsNullOrEmpty(trimmedCmd))
|
{
|
Debug.Log("发送命令: " + trimmedCmd);
|
GMCmdManager.Instance.OnSendGMQuest(trimmedCmd);
|
GMCmdManager.Instance.SetRecordCmdlist(trimmedCmd);
|
|
// 添加到历史记录(避免重复)
|
if (!gmCommandHistory.Contains(trimmedCmd))
|
{
|
gmCommandHistory.Add(trimmedCmd);
|
|
// 限制历史记录数量
|
if (gmCommandHistory.Count > maxGmHistory)
|
{
|
gmCommandHistory.RemoveAt(0);
|
}
|
}
|
}
|
}
|
|
if (commands.Length > 1)
|
{
|
Debug.Log($"已发送 {commands.Length} 条命令");
|
}
|
|
// 保存历史记录
|
SaveGmCommandHistory();
|
}
|
|
private void LoadGmCommandHistory()
|
{
|
string historyFile = Application.dataPath + "/../GMCommandHistory.txt";
|
if (File.Exists(historyFile))
|
{
|
try
|
{
|
string[] lines = File.ReadAllLines(historyFile);
|
gmCommandHistory = new List<string>(lines);
|
Debug.Log($"加载了 {gmCommandHistory.Count} 条历史GM命令");
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError("加载GM命令历史失败: " + e.Message);
|
gmCommandHistory = new List<string>();
|
}
|
}
|
}
|
|
private void SaveGmCommandHistory()
|
{
|
string historyFile = Application.dataPath + "/../GMCommandHistory.txt";
|
try
|
{
|
File.WriteAllLines(historyFile, gmCommandHistory.ToArray());
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError("保存GM命令历史失败: " + e.Message);
|
}
|
}
|
|
private void OnPauseBattle()
|
{
|
for (int i = 0; i < BattleConst.BattleWindows.Count; i++)
|
{
|
var win = UIManager.Instance.GetUI(BattleConst.BattleWindows[i].Name);
|
if (win != null && win.IsActive())
|
{
|
// 使用反射获取 battleField 字段
|
var battleFieldField = win.GetType().GetField("battleField",
|
System.Reflection.BindingFlags.NonPublic |
|
System.Reflection.BindingFlags.Instance);
|
|
if (battleFieldField != null)
|
{
|
var battleField = battleFieldField.GetValue(win) as BattleField;
|
if (battleField != null)
|
{
|
battleField.IsPause = !battleField.IsPause;
|
isBattlePaused = battleField.IsPause;
|
Debug.Log($"战斗暂停状态切换: {battleField.IsPause}");
|
return;
|
}
|
}
|
}
|
}
|
|
Debug.LogWarning("未找到激活的战斗窗口或 battleField 为空");
|
}
|
|
private void OnSkipBattle()
|
{
|
BattleField battleField = GetActiveBattleField();
|
if (battleField == null)
|
{
|
Debug.LogWarning("未找到激活的战场");
|
return;
|
}
|
|
if (battleField.guid == string.Empty)
|
{
|
Debug.LogWarning("主线关卡无法跳过");
|
return;
|
}
|
|
battleField.ForceFinish();
|
|
}
|
|
// ========== 模拟发包工具相关方法 ==========
|
|
private void DrawPacketSimulator()
|
{
|
GUILayout.Label("📦 模拟发包工具", EditorStyles.boldLabel);
|
|
GUILayout.Space(5);
|
|
// 包名输入区域
|
GUILayout.BeginVertical("box");
|
GUILayout.Label("包类名(例如:HB301_tagCSLoginReq)", EditorStyles.miniBoldLabel);
|
|
GUILayout.BeginHorizontal();
|
string newPacketClassName = EditorGUILayout.TextField(packetClassName);
|
|
// 检测包名是否改变
|
if (newPacketClassName != packetClassName)
|
{
|
packetClassName = newPacketClassName;
|
// 清空当前包信息
|
currentPacketType = null;
|
currentPacketInstance = null;
|
fieldValues.Clear();
|
}
|
|
if (GUILayout.Button("加载包", GUILayout.Width(80)))
|
{
|
LoadPacketType();
|
}
|
GUILayout.EndHorizontal();
|
|
GUILayout.EndVertical();
|
|
GUILayout.Space(10);
|
|
// 包字段显示和编辑区域
|
if (currentPacketType != null && currentPacketInstance != null)
|
{
|
GUILayout.BeginVertical("box");
|
GUILayout.Label($"📋 包字段 - {currentPacketType.Name}", EditorStyles.boldLabel);
|
|
packetScrollPosition = GUILayout.BeginScrollView(packetScrollPosition, GUILayout.ExpandHeight(true));
|
|
DrawPacketFields();
|
|
GUILayout.EndScrollView();
|
GUILayout.EndVertical();
|
|
GUILayout.Space(10);
|
|
// 操作按钮
|
GUILayout.BeginHorizontal();
|
|
// 发送按钮(绿色)
|
GUI.backgroundColor = new Color(0.5f, 1f, 0.5f);
|
if (GUILayout.Button("✉ 发送包", GUILayout.Height(35)))
|
{
|
SendPacket();
|
}
|
GUI.backgroundColor = Color.white;
|
|
GUILayout.Space(10);
|
|
// 清空按钮(橙色)
|
GUI.backgroundColor = new Color(1f, 0.8f, 0.4f);
|
if (GUILayout.Button("🗑 清空字段", GUILayout.Height(35)))
|
{
|
ClearPacketFields();
|
}
|
GUI.backgroundColor = Color.white;
|
|
GUILayout.EndHorizontal();
|
}
|
else
|
{
|
GUILayout.FlexibleSpace();
|
|
GUIStyle hintStyle = new GUIStyle(EditorStyles.helpBox);
|
hintStyle.normal.textColor = new Color(0.7f, 0.7f, 0.7f);
|
hintStyle.fontSize = 12;
|
hintStyle.alignment = TextAnchor.MiddleCenter;
|
hintStyle.wordWrap = true;
|
|
GUILayout.Label("💡 输入包类名后点击\"加载包\"按钮\n即可自动识别并编辑包的所有字段", hintStyle, GUILayout.Height(80));
|
|
GUILayout.FlexibleSpace();
|
}
|
}
|
|
private void LoadPacketType()
|
{
|
if (string.IsNullOrEmpty(packetClassName))
|
{
|
Debug.LogWarning("包类名不能为空");
|
return;
|
}
|
|
try
|
{
|
// 尝试在所有已加载的程序集中查找类型
|
System.Type foundType = null;
|
foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
|
{
|
foundType = assembly.GetType(packetClassName);
|
if (foundType != null)
|
break;
|
}
|
|
if (foundType == null)
|
{
|
Debug.LogError($"未找到类型: {packetClassName},请检查类名是否正确(区分大小写)");
|
return;
|
}
|
|
// 检查是否继承自 GameNetPackBasic
|
if (!typeof(GameNetPackBasic).IsAssignableFrom(foundType))
|
{
|
Debug.LogError($"类型 {packetClassName} 不是 GameNetPackBasic 的子类");
|
return;
|
}
|
|
currentPacketType = foundType;
|
currentPacketInstance = System.Activator.CreateInstance(foundType);
|
fieldValues.Clear();
|
|
// 初始化字段值
|
var fields = GetAllFields(foundType);
|
foreach (var field in fields)
|
{
|
object defaultValue = field.GetValue(currentPacketInstance);
|
fieldValues[field] = defaultValue;
|
}
|
|
Debug.Log($"成功加载包: {foundType.Name},共 {fields.Count} 个字段");
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError($"加载包类型失败: {e.Message}");
|
currentPacketType = null;
|
currentPacketInstance = null;
|
fieldValues.Clear();
|
}
|
}
|
|
private List<FieldInfo> GetAllFields(System.Type type)
|
{
|
List<FieldInfo> fields = new List<FieldInfo>();
|
|
// 只获取当前类声明的字段,不包括父类字段
|
FieldInfo[] typeFields = type.GetFields(
|
BindingFlags.Public |
|
BindingFlags.NonPublic |
|
BindingFlags.Instance |
|
BindingFlags.DeclaredOnly);
|
|
foreach (var field in typeFields)
|
{
|
// 排除编译器生成的字段
|
if (!field.Name.Contains("<") && !field.Name.Contains(">"))
|
{
|
fields.Add(field);
|
}
|
}
|
|
return fields;
|
}
|
|
private void DrawPacketFields()
|
{
|
var fields = GetAllFields(currentPacketType);
|
|
foreach (var field in fields)
|
{
|
GUILayout.BeginVertical("box");
|
|
// 字段名称和类型
|
GUILayout.BeginHorizontal();
|
GUILayout.Label(field.Name, EditorStyles.boldLabel, GUILayout.Width(150));
|
GUILayout.Label($"({field.FieldType.Name})", EditorStyles.miniLabel);
|
GUILayout.EndHorizontal();
|
|
// 字段值编辑
|
if (!fieldValues.ContainsKey(field))
|
{
|
fieldValues[field] = field.GetValue(currentPacketInstance);
|
}
|
|
object newValue = DrawFieldValue(field, fieldValues[field]);
|
if (newValue != null || field.FieldType.IsClass)
|
{
|
fieldValues[field] = newValue;
|
field.SetValue(currentPacketInstance, newValue);
|
}
|
|
GUILayout.EndVertical();
|
GUILayout.Space(3);
|
}
|
}
|
|
private object DrawFieldValue(FieldInfo field, object currentValue)
|
{
|
System.Type fieldType = field.FieldType;
|
|
try
|
{
|
// 基本类型
|
if (fieldType == typeof(int))
|
{
|
return EditorGUILayout.IntField((int)(currentValue ?? 0));
|
}
|
else if (fieldType == typeof(uint))
|
{
|
return (uint)EditorGUILayout.LongField((uint)(currentValue ?? 0));
|
}
|
else if (fieldType == typeof(long))
|
{
|
return EditorGUILayout.LongField((long)(currentValue ?? 0L));
|
}
|
else if (fieldType == typeof(ulong))
|
{
|
long val = EditorGUILayout.LongField((long)(ulong)(currentValue ?? 0UL));
|
return (ulong)System.Math.Max(0, val);
|
}
|
else if (fieldType == typeof(short))
|
{
|
return (short)EditorGUILayout.IntField((short)(currentValue ?? (short)0));
|
}
|
else if (fieldType == typeof(ushort))
|
{
|
return (ushort)EditorGUILayout.IntField((ushort)(currentValue ?? (ushort)0));
|
}
|
else if (fieldType == typeof(byte))
|
{
|
return (byte)EditorGUILayout.IntField((byte)(currentValue ?? (byte)0));
|
}
|
else if (fieldType == typeof(sbyte))
|
{
|
return (sbyte)EditorGUILayout.IntField((sbyte)(currentValue ?? (sbyte)0));
|
}
|
else if (fieldType == typeof(float))
|
{
|
return EditorGUILayout.FloatField((float)(currentValue ?? 0f));
|
}
|
else if (fieldType == typeof(double))
|
{
|
return EditorGUILayout.DoubleField((double)(currentValue ?? 0.0));
|
}
|
else if (fieldType == typeof(bool))
|
{
|
return EditorGUILayout.Toggle((bool)(currentValue ?? false));
|
}
|
else if (fieldType == typeof(string))
|
{
|
return EditorGUILayout.TextField((string)(currentValue ?? ""));
|
}
|
// List 类型
|
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
|
{
|
System.Type elementType = fieldType.GetGenericArguments()[0];
|
var list = currentValue as System.Collections.IList;
|
|
if (list == null)
|
{
|
list = (System.Collections.IList)System.Activator.CreateInstance(fieldType);
|
}
|
|
GUILayout.BeginHorizontal();
|
GUILayout.Label($"Count: {list.Count}", GUILayout.Width(80));
|
|
if (GUILayout.Button("➕", GUILayout.Width(30)))
|
{
|
object newElement = GetDefaultValue(elementType);
|
list.Add(newElement);
|
}
|
|
if (list.Count > 0 && GUILayout.Button("➖", GUILayout.Width(30)))
|
{
|
list.RemoveAt(list.Count - 1);
|
}
|
GUILayout.EndHorizontal();
|
|
return list;
|
}
|
// 数组类型
|
else if (fieldType.IsArray)
|
{
|
var array = currentValue as System.Array;
|
int length = array != null ? array.Length : 0;
|
GUILayout.Label($"Array Length: {length}", EditorStyles.miniLabel);
|
return currentValue;
|
}
|
// 枚举类型
|
else if (fieldType.IsEnum)
|
{
|
return EditorGUILayout.EnumPopup((System.Enum)(currentValue ?? System.Enum.GetValues(fieldType).GetValue(0)));
|
}
|
// 其他复杂类型
|
else
|
{
|
GUILayout.Label("(复杂类型,请在代码中设置)", EditorStyles.miniLabel);
|
return currentValue;
|
}
|
}
|
catch (System.Exception e)
|
{
|
GUILayout.Label($"(编辑错误: {e.Message})", EditorStyles.miniLabel);
|
return currentValue;
|
}
|
}
|
|
private object GetDefaultValue(System.Type type)
|
{
|
if (type.IsValueType)
|
{
|
return System.Activator.CreateInstance(type);
|
}
|
else if (type == typeof(string))
|
{
|
return "";
|
}
|
else
|
{
|
try
|
{
|
return System.Activator.CreateInstance(type);
|
}
|
catch
|
{
|
return null;
|
}
|
}
|
}
|
|
private void SendPacket()
|
{
|
if (currentPacketInstance == null)
|
{
|
Debug.LogWarning("没有可发送的包实例");
|
return;
|
}
|
|
if (!Application.isPlaying)
|
{
|
Debug.LogError("只能在运行时发送包");
|
EditorUtility.DisplayDialog("错误", "只能在运行时发送网络包!", "确定");
|
return;
|
}
|
|
try
|
{
|
var packet = currentPacketInstance as GameNetPackBasic;
|
if (packet != null)
|
{
|
GameNetSystem.Instance.SendInfo(packet);
|
Debug.Log($"✅ 成功发送包: {currentPacketType.Name}");
|
|
// 显示发送的详细信息
|
var fields = GetAllFields(currentPacketType);
|
string fieldInfo = "字段值:\n";
|
foreach (var field in fields)
|
{
|
object value = field.GetValue(packet);
|
fieldInfo += $" {field.Name} = {value}\n";
|
}
|
Debug.Log(fieldInfo);
|
}
|
else
|
{
|
Debug.LogError("包实例不是 GameNetPackBasic 类型");
|
}
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError($"发送包失败: {e.Message}\n{e.StackTrace}");
|
EditorUtility.DisplayDialog("发送失败", $"发送包时出错:\n{e.Message}", "确定");
|
}
|
}
|
|
private void ClearPacketFields()
|
{
|
if (currentPacketInstance == null)
|
{
|
return;
|
}
|
|
try
|
{
|
// 重新创建实例
|
currentPacketInstance = System.Activator.CreateInstance(currentPacketType);
|
fieldValues.Clear();
|
|
// 重新初始化字段值
|
var fields = GetAllFields(currentPacketType);
|
foreach (var field in fields)
|
{
|
object defaultValue = field.GetValue(currentPacketInstance);
|
fieldValues[field] = defaultValue;
|
}
|
|
Debug.Log("已清空所有字段");
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogError($"清空字段失败: {e.Message}");
|
}
|
}
|
}
|