1586 lines
62 KiB
C#
1586 lines
62 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using OfficeOpenXml;
|
||
using ArtResource;
|
||
using Debug = UnityEngine.Debug;
|
||
|
||
namespace DesignTools.Scene
|
||
{
|
||
/// <summary>
|
||
/// 场景进度奖励配置Editor工具
|
||
/// 读取Docs/config/IndoorProgress.xlsx和Item.xlsx
|
||
/// </summary>
|
||
public class IndoorProgressEditor : EditorWindow
|
||
{
|
||
private const string ART_SO_PATH = "Assets/Art_SubModule/Art_SO";
|
||
private const string INDOOR_PROGRESS_EXCEL_NAME = "IndoorProgress.xlsx";
|
||
private const string ITEM_EXCEL_NAME = "Item.xlsx";
|
||
private const string INDOOR_PROGRESS_SHEET_NAME = "IndoorProgress";
|
||
private const string ITEM_SHEET_NAME = "Item";
|
||
private const string DOCS_PATH_PREF_KEY = "IndoorProgressEditor_DocsPath";
|
||
private const string DESIGN_SUBMODULE_PATH = "Assets/Design_SubModule";
|
||
|
||
// IType类型定义(从ItemConfigEditor复制)
|
||
private static readonly Dictionary<int, string> ITEM_TYPES = new Dictionary<int, string>
|
||
{
|
||
{ 1, "能量" },
|
||
{ 2, "星星" },
|
||
{ 3, "钻石" },
|
||
{ 97, "Playroom宠物道具" },
|
||
{ 98, "卡牌" },
|
||
{ 99, "背包道具" },
|
||
{ 100, "棋子" },
|
||
{ 101, "卡包" },
|
||
{ 102, "限时事件" },
|
||
{ 103, "小猪存钱罐" },
|
||
{ 104, "万能卡" },
|
||
{ 105, "头像框" },
|
||
{ 106, "活动代币" },
|
||
{ 107, "竞赛游戏代币" },
|
||
{ 108, "Pet Playroom拜访道具" },
|
||
{ 109, "表情" },
|
||
{ 110, "头像" },
|
||
{ 111, "Playroom装饰" },
|
||
{ 112, "Playroom服装" },
|
||
{ 113, "Playroom装饰套装" },
|
||
{ 114, "Playroom服装套装" },
|
||
{ 115, "Playroom道具宝箱" },
|
||
{ 116, "活动通行证代币道具" }
|
||
};
|
||
|
||
// 特殊Item的映射关系
|
||
private static readonly Dictionary<int, string> MY_ITEM_DICT = new Dictionary<int, string>()
|
||
{
|
||
[100001] = "Energy",
|
||
[100002] = "Star",
|
||
[100003] = "Diamond",
|
||
[100004] = "Cardpack1",
|
||
[100005] = "Cardpack2",
|
||
[100006] = "Cardpack3",
|
||
[100007] = "Cardpack4",
|
||
[100008] = "Cardpack5",
|
||
[100011] = "LimitEvent4",
|
||
[100012] = "LimitEvent2",
|
||
[100013] = "LimitEvent3",
|
||
[100014] = "LimitEvent1",
|
||
[100015] = "LimitEvent5",
|
||
[100016] = "LimitEvent6",
|
||
[100017] = "LimitEvent7",
|
||
[100018] = "LimitEvent9",
|
||
[100019] = "LimitEvent10",
|
||
[100020] = "LimitEvent8",
|
||
[100022] = "PurplePig",
|
||
[101149] = "LimitEvent2",
|
||
[101150] = "LimitEvent3",
|
||
[101151] = "LimitEvent4",
|
||
[1021] = "705",
|
||
[1006] = "702",
|
||
[1007] = "703",
|
||
[100035] = "Battery",
|
||
[101445] = "BagItemBox1",
|
||
[101446] = "BagItemBox2",
|
||
[101447] = "BagItemBox3",
|
||
};
|
||
|
||
private string docsRootPath = "";
|
||
private List<IndoorProgressData> allProgressDataList = new List<IndoorProgressData>();
|
||
private List<IndoorProgressData> filteredProgressDataList = new List<IndoorProgressData>();
|
||
private Dictionary<int, ItemData> itemDict = new Dictionary<int, ItemData>(); // Id -> ItemData
|
||
private List<ArtTableSO> allArtTables = new List<ArtTableSO>();
|
||
|
||
private Vector2 scrollPosition;
|
||
private bool isDataLoaded = false;
|
||
private int selectedScene = -1;
|
||
private string sceneInputText = "";
|
||
|
||
// 道具奖励临时编辑状态(按数据ID存储)
|
||
private Dictionary<int, int> tempItemRewardTypeIndex = new Dictionary<int, int>();
|
||
|
||
// 区域奖励临时编辑状态(按数据ID存储)
|
||
private Dictionary<int, int> tempAreaRewardTypeIndex = new Dictionary<int, int>();
|
||
private Dictionary<int, int> tempAreaRewardItemId = new Dictionary<int, int>();
|
||
private Dictionary<int, int> tempAreaRewardNum = new Dictionary<int, int>();
|
||
|
||
[MenuItem("策划工具/场景/场景进度奖励")]
|
||
public static void ShowWindow()
|
||
{
|
||
var window = GetWindow<IndoorProgressEditor>("场景进度奖励配置");
|
||
window.minSize = new Vector2(1400, 700);
|
||
window.Show();
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
// 读取上次保存的路径
|
||
if (EditorPrefs.HasKey(DOCS_PATH_PREF_KEY))
|
||
{
|
||
docsRootPath = EditorPrefs.GetString(DOCS_PATH_PREF_KEY);
|
||
}
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||
EditorGUILayout.LabelField("场景进度奖励配置工具", EditorStyles.boldLabel);
|
||
GUILayout.FlexibleSpace();
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space(5);
|
||
|
||
// Docs路径选择
|
||
EditorGUILayout.BeginVertical("box");
|
||
EditorGUILayout.LabelField("Docs项目根目录", EditorStyles.boldLabel);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
docsRootPath = EditorGUILayout.TextField("路径", docsRootPath);
|
||
if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
|
||
{
|
||
string selectedPath = EditorUtility.OpenFolderPanel("选择Docs项目根目录", "", "");
|
||
if (!string.IsNullOrEmpty(selectedPath))
|
||
{
|
||
docsRootPath = selectedPath;
|
||
EditorPrefs.SetString(DOCS_PATH_PREF_KEY, docsRootPath);
|
||
}
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
if (GUILayout.Button("加载配置数据", GUILayout.Height(30)))
|
||
{
|
||
LoadData();
|
||
}
|
||
|
||
EditorGUILayout.EndVertical();
|
||
|
||
EditorGUILayout.Space(5);
|
||
|
||
// 数据编辑区域
|
||
if (isDataLoaded)
|
||
{
|
||
DrawSceneFilterAndDataEditor();
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox("请先选择Docs根目录并加载配置数据", MessageType.Info);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载配置数据
|
||
/// </summary>
|
||
private void LoadData()
|
||
{
|
||
if (string.IsNullOrEmpty(docsRootPath))
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "请先选择Docs根目录", "确定");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 检查并更新Docs仓库
|
||
if (!CheckAndUpdateDocsRepository())
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 检查并切换Design_SubModule到main分支
|
||
if (!CheckAndSwitchDesignSubModuleBranch())
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 加载所有Art_SO资源
|
||
LoadAllArtTableSO();
|
||
|
||
// 加载Item数据
|
||
LoadItemData();
|
||
|
||
// 加载IndoorProgress数据
|
||
LoadIndoorProgressData();
|
||
|
||
isDataLoaded = true;
|
||
selectedScene = -1;
|
||
sceneInputText = "";
|
||
filteredProgressDataList.Clear();
|
||
|
||
EditorUtility.DisplayDialog("成功", "配置数据加载完成!", "确定");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"加载数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||
isDataLoaded = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行Git命令
|
||
/// </summary>
|
||
private string ExecuteGitCommand(string workingDirectory, string arguments, out bool success)
|
||
{
|
||
try
|
||
{
|
||
var processInfo = new ProcessStartInfo
|
||
{
|
||
FileName = "git",
|
||
Arguments = arguments,
|
||
WorkingDirectory = workingDirectory,
|
||
RedirectStandardOutput = true,
|
||
RedirectStandardError = true,
|
||
UseShellExecute = false,
|
||
CreateNoWindow = true
|
||
};
|
||
|
||
using (var process = Process.Start(processInfo))
|
||
{
|
||
string output = process.StandardOutput.ReadToEnd();
|
||
string error = process.StandardError.ReadToEnd();
|
||
process.WaitForExit();
|
||
|
||
success = process.ExitCode == 0;
|
||
return success ? output : error;
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
success = false;
|
||
return $"执行Git命令失败: {e.Message}";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查并更新Docs仓库
|
||
/// </summary>
|
||
private bool CheckAndUpdateDocsRepository()
|
||
{
|
||
string gitPath = Path.Combine(docsRootPath, ".git");
|
||
if (!Directory.Exists(gitPath))
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"所选目录不是Git仓库:{docsRootPath}\n请确保选择的是Docs项目的根目录。",
|
||
"确定");
|
||
return false;
|
||
}
|
||
|
||
string fetchResult = ExecuteGitCommand(docsRootPath, "fetch", out bool fetchSuccess);
|
||
|
||
if (!fetchSuccess)
|
||
{
|
||
bool continueAnyway = EditorUtility.DisplayDialog("警告",
|
||
$"Git fetch失败:{fetchResult}\n\n是否继续?",
|
||
"继续", "取消");
|
||
return continueAnyway;
|
||
}
|
||
|
||
string statusResult = ExecuteGitCommand(docsRootPath, "status", out bool statusSuccess);
|
||
|
||
if (!statusSuccess)
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"Git status检查失败:{statusResult}",
|
||
"确定");
|
||
return false;
|
||
}
|
||
|
||
if (statusResult.Contains("Changes not staged") || statusResult.Contains("Changes to be committed"))
|
||
{
|
||
bool proceed = EditorUtility.DisplayDialog("提示",
|
||
"Docs仓库有未提交的修改。\n\n建议先提交或暂存这些修改。\n\n是否继续?",
|
||
"继续", "取消");
|
||
|
||
if (!proceed)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (statusResult.Contains("Your branch is behind"))
|
||
{
|
||
bool doPull = EditorUtility.DisplayDialog("提示",
|
||
"Docs仓库有远程更新。\n\n是否执行git pull拉取最新代码?\n(建议执行以获取最新配置)",
|
||
"执行Pull", "跳过");
|
||
|
||
if (doPull)
|
||
{
|
||
string pullResult = ExecuteGitCommand(docsRootPath, "pull", out bool pullSuccess);
|
||
|
||
if (!pullSuccess)
|
||
{
|
||
if (pullResult.Contains("CONFLICT") || pullResult.Contains("conflict"))
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"Git pull遇到冲突:\n{pullResult}\n\n请先在SourceTree或其他Git工具中解决冲突。",
|
||
"确定");
|
||
}
|
||
else
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"Git pull失败:{pullResult}",
|
||
"确定");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
EditorUtility.DisplayDialog("成功", "已拉取最新代码!", "确定");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.Log("Docs仓库已是最新");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查并切换Design_SubModule到main分支
|
||
/// </summary>
|
||
private bool CheckAndSwitchDesignSubModuleBranch()
|
||
{
|
||
string designSubModulePath = Path.Combine(Application.dataPath.Replace("/Assets", ""), DESIGN_SUBMODULE_PATH);
|
||
designSubModulePath = designSubModulePath.Replace("\\", "/");
|
||
|
||
if (!Directory.Exists(designSubModulePath))
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"Design_SubModule目录不存在:{designSubModulePath}",
|
||
"确定");
|
||
return false;
|
||
}
|
||
|
||
string branchResult = ExecuteGitCommand(designSubModulePath, "rev-parse --abbrev-ref HEAD", out bool branchSuccess);
|
||
|
||
if (!branchSuccess)
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"无法获取Design_SubModule当前分支:{branchResult}",
|
||
"确定");
|
||
return false;
|
||
}
|
||
|
||
string currentBranch = branchResult.Trim();
|
||
|
||
if (currentBranch != "main")
|
||
{
|
||
bool doCheckout = EditorUtility.DisplayDialog("提示",
|
||
$"Design_SubModule当前在 '{currentBranch}' 分支。\n\n是否切换到main分支?",
|
||
"切换到main", "取消");
|
||
|
||
if (doCheckout)
|
||
{
|
||
string checkoutResult = ExecuteGitCommand(designSubModulePath, "checkout main", out bool checkoutSuccess);
|
||
|
||
if (!checkoutSuccess)
|
||
{
|
||
EditorUtility.DisplayDialog("错误",
|
||
$"切换到main分支失败:{checkoutResult}",
|
||
"确定");
|
||
return false;
|
||
}
|
||
|
||
EditorUtility.DisplayDialog("成功", "已切换到main分支!", "确定");
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.Log("Design_SubModule已在main分支");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载所有Art_SO资源
|
||
/// </summary>
|
||
private void LoadAllArtTableSO()
|
||
{
|
||
allArtTables.Clear();
|
||
|
||
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO", new[] { ART_SO_PATH });
|
||
foreach (string guid in guids)
|
||
{
|
||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||
ArtTableSO artTable = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
|
||
if (artTable != null)
|
||
{
|
||
allArtTables.Add(artTable);
|
||
}
|
||
}
|
||
|
||
Debug.Log($"加载了 {allArtTables.Count} 个ArtTableSO资源");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载Item数据
|
||
/// </summary>
|
||
private void LoadItemData()
|
||
{
|
||
itemDict.Clear();
|
||
|
||
string itemExcelPath = Path.Combine(docsRootPath, "config", ITEM_EXCEL_NAME);
|
||
if (!File.Exists(itemExcelPath))
|
||
{
|
||
throw new Exception($"未找到Item配置文件: {itemExcelPath}");
|
||
}
|
||
|
||
using (var package = new ExcelPackage(new FileInfo(itemExcelPath)))
|
||
{
|
||
var worksheet = package.Workbook.Worksheets[ITEM_SHEET_NAME];
|
||
if (worksheet == null)
|
||
{
|
||
throw new Exception($"Item.xlsx中未找到Sheet: {ITEM_SHEET_NAME}");
|
||
}
|
||
|
||
// 读取表头(第1行)
|
||
int idCol = -1, nameCol = -1, iTypeCol = -1, resCol = -1;
|
||
int columnCount = worksheet.Dimension.Columns;
|
||
|
||
for (int col = 1; col <= columnCount; col++)
|
||
{
|
||
string header = worksheet.Cells[1, col].Text;
|
||
if (header == "Id") idCol = col;
|
||
else if (header == "Name") nameCol = col;
|
||
else if (header == "IType") iTypeCol = col;
|
||
else if (header == "Res") resCol = col;
|
||
}
|
||
|
||
if (idCol < 0 || nameCol < 0 || iTypeCol < 0)
|
||
{
|
||
throw new Exception("Item.xlsx表结构不正确,缺少必要列(Id/Name/IType)");
|
||
}
|
||
|
||
// 读取数据(从第3行开始)
|
||
int rowCount = worksheet.Dimension.Rows;
|
||
for (int row = 3; row <= rowCount; row++)
|
||
{
|
||
string idText = worksheet.Cells[row, idCol].Text;
|
||
if (string.IsNullOrEmpty(idText)) continue;
|
||
|
||
if (!int.TryParse(idText, out int id)) continue;
|
||
|
||
string name = worksheet.Cells[row, nameCol].Text;
|
||
string iTypeText = worksheet.Cells[row, iTypeCol].Text;
|
||
string res = resCol > 0 ? worksheet.Cells[row, resCol].Text : "";
|
||
|
||
if (!int.TryParse(iTypeText, out int iType)) continue;
|
||
|
||
itemDict[id] = new ItemData
|
||
{
|
||
Id = id,
|
||
Name = name,
|
||
IType = iType,
|
||
Res = res
|
||
};
|
||
}
|
||
}
|
||
|
||
Debug.Log($"加载了 {itemDict.Count} 个Item数据");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载IndoorProgress数据
|
||
/// </summary>
|
||
private void LoadIndoorProgressData()
|
||
{
|
||
allProgressDataList.Clear();
|
||
|
||
string progressExcelPath = Path.Combine(docsRootPath, "config", INDOOR_PROGRESS_EXCEL_NAME);
|
||
if (!File.Exists(progressExcelPath))
|
||
{
|
||
throw new Exception($"未找到IndoorProgress配置文件: {progressExcelPath}");
|
||
}
|
||
|
||
using (var package = new ExcelPackage(new FileInfo(progressExcelPath)))
|
||
{
|
||
var worksheet = package.Workbook.Worksheets[INDOOR_PROGRESS_SHEET_NAME];
|
||
if (worksheet == null)
|
||
{
|
||
throw new Exception($"IndoorProgress.xlsx中未找到Sheet: {INDOOR_PROGRESS_SHEET_NAME}");
|
||
}
|
||
|
||
// 读取表头(第1行)
|
||
int idCol = -1, sceneCol = -1, lvCol = -1, itemCol = -1, emitCol = -1,
|
||
rewardCol = -1, bigRewardCol = -1, areaRewardCol = -1, partCol = -1;
|
||
int columnCount = worksheet.Dimension.Columns;
|
||
|
||
for (int col = 1; col <= columnCount; col++)
|
||
{
|
||
string header = worksheet.Cells[1, col].Text;
|
||
if (header == "Id") idCol = col;
|
||
else if (header == "Scene") sceneCol = col;
|
||
else if (header == "Lv") lvCol = col;
|
||
else if (header == "Item") itemCol = col;
|
||
else if (header == "Emit") emitCol = col;
|
||
else if (header == "Reward") rewardCol = col;
|
||
else if (header == "BigReward") bigRewardCol = col;
|
||
else if (header == "AreaReward") areaRewardCol = col;
|
||
else if (header == "Part") partCol = col;
|
||
}
|
||
|
||
if (idCol < 0 || sceneCol < 0 || lvCol < 0)
|
||
{
|
||
throw new Exception("IndoorProgress.xlsx表结构不正确,缺少必要列(Id/Scene/Lv)");
|
||
}
|
||
|
||
// 读取数据(从第3行开始)
|
||
int rowCount = worksheet.Dimension.Rows;
|
||
for (int row = 3; row <= rowCount; row++)
|
||
{
|
||
string idText = worksheet.Cells[row, idCol].Text;
|
||
if (string.IsNullOrEmpty(idText)) continue;
|
||
|
||
if (!int.TryParse(idText, out int id)) continue;
|
||
|
||
string sceneText = worksheet.Cells[row, sceneCol].Text;
|
||
string lvText = worksheet.Cells[row, lvCol].Text;
|
||
|
||
if (!int.TryParse(sceneText, out int scene)) continue;
|
||
if (!int.TryParse(lvText, out int lv)) continue;
|
||
|
||
string item = itemCol > 0 ? worksheet.Cells[row, itemCol].Text : "";
|
||
string emit = emitCol > 0 ? worksheet.Cells[row, emitCol].Text : "";
|
||
string reward = rewardCol > 0 ? worksheet.Cells[row, rewardCol].Text : "";
|
||
string bigReward = bigRewardCol > 0 ? worksheet.Cells[row, bigRewardCol].Text : "";
|
||
string areaReward = areaRewardCol > 0 ? worksheet.Cells[row, areaRewardCol].Text : "";
|
||
string partText = partCol > 0 ? worksheet.Cells[row, partCol].Text : "";
|
||
|
||
int.TryParse(partText, out int part);
|
||
|
||
var progressData = new IndoorProgressData
|
||
{
|
||
Id = id,
|
||
Scene = scene,
|
||
Lv = lv,
|
||
Item = item,
|
||
Emit = emit,
|
||
Reward = reward,
|
||
BigReward = bigReward,
|
||
AreaReward = areaReward,
|
||
Part = part
|
||
};
|
||
|
||
allProgressDataList.Add(progressData);
|
||
}
|
||
}
|
||
|
||
Debug.Log($"加载了 {allProgressDataList.Count} 条IndoorProgress数据");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制场景筛选和数据编辑器
|
||
/// </summary>
|
||
private void DrawSceneFilterAndDataEditor()
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
|
||
// 场景筛选
|
||
EditorGUILayout.LabelField("场景筛选", EditorStyles.boldLabel);
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
sceneInputText = EditorGUILayout.TextField("输入场景ID", sceneInputText, GUILayout.Width(200));
|
||
|
||
if (GUILayout.Button("确认加载场景", GUILayout.Width(120)))
|
||
{
|
||
if (int.TryParse(sceneInputText, out int sceneId))
|
||
{
|
||
selectedScene = sceneId;
|
||
FilterProgressByScene();
|
||
}
|
||
else
|
||
{
|
||
EditorUtility.DisplayDialog("错误", "请输入有效的场景ID(数字)", "确定");
|
||
}
|
||
}
|
||
|
||
if (selectedScene > 0)
|
||
{
|
||
EditorGUILayout.LabelField($"当前场景: {selectedScene}", EditorStyles.boldLabel, GUILayout.Width(150));
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
EditorGUILayout.EndVertical();
|
||
|
||
if (selectedScene > 0 && filteredProgressDataList.Count > 0)
|
||
{
|
||
EditorGUILayout.Space(10);
|
||
|
||
// 新增场景按钮
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("+ 新增场景", GUILayout.Height(30), GUILayout.Width(150)))
|
||
{
|
||
AddNewScene();
|
||
}
|
||
GUILayout.FlexibleSpace();
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
EditorGUILayout.Space(5);
|
||
|
||
// 数据列表
|
||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||
|
||
EditorGUILayout.BeginVertical("box");
|
||
DrawProgressDataList();
|
||
EditorGUILayout.EndVertical();
|
||
|
||
EditorGUILayout.EndScrollView();
|
||
|
||
EditorGUILayout.Space(10);
|
||
|
||
// 保存按钮
|
||
EditorGUILayout.BeginHorizontal();
|
||
GUILayout.FlexibleSpace();
|
||
if (GUILayout.Button("保存配置到Excel", GUILayout.Height(40), GUILayout.Width(200)))
|
||
{
|
||
SaveData();
|
||
}
|
||
GUILayout.FlexibleSpace();
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
else if (selectedScene > 0)
|
||
{
|
||
EditorGUILayout.Space(10);
|
||
EditorGUILayout.HelpBox($"场景 {selectedScene} 没有数据", MessageType.Warning);
|
||
|
||
// 新增场景按钮
|
||
EditorGUILayout.BeginHorizontal();
|
||
if (GUILayout.Button("+ 新增场景", GUILayout.Height(30), GUILayout.Width(150)))
|
||
{
|
||
AddNewScene();
|
||
}
|
||
GUILayout.FlexibleSpace();
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.Space(10);
|
||
EditorGUILayout.HelpBox("请输入场景ID并点击确认加载", MessageType.Info);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据场景筛选数据
|
||
/// </summary>
|
||
private void FilterProgressByScene()
|
||
{
|
||
filteredProgressDataList = allProgressDataList
|
||
.Where(x => x.Scene == selectedScene)
|
||
.OrderBy(x => x.Lv)
|
||
.ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制进度数据列表
|
||
/// </summary>
|
||
private void DrawProgressDataList()
|
||
{
|
||
// 表头
|
||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||
EditorGUILayout.LabelField("ID", EditorStyles.boldLabel, GUILayout.Width(40));
|
||
EditorGUILayout.LabelField("Lv", EditorStyles.boldLabel, GUILayout.Width(40));
|
||
EditorGUILayout.LabelField("道具奖励", EditorStyles.boldLabel, GUILayout.Width(450));
|
||
EditorGUILayout.LabelField("区域奖励", EditorStyles.boldLabel, GUILayout.Width(450));
|
||
EditorGUILayout.LabelField("零件", EditorStyles.boldLabel, GUILayout.Width(60));
|
||
EditorGUILayout.LabelField("操作", EditorStyles.boldLabel, GUILayout.Width(60));
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
// 数据行
|
||
for (int i = 0; i < filteredProgressDataList.Count; i++)
|
||
{
|
||
DrawProgressDataRow(filteredProgressDataList[i], i);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制单行进度数据
|
||
/// </summary>
|
||
private void DrawProgressDataRow(IndoorProgressData data, int index)
|
||
{
|
||
EditorGUILayout.BeginHorizontal("box");
|
||
|
||
// ID(不可编辑)
|
||
EditorGUILayout.LabelField(data.Id.ToString(), GUILayout.Width(40));
|
||
|
||
// Lv(可编辑)
|
||
EditorGUI.BeginChangeCheck();
|
||
int newLv = EditorGUILayout.IntField(data.Lv, GUILayout.Width(40));
|
||
if (EditorGUI.EndChangeCheck())
|
||
{
|
||
data.Lv = newLv;
|
||
}
|
||
|
||
// 道具奖励(Item/Emit/Reward一体)
|
||
EditorGUILayout.BeginVertical(GUILayout.Width(450));
|
||
DrawItemRewardEditor(data);
|
||
EditorGUILayout.EndVertical();
|
||
|
||
// 区域奖励(AreaReward/BigReward)
|
||
EditorGUILayout.BeginVertical(GUILayout.Width(450));
|
||
DrawAreaRewardEditor(data);
|
||
EditorGUILayout.EndVertical();
|
||
|
||
// 零件数量
|
||
EditorGUI.BeginChangeCheck();
|
||
int newPart = EditorGUILayout.IntField(data.Part, GUILayout.Width(60));
|
||
if (EditorGUI.EndChangeCheck())
|
||
{
|
||
data.Part = newPart;
|
||
}
|
||
|
||
// 操作按钮
|
||
if (GUILayout.Button("删除", GUILayout.Width(60)))
|
||
{
|
||
if (EditorUtility.DisplayDialog("确认删除",
|
||
$"确定要删除ID {data.Id} (场景{data.Scene}-Lv{data.Lv}) 的数据吗?",
|
||
"删除", "取消"))
|
||
{
|
||
filteredProgressDataList.RemoveAt(index);
|
||
allProgressDataList.Remove(data);
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制道具奖励编辑器(Item/Emit/Reward)
|
||
/// </summary>
|
||
private void DrawItemRewardEditor(IndoorProgressData data)
|
||
{
|
||
// 解析当前数据
|
||
var itemReward = ParseItemReward(data.Item);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
// 选择IType
|
||
var typeOptions = new List<string> { "无" };
|
||
var typeValues = new List<int> { -1 };
|
||
|
||
foreach (var kvp in ITEM_TYPES.OrderBy(x => x.Key))
|
||
{
|
||
typeOptions.Add($"{kvp.Value} ({kvp.Key})");
|
||
typeValues.Add(kvp.Key);
|
||
}
|
||
|
||
// 初始化临时状态:如果有数据就用数据的类型,如果没数据就用临时保存的选择
|
||
if (!tempItemRewardTypeIndex.ContainsKey(data.Id))
|
||
{
|
||
if (itemReward.HasValue)
|
||
{
|
||
int itemType = GetItemType(itemReward.Value.Id);
|
||
tempItemRewardTypeIndex[data.Id] = typeValues.IndexOf(itemType);
|
||
}
|
||
else
|
||
{
|
||
tempItemRewardTypeIndex[data.Id] = 0; // 默认"无"
|
||
}
|
||
}
|
||
else if (itemReward.HasValue)
|
||
{
|
||
// 如果有数据,同步临时状态
|
||
int itemType = GetItemType(itemReward.Value.Id);
|
||
int typeIndex = typeValues.IndexOf(itemType);
|
||
if (typeIndex >= 0)
|
||
{
|
||
tempItemRewardTypeIndex[data.Id] = typeIndex;
|
||
}
|
||
}
|
||
|
||
int currentTypeIndex = tempItemRewardTypeIndex[data.Id];
|
||
if (currentTypeIndex < 0) currentTypeIndex = 0;
|
||
|
||
EditorGUI.BeginChangeCheck();
|
||
int newTypeIndex = EditorGUILayout.Popup(currentTypeIndex, typeOptions.ToArray(), GUILayout.Width(120));
|
||
bool typeChanged = EditorGUI.EndChangeCheck();
|
||
|
||
if (typeChanged)
|
||
{
|
||
tempItemRewardTypeIndex[data.Id] = newTypeIndex;
|
||
}
|
||
|
||
if (newTypeIndex == 0)
|
||
{
|
||
// 选择"无"
|
||
if (itemReward.HasValue)
|
||
{
|
||
data.Item = "";
|
||
data.Emit = "";
|
||
data.Reward = "";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int selectedType = typeValues[newTypeIndex];
|
||
|
||
// 如果类型改变,且当前有数据但类型不匹配,清空数据
|
||
if (typeChanged && itemReward.HasValue && GetItemType(itemReward.Value.Id) != selectedType)
|
||
{
|
||
data.Item = "";
|
||
data.Emit = "";
|
||
data.Reward = "";
|
||
itemReward = null;
|
||
}
|
||
|
||
// 根据类型筛选Item
|
||
var itemsOfType = itemDict.Values.Where(x => x.IType == selectedType).ToList();
|
||
|
||
if (itemsOfType.Count > 0)
|
||
{
|
||
// IType=100(棋子)使用输入框+刷新按钮,其他使用下拉菜单
|
||
if (selectedType == 100)
|
||
{
|
||
// 输入ID
|
||
EditorGUILayout.LabelField("ID:", GUILayout.Width(25));
|
||
|
||
// 当前ID:如果类型改变,重置为0;否则使用现有值
|
||
int inputId = 0;
|
||
if (!typeChanged && itemReward.HasValue && GetItemType(itemReward.Value.Id) == 100)
|
||
{
|
||
inputId = itemReward.Value.Id;
|
||
}
|
||
|
||
EditorGUI.BeginChangeCheck();
|
||
inputId = EditorGUILayout.IntField(inputId, GUILayout.Width(60));
|
||
bool idChanged = EditorGUI.EndChangeCheck();
|
||
|
||
// 数量
|
||
EditorGUILayout.LabelField("数量:", GUILayout.Width(35));
|
||
int num = itemReward.HasValue ? itemReward.Value.Num : 1;
|
||
EditorGUI.BeginChangeCheck();
|
||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||
bool numChanged = EditorGUI.EndChangeCheck();
|
||
|
||
// 刷新按钮
|
||
if (GUILayout.Button("刷新", GUILayout.Width(50)))
|
||
{
|
||
if (inputId > 0 && itemDict.ContainsKey(inputId) && itemDict[inputId].IType == 100)
|
||
{
|
||
UpdateItemReward(data, inputId, num);
|
||
}
|
||
else
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"ID {inputId} 不存在或不是棋子类型", "确定");
|
||
}
|
||
}
|
||
|
||
// 如果ID或数量改变,自动更新
|
||
if (idChanged || numChanged)
|
||
{
|
||
if (inputId > 0 && itemDict.ContainsKey(inputId) && itemDict[inputId].IType == 100)
|
||
{
|
||
UpdateItemReward(data, inputId, num);
|
||
}
|
||
}
|
||
|
||
// 显示预览图
|
||
if (inputId > 0 && itemDict.TryGetValue(inputId, out var item) && item.IType == 100)
|
||
{
|
||
DrawItemPreview(item);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField("暂无预览", GUILayout.Width(60));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 其他类型使用下拉菜单
|
||
var itemOptions = itemsOfType.Select(x => $"{x.Name} ({x.Id})").ToList();
|
||
var itemIds = itemsOfType.Select(x => x.Id).ToList();
|
||
|
||
int currentItemIndex = 0;
|
||
if (!typeChanged && itemReward.HasValue)
|
||
{
|
||
currentItemIndex = itemIds.IndexOf(itemReward.Value.Id);
|
||
if (currentItemIndex < 0) currentItemIndex = 0;
|
||
}
|
||
|
||
EditorGUI.BeginChangeCheck();
|
||
int newItemIndex = EditorGUILayout.Popup(currentItemIndex, itemOptions.ToArray(), GUILayout.Width(180));
|
||
int selectedItemId = itemIds[newItemIndex];
|
||
|
||
// 数量
|
||
int num = itemReward.HasValue ? itemReward.Value.Num : 1;
|
||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||
|
||
// 更新数据
|
||
if (EditorGUI.EndChangeCheck() || typeChanged || !itemReward.HasValue)
|
||
{
|
||
UpdateItemReward(data, selectedItemId, num);
|
||
}
|
||
|
||
// 显示预览图
|
||
if (itemDict.TryGetValue(selectedItemId, out var item2))
|
||
{
|
||
DrawItemPreview(item2);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField("该类型无可用Item", GUILayout.Width(150));
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制区域奖励编辑器(AreaReward/BigReward)
|
||
/// </summary>
|
||
private void DrawAreaRewardEditor(IndoorProgressData data)
|
||
{
|
||
// 判断当前Lv是否可以配置AreaReward
|
||
bool canConfigArea = CanConfigAreaReward(data.Scene, data.Lv);
|
||
|
||
if (!canConfigArea)
|
||
{
|
||
// 不显示任何内容,保持空白
|
||
return;
|
||
}
|
||
|
||
// 解析当前AreaReward
|
||
var areaRewards = ParseAreaReward(data.AreaReward);
|
||
|
||
EditorGUILayout.BeginVertical();
|
||
|
||
// 显示现有奖励
|
||
for (int i = 0; i < areaRewards.Count; i++)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
var reward = areaRewards[i];
|
||
|
||
// 显示Item信息和图片
|
||
if (itemDict.TryGetValue(reward.Id, out var rewardItem))
|
||
{
|
||
EditorGUILayout.LabelField($"{rewardItem.Name}", GUILayout.Width(100));
|
||
|
||
// 显示预览图
|
||
DrawItemPreview(rewardItem);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField($"ID:{reward.Id}", GUILayout.Width(100));
|
||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||
}
|
||
|
||
// 数量
|
||
EditorGUILayout.LabelField("x", GUILayout.Width(10));
|
||
EditorGUI.BeginChangeCheck();
|
||
int newNum = EditorGUILayout.IntField(reward.Num, GUILayout.Width(40));
|
||
if (EditorGUI.EndChangeCheck())
|
||
{
|
||
areaRewards[i] = new ItemReward { Id = reward.Id, Num = newNum };
|
||
UpdateAreaReward(data, areaRewards);
|
||
}
|
||
|
||
// 删除按钮
|
||
if (GUILayout.Button("-", GUILayout.Width(30)))
|
||
{
|
||
areaRewards.RemoveAt(i);
|
||
UpdateAreaReward(data, areaRewards);
|
||
break;
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
// 添加新奖励UI
|
||
DrawAddAreaRewardUI(data, areaRewards);
|
||
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断是否可以配置AreaReward
|
||
/// </summary>
|
||
private bool CanConfigAreaReward(int scene, int lv)
|
||
{
|
||
if (scene == 1)
|
||
{
|
||
// 场景1: 20, 29, 36, 44
|
||
return lv == 20 || lv == 29 || lv == 36 || lv == 44;
|
||
}
|
||
else if (scene >= 2 && scene <= 5)
|
||
{
|
||
// 场景2-5: 20
|
||
return lv == 20;
|
||
}
|
||
else
|
||
{
|
||
// 其他场景: 25
|
||
return lv == 25;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制添加区域奖励UI
|
||
/// </summary>
|
||
private void DrawAddAreaRewardUI(IndoorProgressData data, List<ItemReward> areaRewards)
|
||
{
|
||
EditorGUILayout.BeginHorizontal();
|
||
|
||
// 选择IType
|
||
var typeOptions = new List<string> { "选择类型..." };
|
||
var typeValues = new List<int> { -1 };
|
||
|
||
foreach (var kvp in ITEM_TYPES.OrderBy(x => x.Key))
|
||
{
|
||
typeOptions.Add($"{kvp.Value} ({kvp.Key})");
|
||
typeValues.Add(kvp.Key);
|
||
}
|
||
|
||
// 使用临时变量存储当前选择
|
||
if (!tempAreaRewardTypeIndex.ContainsKey(data.Id))
|
||
{
|
||
tempAreaRewardTypeIndex[data.Id] = 0;
|
||
}
|
||
if (!tempAreaRewardItemId.ContainsKey(data.Id))
|
||
{
|
||
tempAreaRewardItemId[data.Id] = 0;
|
||
}
|
||
if (!tempAreaRewardNum.ContainsKey(data.Id))
|
||
{
|
||
tempAreaRewardNum[data.Id] = 1;
|
||
}
|
||
|
||
int currentTypeIndex = tempAreaRewardTypeIndex[data.Id];
|
||
|
||
EditorGUI.BeginChangeCheck();
|
||
int newTypeIndex = EditorGUILayout.Popup(currentTypeIndex, typeOptions.ToArray(), GUILayout.Width(120));
|
||
bool typeChanged = EditorGUI.EndChangeCheck();
|
||
|
||
if (typeChanged)
|
||
{
|
||
tempAreaRewardTypeIndex[data.Id] = newTypeIndex;
|
||
tempAreaRewardItemId[data.Id] = 0; // 重置ItemId
|
||
}
|
||
|
||
if (newTypeIndex > 0)
|
||
{
|
||
int selectedType = typeValues[newTypeIndex];
|
||
|
||
// 根据类型筛选Item
|
||
var itemsOfType = itemDict.Values.Where(x => x.IType == selectedType).ToList();
|
||
|
||
if (itemsOfType.Count > 0)
|
||
{
|
||
// IType=100(棋子)使用输入框+添加按钮
|
||
if (selectedType == 100)
|
||
{
|
||
EditorGUILayout.LabelField("ID:", GUILayout.Width(25));
|
||
int inputId = tempAreaRewardItemId[data.Id];
|
||
inputId = EditorGUILayout.IntField(inputId, GUILayout.Width(60));
|
||
tempAreaRewardItemId[data.Id] = inputId;
|
||
|
||
EditorGUILayout.LabelField("数量:", GUILayout.Width(35));
|
||
int num = tempAreaRewardNum[data.Id];
|
||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||
tempAreaRewardNum[data.Id] = num;
|
||
|
||
if (GUILayout.Button("添加", GUILayout.Width(50)))
|
||
{
|
||
if (itemDict.ContainsKey(inputId) && itemDict[inputId].IType == 100)
|
||
{
|
||
areaRewards.Add(new ItemReward { Id = inputId, Num = num });
|
||
UpdateAreaReward(data, areaRewards);
|
||
// 重置
|
||
tempAreaRewardTypeIndex[data.Id] = 0;
|
||
tempAreaRewardItemId[data.Id] = 0;
|
||
tempAreaRewardNum[data.Id] = 1;
|
||
}
|
||
else
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"ID {inputId} 不存在或不是棋子类型", "确定");
|
||
}
|
||
}
|
||
|
||
// 显示预览图
|
||
if (itemDict.TryGetValue(inputId, out var previewItem) && previewItem.IType == 100)
|
||
{
|
||
DrawItemPreview(previewItem);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField("暂无预览", GUILayout.Width(60));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 其他类型使用下拉菜单
|
||
var itemOptions = itemsOfType.Select(x => $"{x.Name} ({x.Id})").ToList();
|
||
var itemIds = itemsOfType.Select(x => x.Id).ToList();
|
||
|
||
int savedItemId = tempAreaRewardItemId[data.Id];
|
||
int currentItemIndex = itemIds.IndexOf(savedItemId);
|
||
if (currentItemIndex < 0) currentItemIndex = 0;
|
||
|
||
EditorGUI.BeginChangeCheck();
|
||
int newItemIndex = EditorGUILayout.Popup(currentItemIndex, itemOptions.ToArray(), GUILayout.Width(150));
|
||
if (EditorGUI.EndChangeCheck())
|
||
{
|
||
tempAreaRewardItemId[data.Id] = itemIds[newItemIndex];
|
||
}
|
||
|
||
int selectedItemId = itemIds[newItemIndex];
|
||
|
||
// 数量
|
||
int num = tempAreaRewardNum[data.Id];
|
||
num = EditorGUILayout.IntField(num, GUILayout.Width(40));
|
||
tempAreaRewardNum[data.Id] = num;
|
||
|
||
if (GUILayout.Button("添加", GUILayout.Width(50)))
|
||
{
|
||
areaRewards.Add(new ItemReward { Id = selectedItemId, Num = num });
|
||
UpdateAreaReward(data, areaRewards);
|
||
// 重置
|
||
tempAreaRewardTypeIndex[data.Id] = 0;
|
||
tempAreaRewardItemId[data.Id] = 0;
|
||
tempAreaRewardNum[data.Id] = 1;
|
||
}
|
||
|
||
// 显示预览图
|
||
if (itemDict.TryGetValue(selectedItemId, out var previewItem2))
|
||
{
|
||
DrawItemPreview(previewItem2);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.LabelField("该类型无可用Item", GUILayout.Width(120));
|
||
}
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析Item奖励
|
||
/// </summary>
|
||
private ItemReward? ParseItemReward(string itemJson)
|
||
{
|
||
if (string.IsNullOrEmpty(itemJson)) return null;
|
||
|
||
try
|
||
{
|
||
// 格式: [{"Id":83,"Num":1}]
|
||
itemJson = itemJson.Trim();
|
||
if (!itemJson.StartsWith("[") || !itemJson.EndsWith("]"))
|
||
return null;
|
||
|
||
itemJson = itemJson.Substring(1, itemJson.Length - 2); // 去掉[]
|
||
|
||
// 简单解析
|
||
var idMatch = System.Text.RegularExpressions.Regex.Match(itemJson, @"""Id"":(\d+)");
|
||
var numMatch = System.Text.RegularExpressions.Regex.Match(itemJson, @"""Num"":(\d+)");
|
||
|
||
if (idMatch.Success && numMatch.Success)
|
||
{
|
||
return new ItemReward
|
||
{
|
||
Id = int.Parse(idMatch.Groups[1].Value),
|
||
Num = int.Parse(numMatch.Groups[1].Value)
|
||
};
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析区域奖励
|
||
/// </summary>
|
||
private List<ItemReward> ParseAreaReward(string areaRewardJson)
|
||
{
|
||
var result = new List<ItemReward>();
|
||
|
||
if (string.IsNullOrEmpty(areaRewardJson)) return result;
|
||
|
||
try
|
||
{
|
||
// 格式: [{"Id":100001,"Num":25}] 或 [{"Id":100001,"Num":50},{"Id":100021,"Num":1}]
|
||
var matches = System.Text.RegularExpressions.Regex.Matches(areaRewardJson, @"\{""Id"":(\d+),""Num"":(\d+)\}");
|
||
|
||
foreach (System.Text.RegularExpressions.Match match in matches)
|
||
{
|
||
result.Add(new ItemReward
|
||
{
|
||
Id = int.Parse(match.Groups[1].Value),
|
||
Num = int.Parse(match.Groups[2].Value)
|
||
});
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取Item的类型
|
||
/// </summary>
|
||
private int GetItemType(int itemId)
|
||
{
|
||
if (itemDict.TryGetValue(itemId, out var item))
|
||
{
|
||
return item.IType;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新Item奖励
|
||
/// </summary>
|
||
private void UpdateItemReward(IndoorProgressData data, int itemId, int num)
|
||
{
|
||
// Item格式: [{"Id":83,"Num":1}]
|
||
data.Item = $"[{{\"Id\":{itemId},\"Num\":{num}}}]";
|
||
|
||
// Emit格式: 83
|
||
data.Emit = itemId.ToString();
|
||
|
||
// Reward格式: 83=1
|
||
data.Reward = $"{itemId}={num}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新区域奖励
|
||
/// </summary>
|
||
private void UpdateAreaReward(IndoorProgressData data, List<ItemReward> areaRewards)
|
||
{
|
||
if (areaRewards.Count == 0)
|
||
{
|
||
data.AreaReward = "";
|
||
data.BigReward = "";
|
||
|
||
// 如果这是最后一个AreaReward位置,需要清空Lv=1的BigReward
|
||
if (IsLastAreaRewardPosition(data.Scene, data.Lv))
|
||
{
|
||
var lv1Data = allProgressDataList.FirstOrDefault(x => x.Scene == data.Scene && x.Lv == 1);
|
||
if (lv1Data != null)
|
||
{
|
||
lv1Data.BigReward = "";
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
// AreaReward格式: [{"Id":100001,"Num":25}] 或 [{"Id":100001,"Num":50},{"Id":100021,"Num":1}]
|
||
var areaRewardItems = areaRewards.Select(r => $"{{\"Id\":{r.Id},\"Num\":{r.Num}}}");
|
||
data.AreaReward = $"[{string.Join(",", areaRewardItems)}]";
|
||
|
||
// 当前行的BigReward清空(只有Lv=1才有BigReward)
|
||
data.BigReward = "";
|
||
|
||
// 如果这是最后一个AreaReward位置,更新Lv=1的BigReward
|
||
if (IsLastAreaRewardPosition(data.Scene, data.Lv))
|
||
{
|
||
var lv1Data = allProgressDataList.FirstOrDefault(x => x.Scene == data.Scene && x.Lv == 1);
|
||
if (lv1Data != null)
|
||
{
|
||
// BigReward格式: Energy=25 或 Energy=50,PurplePig=1,101451=1
|
||
var bigRewardItems = new List<string>();
|
||
bool hasUnmappedItem = false;
|
||
|
||
foreach (var reward in areaRewards)
|
||
{
|
||
if (MY_ITEM_DICT.TryGetValue(reward.Id, out string mappedName))
|
||
{
|
||
bigRewardItems.Add($"{mappedName}={reward.Num}");
|
||
}
|
||
else
|
||
{
|
||
bigRewardItems.Add($"{reward.Id}={reward.Num}");
|
||
hasUnmappedItem = true;
|
||
}
|
||
}
|
||
|
||
lv1Data.BigReward = string.Join(",", bigRewardItems);
|
||
|
||
// 如果有未映射的Item,显示警告
|
||
if (hasUnmappedItem)
|
||
{
|
||
Debug.LogWarning($"场景{data.Scene}的AreaReward包含未映射的ItemId,可能会出问题!");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断当前Lv是否为最后一个AreaReward位置
|
||
/// </summary>
|
||
private bool IsLastAreaRewardPosition(int scene, int lv)
|
||
{
|
||
if (scene == 1)
|
||
{
|
||
// 场景1的最后一个是44
|
||
return lv == 44;
|
||
}
|
||
else if (scene >= 2 && scene <= 5)
|
||
{
|
||
// 场景2-5的最后一个是20
|
||
return lv == 20;
|
||
}
|
||
else
|
||
{
|
||
// 其他场景的最后一个是25
|
||
return lv == 25;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制Item预览
|
||
/// </summary>
|
||
private void DrawItemPreview(ItemData item)
|
||
{
|
||
if (string.IsNullOrEmpty(item.Res))
|
||
{
|
||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||
return;
|
||
}
|
||
|
||
// 解析Res格式: "tableId,itemId"
|
||
var parts = item.Res.Split(',');
|
||
if (parts.Length != 2)
|
||
{
|
||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||
return;
|
||
}
|
||
|
||
if (!int.TryParse(parts[0], out int tableId)) return;
|
||
if (!int.TryParse(parts[1], out int itemId)) return;
|
||
|
||
// 查找对应的ArtTableSO
|
||
var artTable = allArtTables.FirstOrDefault(x => x.TableId == tableId);
|
||
if (artTable == null)
|
||
{
|
||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||
return;
|
||
}
|
||
|
||
var artItem = artTable.Items.FirstOrDefault(x => x.Id == itemId);
|
||
if (artItem == null || artItem.Sprite == null)
|
||
{
|
||
EditorGUILayout.LabelField("无图", GUILayout.Width(50));
|
||
return;
|
||
}
|
||
|
||
// 显示预览图
|
||
Rect rect = GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||
GUI.DrawTexture(rect, artItem.Sprite.texture, ScaleMode.ScaleToFit);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新增场景
|
||
/// </summary>
|
||
private void AddNewScene()
|
||
{
|
||
// 找到最大的Scene ID
|
||
int maxScene = allProgressDataList.Count > 0 ? allProgressDataList.Max(x => x.Scene) : 0;
|
||
int newScene = maxScene + 1;
|
||
|
||
// 找到最大的ID
|
||
int maxId = allProgressDataList.Count > 0 ? allProgressDataList.Max(x => x.Id) : 0;
|
||
|
||
// 创建25个新记录
|
||
for (int lv = 1; lv <= 25; lv++)
|
||
{
|
||
maxId++;
|
||
|
||
var newData = new IndoorProgressData
|
||
{
|
||
Id = maxId,
|
||
Scene = newScene,
|
||
Lv = lv,
|
||
Item = "",
|
||
Emit = "",
|
||
Reward = "",
|
||
BigReward = "",
|
||
AreaReward = "",
|
||
Part = 0
|
||
};
|
||
|
||
// 如果是Lv25,添加默认的AreaReward
|
||
if (lv == 25)
|
||
{
|
||
// 默认给25能量
|
||
newData.AreaReward = "[{\"Id\":100001,\"Num\":25}]";
|
||
newData.BigReward = "Energy=25";
|
||
}
|
||
|
||
allProgressDataList.Add(newData);
|
||
}
|
||
|
||
// 切换到新场景
|
||
selectedScene = newScene;
|
||
sceneInputText = newScene.ToString();
|
||
FilterProgressByScene();
|
||
|
||
EditorUtility.DisplayDialog("成功", $"已创建场景 {newScene},包含25个步骤(Lv 1-25)", "确定");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存数据
|
||
/// </summary>
|
||
private void SaveData()
|
||
{
|
||
try
|
||
{
|
||
string progressExcelPath = Path.Combine(docsRootPath, "config", INDOOR_PROGRESS_EXCEL_NAME);
|
||
if (!File.Exists(progressExcelPath))
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"未找到IndoorProgress配置文件: {progressExcelPath}", "确定");
|
||
return;
|
||
}
|
||
|
||
using (var package = new ExcelPackage(new FileInfo(progressExcelPath)))
|
||
{
|
||
var worksheet = package.Workbook.Worksheets[INDOOR_PROGRESS_SHEET_NAME];
|
||
if (worksheet == null)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"IndoorProgress.xlsx中未找到Sheet: {INDOOR_PROGRESS_SHEET_NAME}", "确定");
|
||
return;
|
||
}
|
||
|
||
// 查找列索引
|
||
int idCol = -1, sceneCol = -1, lvCol = -1, itemCol = -1, emitCol = -1,
|
||
rewardCol = -1, bigRewardCol = -1, areaRewardCol = -1, partCol = -1;
|
||
int columnCount = worksheet.Dimension.Columns;
|
||
|
||
for (int col = 1; col <= columnCount; col++)
|
||
{
|
||
string header = worksheet.Cells[1, col].Text;
|
||
if (header == "Id") idCol = col;
|
||
else if (header == "Scene") sceneCol = col;
|
||
else if (header == "Lv") lvCol = col;
|
||
else if (header == "Item") itemCol = col;
|
||
else if (header == "Emit") emitCol = col;
|
||
else if (header == "Reward") rewardCol = col;
|
||
else if (header == "BigReward") bigRewardCol = col;
|
||
else if (header == "AreaReward") areaRewardCol = col;
|
||
else if (header == "Part") partCol = col;
|
||
}
|
||
|
||
// 更新和删除数据(从第3行开始)
|
||
int rowCount = worksheet.Dimension.Rows;
|
||
var processedIds = new HashSet<int>();
|
||
|
||
// 第一遍:更新现有行或标记删除
|
||
for (int row = rowCount; row >= 3; row--)
|
||
{
|
||
string idText = worksheet.Cells[row, idCol].Text;
|
||
if (string.IsNullOrEmpty(idText)) continue;
|
||
|
||
if (!int.TryParse(idText, out int id)) continue;
|
||
|
||
// 查找对应的数据
|
||
var progressData = allProgressDataList.Find(x => x.Id == id);
|
||
if (progressData != null)
|
||
{
|
||
// 更新数据
|
||
worksheet.Cells[row, sceneCol].Value = progressData.Scene;
|
||
worksheet.Cells[row, lvCol].Value = progressData.Lv;
|
||
worksheet.Cells[row, itemCol].Value = progressData.Item;
|
||
worksheet.Cells[row, emitCol].Value = progressData.Emit;
|
||
worksheet.Cells[row, rewardCol].Value = progressData.Reward;
|
||
worksheet.Cells[row, bigRewardCol].Value = progressData.BigReward;
|
||
worksheet.Cells[row, areaRewardCol].Value = progressData.AreaReward;
|
||
worksheet.Cells[row, partCol].Value = progressData.Part > 0 ? progressData.Part.ToString() : "";
|
||
|
||
processedIds.Add(id);
|
||
}
|
||
else
|
||
{
|
||
// 删除行
|
||
worksheet.DeleteRow(row);
|
||
}
|
||
}
|
||
|
||
// 第二遍:添加新行
|
||
int currentRow = worksheet.Dimension?.Rows ?? 2;
|
||
foreach (var progressData in allProgressDataList.OrderBy(x => x.Id))
|
||
{
|
||
if (!processedIds.Contains(progressData.Id))
|
||
{
|
||
// 这是新增的数据
|
||
currentRow++;
|
||
worksheet.Cells[currentRow, idCol].Value = progressData.Id;
|
||
worksheet.Cells[currentRow, sceneCol].Value = progressData.Scene;
|
||
worksheet.Cells[currentRow, lvCol].Value = progressData.Lv;
|
||
worksheet.Cells[currentRow, itemCol].Value = progressData.Item;
|
||
worksheet.Cells[currentRow, emitCol].Value = progressData.Emit;
|
||
worksheet.Cells[currentRow, rewardCol].Value = progressData.Reward;
|
||
worksheet.Cells[currentRow, bigRewardCol].Value = progressData.BigReward;
|
||
worksheet.Cells[currentRow, areaRewardCol].Value = progressData.AreaReward;
|
||
worksheet.Cells[currentRow, partCol].Value = progressData.Part > 0 ? progressData.Part.ToString() : "";
|
||
}
|
||
}
|
||
|
||
// 保存文件
|
||
package.Save();
|
||
}
|
||
|
||
// 提示成功并提醒推送
|
||
EditorUtility.DisplayDialog("保存成功",
|
||
"配置已保存到Excel文件!\n\n" +
|
||
"⚠️ 重要提醒:\n" +
|
||
"请及时在SourceTree或GitHubDesktop中:\n" +
|
||
"1. 提交(Commit)本次修改\n" +
|
||
"2. 推送(Push)到远程仓库\n\n" +
|
||
"避免与其他策划产生冲突!",
|
||
"我知道了");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"保存数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 场景进度数据类
|
||
/// </summary>
|
||
private class IndoorProgressData
|
||
{
|
||
public int Id;
|
||
public int Scene;
|
||
public int Lv;
|
||
public string Item;
|
||
public string Emit;
|
||
public string Reward;
|
||
public string BigReward;
|
||
public string AreaReward;
|
||
public int Part;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Item数据类
|
||
/// </summary>
|
||
private class ItemData
|
||
{
|
||
public int Id;
|
||
public string Name;
|
||
public int IType;
|
||
public string Res;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Item奖励结构
|
||
/// </summary>
|
||
private struct ItemReward
|
||
{
|
||
public int Id;
|
||
public int Num;
|
||
}
|
||
}
|
||
}
|