diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..f598828
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..4cd2db0
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace WpfApp7
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/H5MotaUpdate.csproj b/H5MotaUpdate.csproj
new file mode 100644
index 0000000..2d044b9
--- /dev/null
+++ b/H5MotaUpdate.csproj
@@ -0,0 +1,15 @@
+
+
+
+ WinExe
+ net8.0-windows
+ enable
+ enable
+ true
+
+
+
+
+
+
+
diff --git a/H5MotaUpdate.sln b/H5MotaUpdate.sln
new file mode 100644
index 0000000..8aa7af4
--- /dev/null
+++ b/H5MotaUpdate.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34330.188
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "H5MotaUpdate", "H5MotaUpdate.csproj", "{47588914-BA36-4776-A2EF-268CA2F4670D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {47588914-BA36-4776-A2EF-268CA2F4670D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {47588914-BA36-4776-A2EF-268CA2F4670D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {47588914-BA36-4776-A2EF-268CA2F4670D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {47588914-BA36-4776-A2EF-268CA2F4670D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {28674D31-0DB9-4B3C-B931-D4BB89121469}
+ EndGlobalSection
+EndGlobal
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..ebbf6a6
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..39a6dbe
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,28 @@
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Forms;
+using System.IO;
+using H5MotaUpdate.ViewModels;
+
+namespace H5MotaUpdate
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs
new file mode 100644
index 0000000..78b1f73
--- /dev/null
+++ b/ViewModels/MainViewModel.cs
@@ -0,0 +1,189 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace H5MotaUpdate.ViewModels
+{
+ //silverCoin->Wand
+ class MainViewModel : INotifyPropertyChanged
+ {
+ private string? _sourceRootDirectory;
+ private string? _destRootDirectory;
+ private string? _versionString;
+ private string? SourceProjectDirectory, DestProjectDirectroy;
+ private bool _migrateServerTable;
+
+ public string? SourceRootDirectory
+ {
+ get { return _sourceRootDirectory; }
+ set
+ {
+ _sourceRootDirectory = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string? DestRootDirectory
+ {
+ get => _destRootDirectory;
+ set
+ {
+ _destRootDirectory = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public string? VersionString
+ {
+ get => _versionString;
+ set
+ {
+ _versionString = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public bool MigrateServerTable
+ {
+ get => _migrateServerTable;
+ set
+ {
+ _migrateServerTable = value;
+ OnPropertyChanged();
+ }
+ }
+
+ public ICommand SelectSourceCommand { get; set; }
+ public ICommand SelectDestCommand { get; set; }
+ public ICommand MigrateCommand { get; set; }
+
+ public MainViewModel()
+ {
+ SourceRootDirectory = "请选择包含要翻新的旧塔的文件夹";
+ DestRootDirectory = "请选择一个包含新的2.10.3样板的文件夹";
+ VersionString = "-";
+ SelectSourceCommand = new RelayCommand(SelectSourceRootFolder);
+ SelectDestCommand = new RelayCommand(SelectDestRootFolder);
+ MigrateCommand = new RelayCommand(StartMigrate);
+ MigrateServerTable = false;
+ }
+
+ private void SelectSourceRootFolder()
+ {
+ using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
+ {
+ if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ {
+ SourceRootDirectory = folderBrowserDialog.SelectedPath;
+ }
+ VersionString = VersionUtils.GetVersion(SourceRootDirectory);
+ }
+ }
+
+ public void SelectDestRootFolder()
+ {
+ using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
+ {
+ if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ {
+ DestRootDirectory = folderBrowserDialog.SelectedPath;
+ }
+ }
+ }
+
+ // 检查源文件夹和目标文件夹是否存在project子文件夹
+ bool CheckValid()
+ {
+ if (!FileUtils.IsFolderPathValid(SourceRootDirectory, "源")) return false;
+ if (!FileUtils.IsFolderPathValid(DestRootDirectory, "目标")) return false;
+ SourceProjectDirectory = Path.Combine(SourceRootDirectory, "project");
+ DestProjectDirectroy = Path.Combine(DestRootDirectory, "project");
+ if (!FileUtils.IsFolderPathValid(SourceProjectDirectory, "源/project")) return false;
+ if (!FileUtils.IsFolderPathValid(DestProjectDirectroy, "目标/project")) return false;
+ if (!VersionUtils.IsValidVersion(VersionString))
+ {
+ MessageBox.Show("版本号格式不合法!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return false;
+ }
+ return true;
+ }
+
+
+ // 本按键功能:样板版本在2.7及以上,直接复制project文件夹下的animates, autotiles, bgms, fonts, images, materials, sounds, tilesets七个文件夹
+ // 否则,复制animates文件夹,然后:根据data.js和icons.js等文件的注册信息,拆分sounds文件夹为sounds和bgms,拆分images文件夹
+ // 在原工程未注册的素材不会复制,如有需要请手动复制
+ // 需要配合“复制全塔属性”“复制素材信息”按钮完成素材的自动注册。之后请手动检查工程是否能打开,迁移结果是否正确,并进行相应调整。
+ public void StartMigrate()
+ {
+ if (!CheckValid()) return;
+ Version ver;
+ Version.TryParse(VersionString, out ver);
+ int width = StringUtils.ReadMapWidth(Path.Combine(SourceRootDirectory, "libs/core.js"));
+
+ DataJSMigrator dataJSMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver);
+ EnemysJSMigrator enemysJSMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver);
+ IconsJSMigrator iconsJSMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver);
+ ItemsJSMigrator itemsJSMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver);
+ MapsJSMigrator mapsJSMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver);
+ FloorsMigrator floorsMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver, width);
+ MediaSourceMigrator mediaSourceJSMigrator = new(SourceProjectDirectory, DestProjectDirectroy, ver);
+
+ dataJSMigrator.Migrate();
+ enemysJSMigrator.Migrate();
+ iconsJSMigrator.Migrate();
+ itemsJSMigrator.Migrate();
+ mapsJSMigrator.Migrate();
+ floorsMigrator.Migrate(mapsJSMigrator.mapsIndexArray);
+ mediaSourceJSMigrator.Migrate();
+
+ if (MigrateServerTable)
+ {
+ ServerTableMigrator serverTableJSMigrator = new(SourceRootDirectory, DestRootDirectory, ver);
+ serverTableJSMigrator.Migrate();
+ }
+
+ }
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+ protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public class RelayCommand : ICommand
+ {
+ public event EventHandler? CanExecuteChanged;
+
+ private Action action;
+ public RelayCommand(Action action)
+ {
+ this.action = action;
+ }
+
+ public bool CanExecute(object? parameter)
+ {
+ return true;
+ }
+
+ public void Execute(object? parameter)
+ {
+ action?.Invoke();
+ }
+ }
+ }
+
+
+}
diff --git a/ViewModels/Migrator/DataJSMigrator.cs b/ViewModels/Migrator/DataJSMigrator.cs
new file mode 100644
index 0000000..e7511e2
--- /dev/null
+++ b/ViewModels/Migrator/DataJSMigrator.cs
@@ -0,0 +1,331 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Media.Effects;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Microsoft.VisualBasic.Devices;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal class DataJSMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "data.js",
+ DATANAME = "data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d";
+ public DataJSMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver)
+ {
+ sourcePath = System.IO.Path.Combine(oldProjectDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newProjectDirectory, FILENAME);
+ this.version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 7)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ JObject jsonObject = StringUtils.getValidJson(sourcePath);
+ if (version.CompareTo(new Version(2, 7)) < 0)
+ {
+ Convert(jsonObject);
+ }
+ StringBuilder newJsContent = new StringBuilder();
+ newJsContent.Append("var " + DATANAME + " = ");
+ newJsContent.Append(jsonObject.ToString());
+ File.WriteAllText(destPath, newJsContent.ToString());
+ }
+ MessageBox.Show("迁移project/" + FILENAME + "文件完成。");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移project/" + FILENAME + $"过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+ FileUtils.CopyFile(sourcePath, destPath, FILENAME);
+ }
+
+ void Convert(JObject jsonObject)
+ {
+
+ JObject mainData = (JObject)jsonObject["main"],
+ firstData = (JObject)jsonObject["firstData"],
+ valuesData = (JObject)jsonObject["values"],
+ flagsData = (JObject)jsonObject["flags"];
+
+ JArray tilesetArr = (JArray)mainData["tilesets"];
+ if (tilesetArr != null && tilesetArr.Count > 0) // 2.4.2(?)之前不存在main.tilesets
+ {
+ foreach (JToken tileset in tilesetArr)
+ {
+ string tilesetStr = tileset.ToString();
+ if (tilesetStr.Contains('.'))
+ {
+ tilesetStr += ".png";
+ }
+ }
+ }
+
+ #region
+ // 转换难度列表levelChoose的格式
+ JArray levelChooseArr = (JArray)mainData["levelChoose"];
+ JArray newlevelChooseArr = new JArray();
+
+ if (flagsData["startDirectly"] != null && flagsData["startDirectly"].Value() == true)
+ {
+ // startDirectly为true时直接开始,无难度选项
+ }
+ else
+ {
+ for (int i = 0; i < levelChooseArr.Count; i++)
+ {
+ JArray level = (JArray)levelChooseArr[i];
+ JObject newLevel = new JObject();
+ newLevel.Add("title", level[0]);
+ newLevel.Add("name", level[1]);
+ newLevel.Add("hard", i);
+ newLevel.Add("color", new JArray(64, 25, 85, 1));
+ newLevel.Add("action", new JArray());
+ newlevelChooseArr.Add(newLevel);
+ }
+ }
+ mainData["levelChoose"] = newlevelChooseArr;
+ #endregion
+
+ // 增加main.fonts
+ mainData.Add("fonts", new JArray());
+
+ // 合并main.styles
+ JObject newStyles = new JObject(
+ new JProperty("startBackground", "project/images/bg.jpg"),
+ new JProperty("startVerticalBackground", "project/images/bg.jpg"), // 竖屏标题界面背景图
+ new JProperty("startLogoStyle", "color: black"),
+ new JProperty("startButtonsStyle", "background-color: #32369F; opacity: 0.85; color: #FFFFFF; border: #FFFFFF 2px solid; caret-color: #FFD700;"),
+ new JProperty("statusLeftBackground", "url(project/materials/ground.png) repeat"),
+ new JProperty("statusTopBackground", "url(project/materials/ground.png) repeat"),
+ new JProperty("toolsBackground", "url(project/materials/ground.png) repeat"),
+ new JProperty("borderColor", new JArray(204, 204, 204, 1)),
+ new JProperty("statusBarColor", new JArray(255, 255, 255, 1)),
+ new JProperty("floorChangingStyle", "background-color: black; color: white"),
+ new JProperty("font", "Verdana")
+ );
+ string[] styleKeys = ["startLogoStyle", "startButtonsStyle", "statusLeftBackground", "statusTopBackground", "toolsBackground", "font"];
+ foreach (string key in styleKeys)
+ {
+ if (mainData.ContainsKey(key))
+ {
+ newStyles[key] = mainData[key];
+ }
+ }
+ foreach (JProperty prop in newStyles.Properties())
+ {
+ string key = prop.Name;
+ if (mainData.ContainsKey(key))
+ {
+ mainData.Remove(key);
+ }
+ }
+ mainData["styles"] = newStyles;
+
+ mainData.Remove("floorChangingBackground");
+ mainData.Remove("floorChangingTextColor");
+
+ JArray imageArr = (JArray)mainData["images"];
+ if (!imageArr.Contains("hero.png")) imageArr.Add("hero.png"); //?
+
+ JObject heroData = (JObject)firstData["hero"],
+ heroItemData = (JObject)heroData["items"];
+ heroData["image"] = "hero.png";
+ heroData["followers"] = new JArray();
+ if (heroData["exp"] == null) heroData["exp"] = heroData["experience"];
+ heroData.Remove("experience");
+
+ JObject heroToolsData = (JObject)heroItemData["tools"];
+ JObject heroKeysData = (JObject)heroItemData["keys"];
+ if (heroKeysData != null)
+ {
+ if (heroToolsData == null)
+ {
+ heroToolsData = new JObject();
+ }
+ StringUtils.MergeJObjects(heroToolsData, heroKeysData);
+ }
+ heroItemData.Remove("keys");
+
+
+ #region
+ // 转换全局商店shop的格式
+ JArray shopArr = (JArray)firstData["shops"];
+ for (int i = 0; i < shopArr.Count; i++)
+ {
+ JObject shop = (JObject)shopArr[i];
+
+ if (shop["item"] != null && shop["item"].Value() == true) // 道具商店
+ {
+ shop["use"] = "money";
+ }
+ else if (shop.ContainsKey("commonEvent")) // 公共事件商店
+ {
+
+ }
+ else //普通商店
+ {
+ JArray choiceArr = (JArray)shop["choices"];
+ string use = shop["use"].ToString(),
+ shopNeed = shop["need"].ToString(),
+ shopText = shop["text"].ToString(),
+ shopId = shop["id"].ToString(),
+ flagName_Time = "flag:" + shopId + "_times", // 新设的购买次数变量flag:xxx
+ flagName_Price = "flag:" + shopId + "_price"; // 新设的价格变量flag:xxx
+ string priceStr = shopNeed.Replace("times", flagName_Time); //用新变量名取代times之后的商店价格字符串
+ shop["text"] = StringUtils.ReplaceInBetweenCurlyBraces(shopText, "times", flagName_Time);
+ shop["text"] = StringUtils.ReplaceInBetweenCurlyBraces(shopText, "need", flagName_Price);
+ shop["disablePreview"] = false;
+
+ if (use == "experience")
+ {
+ use = "exp";
+ }
+
+ for (int j = 0; j < choiceArr.Count; j++)
+ {
+ JObject choice = (JObject)choiceArr[j];
+ string requirement;
+ string? choiceNeed = null;
+ if (choice.ContainsKey("need"))
+ {
+ choiceNeed = choice["need"].ToString().Replace("times", flagName_Time);
+ requirement = "status:" + use + ">=" + choiceNeed;
+ }
+ else
+ {
+ requirement = "status:" + use + ">=" + priceStr;
+ }
+ choice["need"] = requirement; // 单个选项的使用条件
+
+ JArray newAction = new JArray();
+ JObject setPrice = StringUtils.getAddValueJson(flagName_Price, choiceNeed != null ? choiceNeed : priceStr, "="),
+ deductMoney = StringUtils.getAddValueJson("status:" + use, flagName_Price, "-="),
+ addTime = StringUtils.getAddValueJson(flagName_Time, "1", "+=");
+ newAction.Add(setPrice);
+ newAction.Add(deductMoney);
+ newAction.Add(addTime);
+
+ string oldEffect = choice["effect"].ToString();
+ var newEffectJArray = StringUtils.doEffect(oldEffect);
+ newAction.Merge(newEffectJArray);
+ choice["action"] = newAction; //单个选项使用时执行的事件
+ }
+ }
+ }
+ #endregion
+
+ if (valuesData["statusCanvasRowsOnMobile"] == null)
+ {
+ valuesData["statusCanvasRowsOnMobile"] = flagsData["statusCanvasRowsOnMobile"];
+ }
+ flagsData.Remove("statusCanvasRowsOnMobile");
+
+ if (valuesData["redGem"] == null)
+ {
+ valuesData["redGem"] = valuesData["redJewel"];
+ }
+ valuesData.Remove("redJewel");
+
+ if (valuesData["blueGem"] == null)
+ {
+ valuesData["blueGem"] = valuesData["blueJewel"];
+ }
+ valuesData.Remove("blueJewel");
+
+ if (valuesData["greenGem"] == null)
+ {
+ valuesData["greenGem"] = valuesData["greenJewel"];
+ }
+ valuesData.Remove("greenJewel");
+
+ valuesData.Remove("moveSpeed");
+ valuesData.Remove("floorChangeTime");
+
+ string[] statusList = [
+ "enableHP",
+ "enableAtk",
+ "enableDef",
+ "enableFloor",
+ "enableName",
+ "enableLv",
+ "enableHPMax",
+ "enableMana",
+ "enableMDef",
+ "enableMoney",
+ "enableExp",
+ "enableLevelUp",
+ "levelUpLeftMode",
+ "enableKeys",
+ "enableGreenKey",
+ "enablePZF",
+ "enableDebuff",
+ "enableSkill",
+ ];
+
+ if (flagsData["statusBarItems"] == null)
+ {
+ JArray statusBarItemsArr = new JArray();
+ foreach (string status in statusList)
+ {
+ if (flagsData.ContainsKey(status) && flagsData[status].Value() == true)
+ {
+ statusBarItemsArr.Add(status);
+ }
+ else if (new string[] { "enableHP", "enableAtk", "enableDef" }.Contains(status)) // hp,atk,def为2.7前默认显示的变量,必须显示
+
+ {
+ statusBarItemsArr.Add(status);
+ }
+ else if (status == "enableExp" && flagsData["enableExperience"].Value() == true) // experience更新为exp
+ {
+ statusBarItemsArr.Add(status);
+ }
+ }
+ flagsData["statusBarItems"] = statusBarItemsArr;
+ }
+ foreach (string status in statusList)
+ {
+ flagsData.Remove(status);
+ }
+ flagsData.Remove("pickaxeFourDirections");
+ flagsData.Remove("bombFourDirections");
+ flagsData.Remove("snowFourDirections");
+ flagsData.Remove("bigKeyIsBox");
+ flagsData.Remove("equipment");
+ flagsData.Remove("iconInEquipbox");
+ flagsData.Remove("hatredDecrease");
+ flagsData.Remove("betweenAttackCeil");
+ flagsData.Remove("startDirectly");
+ flagsData.Remove("enableDisabledShop");
+ flagsData.Remove("checkConsole");
+ }
+ }
+}
diff --git a/ViewModels/Migrator/EnemysJSMigrator.cs b/ViewModels/Migrator/EnemysJSMigrator.cs
new file mode 100644
index 0000000..d4d5eae
--- /dev/null
+++ b/ViewModels/Migrator/EnemysJSMigrator.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+
+// 功能:复制project/enemy.js文件
+// 样板版本在2.9以下 按照如下逻辑改写相关数据:敌人具有光环属性:enemy.haloRange = enemy.range;同时若不具有领域enemy.range=null
+// 敌人具有光环属性:enemy.haloSquare = enemy.zoneSquare;同时若不具有领域enemy.zoneSquare=null
+// 敌人具有光环属性:enemy.haloAdd = enemy.Add;同时若不具有吸血enemy.add=null
+// 敌人具有反击属性: enemy.counterAttack = enemy.atkValue;同时若不具有退化 enemy.atkValue = null
+// 敌人具有破甲属性: enemy.breakArmor = enemy.defValue;同时若不具有退化 enemy.defValue = null
+// 敌人具有领域、阻击、激光属性 分别令enemy.zone/repulse/laser
+// enemy.value = null
+namespace H5MotaUpdate.ViewModels
+{
+ internal class EnemysJSMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "enemys.js",
+ DATANAME = "enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80";
+
+ ///
+ /// 请输入新旧Project文件夹的路径
+ ///
+ public EnemysJSMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver)
+ {
+ sourcePath = System.IO.Path.Combine(oldProjectDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newProjectDirectory, FILENAME);
+ version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 9)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ JObject jsonObject = StringUtils.getValidJson(sourcePath);
+ Convert(jsonObject);
+ StringBuilder newJsContent = new StringBuilder();
+ newJsContent.Append("var " + DATANAME + " = ");
+ newJsContent.Append(jsonObject.ToString());
+ File.WriteAllText(destPath, newJsContent.ToString());
+ }
+ MessageBox.Show("迁移project/" + FILENAME + "文件完成");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移project/" + FILENAME + $"过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+ FileUtils.CopyFile(sourcePath, destPath, FILENAME);
+ }
+
+ void Convert(JObject jsonObject)
+ {
+ if (version.CompareTo(new Version(2, 7)) < 0)
+ {
+ Convert_before2_7(jsonObject);
+ }
+ Convert_before2_9(jsonObject);
+ }
+
+ static void Convert_before2_9(JObject jsonObject)
+ {
+ foreach (JProperty prop in jsonObject.Properties())
+ {
+
+ JObject enemyData = (JObject)prop.Value;
+ JToken enemySpecial = enemyData["special"];
+ if (enemySpecial is JValue specialValue)
+ {
+ transferSpecialValue(specialValue, enemyData);
+ }
+ else if (enemySpecial is JArray specialValueArr)
+ {
+ transferSpecialArr(specialValueArr, enemyData);
+ }
+ }
+ }
+
+ static void Convert_before2_7(JObject jsonObject)
+ {
+ foreach (JProperty prop in jsonObject.Properties())
+ {
+
+ JObject enemyData = (JObject)prop.Value;
+ JValue enemyExperience = (JValue)enemyData["experience"];
+ if (enemyExperience != null)
+ {
+ enemyData["exp"] = enemyExperience;
+ }
+ enemyData.Remove("experience");
+ }
+
+ }
+
+ ///
+ /// 根据单个特殊属性的JValue改写敌人数据
+ ///
+ static void transferSpecialValue(JValue specialValue, JObject enemy)
+ {
+ if (specialValue.Type == JTokenType.Integer)
+ {
+ switch ((int)specialValue)
+ {
+ case 7: //破甲
+ enemy["counterAttack"] = enemy["atkValue"];
+ enemy.Remove("atkValue");
+ break;
+ case 8: //反击
+ enemy["breakArmor"] = enemy["defValue"];
+ enemy.Remove("defValue");
+ break;
+ case 15://领域
+ enemy["zone"] = enemy["value"];
+ enemy.Remove("value");
+ break;
+ case 18://阻击
+ enemy["repulse"] = enemy["value"];
+ enemy.Remove("value");
+ break;
+ case 24://激光
+ enemy["laser"] = enemy["value"];
+ enemy.Remove("value");
+ break;
+ case 25: //光环
+ enemy["haloRange"] = enemy["range"];
+ enemy["haloSquare"] = enemy["zoneSquare"];
+ enemy["haloAdd"] = enemy["add"];
+ enemy.Remove("range");
+ enemy.Remove("zoneSquare");
+ enemy.Remove("add");
+ break;
+ }
+ }
+ }
+
+ ///
+ /// 根据特殊属性列表JArray改写敌人数据
+ ///
+ static void transferSpecialArr(JArray specialValueArr, JObject enemy)
+ {
+ HashSet specialValuesToCheck = new HashSet { 7, 8, 15, 18, 24, 25 };
+ HashSet foundSpecialValues = new HashSet();
+
+ foreach (JToken item in specialValueArr)
+ {
+ if (item.Type == JTokenType.Integer && specialValuesToCheck.Contains((int)item))
+ {
+ foundSpecialValues.Add((int)item);
+ }
+ }
+
+ if (foundSpecialValues.Contains(7))
+ {
+ enemy["counterAttack"] = enemy["atkValue"];
+ if (!foundSpecialValues.Contains(21)) enemy.Remove("atkValue");
+ }
+ if (foundSpecialValues.Contains(8))
+ {
+ enemy["breakArmor"] = enemy["defValue"];
+ if (!foundSpecialValues.Contains(21)) enemy.Remove("defValue");
+ }
+ if (foundSpecialValues.Contains(15))
+ {
+ enemy["zone"] = enemy["value"];
+ enemy.Remove("value");
+ }
+ if (foundSpecialValues.Contains(18))
+ {
+ enemy["repulse"] = enemy["value"];
+ enemy.Remove("value");
+ }
+ if (foundSpecialValues.Contains(24))
+ {
+ enemy["laser"] = enemy["value"];
+ enemy.Remove("value");
+ }
+ if (foundSpecialValues.Contains(25))
+ {
+ enemy["haloRange"] = enemy["range"];
+ enemy["haloSquare"] = enemy["zoneSquare"];
+ enemy["haloAdd"] = enemy["add"];
+ if (!foundSpecialValues.Contains(11)) enemy.Remove("add");
+ if (!foundSpecialValues.Contains(15))
+ {
+ enemy.Remove("range");
+ enemy.Remove("zoneSquare");
+ }
+ }
+ }
+ }
+}
diff --git a/ViewModels/Migrator/FloorsMigrator.cs b/ViewModels/Migrator/FloorsMigrator.cs
new file mode 100644
index 0000000..2661a55
--- /dev/null
+++ b/ViewModels/Migrator/FloorsMigrator.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.JavaScript;
+using System.Text;
+using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal class FloorsMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "floors";
+ string?[] mapsIndexArray;
+ int mapWidth = 13;
+
+ ///
+ /// 请输入新旧Project文件夹的路径
+ ///
+ public FloorsMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver, int width)
+ {
+ sourcePath = System.IO.Path.Combine(oldProjectDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newProjectDirectory, FILENAME);
+ this.version = ver;
+ this.mapWidth = width;
+ }
+
+ public void Migrate(string?[] mapsIndexArray)
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 7)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ this.mapsIndexArray = mapsIndexArray;
+ MigrateFloors();
+ }
+ MessageBox.Show("迁移project/" + FILENAME + "文件夹完成。");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移project/" + FILENAME + $"文件夹过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+ FileUtils.CopyFolderContents(sourcePath, destPath);
+ }
+
+ void MigrateFloors()
+ {
+ string[] files = Directory.GetFiles(sourcePath);
+ foreach (string file in files)
+ {
+ string sourceFilePath = System.IO.Path.Combine(sourcePath, System.IO.Path.GetFileName(file)),
+ destFilePath = System.IO.Path.Combine(destPath, System.IO.Path.GetFileName(file));
+ MigrateOneFloor(sourceFilePath, destFilePath);
+ }
+ }
+
+ void MigrateOneFloor(string sourceFilePath, string destFilePath)
+ {
+ // 每一层try catch一次,一层出错不影响其它层继续复制
+ try
+ {
+ string floorName = System.IO.Path.GetFileNameWithoutExtension(sourceFilePath);
+ JObject jsonObject = StringUtils.getValidJson(sourceFilePath);
+ if (version.CompareTo(new Version(2, 7)) <= 0)
+ {
+ Convert(jsonObject);
+ }
+ StringBuilder newJsContent = new StringBuilder();
+ newJsContent.Append("main.floors." + floorName + " = ");
+ newJsContent.Append(jsonObject.ToString());
+ File.WriteAllText(destFilePath, newJsContent.ToString());
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移楼层文件" + System.IO.Path.GetFileName(sourceFilePath) + $"时出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void Convert(JObject jsonObject)
+ {
+ jsonObject["canFlyFrom"] = jsonObject["canFlyTo"];
+ jsonObject["ratio"] = jsonObject["item_ratio"];
+ jsonObject.Remove("item_ratio");
+ jsonObject["width"] = mapWidth;
+ jsonObject["height"] = mapWidth;
+ jsonObject["autoEvent"] = new JObject();
+
+ #region
+ // 楼层贴图:老版本为一字符串或数组,字符串会被自动转为[0,0,str]
+ // 其中t[0],t[1]分别为x,y,t[2]为贴图名字 剩下的不知道干嘛的
+ JToken oldImages = jsonObject["images"];
+ string imageName = null;
+ if (oldImages is JArray oldImagesArr)
+ {
+ if (oldImagesArr.Count >= 3)
+ {
+ imageName = oldImagesArr[2].ToString();
+ }
+ }
+ else
+ {
+ imageName = oldImages.ToString();
+ }
+ if (imageName == null)
+ {
+ jsonObject["images"] = new JArray();
+ }
+ JObject newImages = new JObject(new JProperty("name", imageName),
+ new JProperty("canvas", "bg"),
+ new JProperty("sx", 0),
+ new JProperty("sy", 0),
+ new JProperty("w", 416),
+ new JProperty("h", 416)
+ );
+ jsonObject["images"] = new JArray(newImages);
+ #endregion
+
+ #region
+ JArray mapMatrix = (JArray)jsonObject["map"];
+ JArray zeroBgMatrix = StringUtils.CreateMatrix(mapWidth, mapWidth);
+ for (int i = 0; i < mapMatrix.Count; i++)
+ {
+ JArray lineArr = (JArray)mapMatrix[i];
+ for (int j = 0; j < lineArr.Count; j++)
+ {
+ int onePoint = lineArr[j].Value();
+ if (onePoint >= 81 && onePoint <= 86 && mapsIndexArray != null)
+ { // 将terrains门替换为animates门
+ lineArr[j] = mapsIndexArray[onePoint - 81];
+ }
+ if (onePoint == 167 && version.CompareTo(new Version(2, 6)) < 0)
+ {
+ // 2.6以后,滑冰转移到背景层
+ JArray bgMatrix = (JArray)jsonObject["bgmap"].DeepClone();
+ if (bgMatrix == null || bgMatrix.Count == 0)
+ {
+ jsonObject["bgmap"] = zeroBgMatrix.DeepClone();
+ }
+ jsonObject["bgmap"][i][j] = 167;
+ mapMatrix[i][j] = 0;
+ }
+ }
+ }
+ #endregion
+ }
+ }
+}
diff --git a/ViewModels/Migrator/IconsJSMigrator.cs b/ViewModels/Migrator/IconsJSMigrator.cs
new file mode 100644
index 0000000..dfdbe6f
--- /dev/null
+++ b/ViewModels/Migrator/IconsJSMigrator.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal class IconsJSMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "icons.js",
+ DATANAME = "icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1";
+
+ ///
+ /// 请输入新旧Project文件夹的路径
+ ///
+ public IconsJSMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver)
+ {
+ sourcePath = System.IO.Path.Combine(oldProjectDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newProjectDirectory, FILENAME);
+ version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 7)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ JObject jsonObject = StringUtils.getValidJson(sourcePath);
+ if (version.CompareTo(new Version(2, 7)) < 0)
+ {
+ ConvertIconsJS_before2_7(jsonObject);
+ }
+ StringBuilder newJsContent = new StringBuilder();
+ newJsContent.Append("var " + DATANAME + " = ");
+ newJsContent.Append(jsonObject.ToString());
+ File.WriteAllText(destPath, newJsContent.ToString());
+ }
+ MessageBox.Show("迁移project/" + FILENAME + "文件完成");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移project/" + FILENAME + $"过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+
+ FileUtils.CopyFile(sourcePath, destPath, FILENAME);
+ }
+
+ void ConvertIconsJS_before2_7(JObject jsonObject)
+ {
+ // 暂时将错就错沿用旧名字,有问题再说
+ /*
+ JObject terrains = (JObject)jsonObject["terrains"];
+ terrains.Remove("yellowWall");
+ terrains.Remove("blueWall");
+ terrains.Remove("whiteWall");
+ terrains.Remove("yellowDoor");
+ terrains.Remove("blueDoor");
+ terrains.Remove("redDoor");
+ terrains.Remove("greenDoor");
+ terrains.Remove("specialDoor");
+ terrains.Remove("steelDoor");
+
+ if (terrains["blueShopLeft"] == null)
+ {
+ terrains["blueShopLeft"] = terrains["blueShop-left"];
+ }
+ terrains.Remove("blueShop-right");
+ if (terrains["blueShopRight"] == null)
+ {
+ terrains["blueShopRight"] = terrains["blueShop-right"];
+ }
+ terrains.Remove("blueShop-right");
+ if (terrains["pinkShopLeft"] == null)
+ {
+ terrains["pinkShopLeft"] = terrains["pinkShop-left"];
+ }
+ terrains.Remove("pinkShop-left");
+ if (terrains["pinkShopRight"] == null)
+ {
+ terrains["pinkShopRight"] = terrains["pinkShop-right"];
+ }
+ terrains.Remove("pinkShop-left");
+ JObject items = (JObject)jsonObject["items"];
+ if (items["snow"] != null)
+ {
+ items["freezeBadge"] = items["snow"];
+ }
+ items.Remove("snow");
+ */
+ }
+ }
+}
diff --git a/ViewModels/Migrator/ItemsJSMigrator.cs b/ViewModels/Migrator/ItemsJSMigrator.cs
new file mode 100644
index 0000000..4e5b38e
--- /dev/null
+++ b/ViewModels/Migrator/ItemsJSMigrator.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+namespace H5MotaUpdate.ViewModels
+{
+ internal class ItemsJSMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "items.js",
+ DATANAME = "items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a";
+
+ ///
+ /// 请输入新旧Project文件夹的路径
+ ///
+ public ItemsJSMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver)
+ {
+ sourcePath = System.IO.Path.Combine(oldProjectDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newProjectDirectory, FILENAME);
+ this.version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 7)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ JObject jsonObject = StringUtils.getValidJson(sourcePath);
+ if (version.CompareTo(new Version(2, 7)) < 0)
+ {
+ Convert(ref jsonObject);
+ }
+ StringBuilder newJsContent = new StringBuilder();
+ newJsContent.Append("var " + DATANAME + " = ");
+ newJsContent.Append(jsonObject.ToString());
+ File.WriteAllText(destPath, newJsContent.ToString());
+ }
+ MessageBox.Show("迁移project/" + FILENAME + "文件完成。");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移project/" + FILENAME + $"过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+ FileUtils.CopyFile(sourcePath, destPath, FILENAME);
+ }
+
+ void Convert(ref JObject jsonObject)
+ {
+ //2.7之前items.js有items,itemEffect, itemEffectTip, useItemEvent, useItemEffect, canUseItemEffect, equipCondition七个键
+ //新建一个items对象,迁移其他对象中的内容
+ JObject newItemDatas = (JObject)jsonObject["items"],
+ itemEffect = (JObject)jsonObject["itemEffect"],
+ itemEffectTip = (JObject)jsonObject["itemEffectTip"],
+ useItemEvent = (JObject)jsonObject["useItemEvent"],
+ useItemEffect = (JObject)jsonObject[" useItemEffect"],
+ canUseItemEffect = (JObject)jsonObject["canUseItemEffect"],
+ equipCondition = (JObject)jsonObject["equipCondition"];
+ string[] arr = ["itemEffect", "itemEffectTip", "useItemEvent", "useItemEffect", "canUseItemEffect", "equipCondition"];
+ foreach (string ele in arr)
+ {
+ JObject eleData = (JObject)jsonObject[ele];
+ if (eleData == null || eleData.Count == 0) { continue; }
+ foreach (JProperty prop in eleData.Properties())
+ {
+ string key = prop.Name,
+ valueString = prop.Value.ToString();
+ valueString = StringUtils.ReplaceOldNames(valueString, version); //危险操作
+ if (newItemDatas.ContainsKey(key))
+ {
+ JObject itemData = (JObject)newItemDatas[key];
+ itemData[ele] = valueString;
+ }
+ }
+ }
+ foreach (JProperty prop in newItemDatas.Properties())
+ {
+ string key = prop.Name;
+ JObject perData = (JObject)prop.Value;
+ if (perData["cls"].ToString() == "keys")
+ {
+ perData["cls"] = "tools";
+ perData["hideInToolbox"] = true;
+ }
+ }
+ if (newItemDatas.ContainsKey("snow"))
+ {
+ newItemDatas["freezeBadge"] = newItemDatas["snow"];
+ newItemDatas.Remove("snow");
+ }
+ jsonObject = newItemDatas; //直接赋值操作需要加ref
+ }
+ }
+}
diff --git a/ViewModels/Migrator/MapsJSMigrator.cs b/ViewModels/Migrator/MapsJSMigrator.cs
new file mode 100644
index 0000000..a9871ec
--- /dev/null
+++ b/ViewModels/Migrator/MapsJSMigrator.cs
@@ -0,0 +1,188 @@
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal class MapsJSMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "maps.js",
+ DATANAME = "maps_90f36752_8815_4be8_b32b_d7fad1d0542e";
+ public string?[] mapsIndexArray;
+
+ ///
+ /// 请输入新旧Project文件夹的路径
+ ///
+ public MapsJSMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver)
+ {
+ sourcePath = System.IO.Path.Combine(oldProjectDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newProjectDirectory, FILENAME);
+ this.version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 7)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ JObject jsonObject = StringUtils.getValidJson(sourcePath);
+ if (version.CompareTo(new Version(2, 7)) < 0)
+ {
+ Convert_before2_7(jsonObject);
+ }
+ StringBuilder newJsContent = new StringBuilder();
+ newJsContent.Append("var " + DATANAME + " = ");
+ newJsContent.Append(jsonObject.ToString());
+ File.WriteAllText(destPath, newJsContent.ToString());
+ }
+ MessageBox.Show("迁移project/" + FILENAME + "文件完成。");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移project/" + FILENAME + $"过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+ FileUtils.CopyFile(sourcePath, destPath, FILENAME);
+ }
+
+ void Convert_before2_7(JObject jsonObject)
+ {
+ // 下面列出一些样板图块名字的更改,以备后续需求。为适配旧有事件和脚本,暂时沿用原来的名字。
+ // blueShop-left -> blueShopLeft,
+ // blueShop-right -> blueShopRight,
+ // pinkShop-left -> pinkShopLeft
+ // pinkShop-right -> pinkShopRight
+ // snow -> freezeBadge
+ Dictionary dictionary = new Dictionary{
+ { "blueShop-left", new JObject { { "cls", "terrains" } } },
+ { "blueShop-right", new JObject { { "cls", "terrains" } } },
+ { "pinkShop-left", new JObject { { "cls", "terrains" } } },
+ { "pinkShop-right", new JObject { { "cls", "terrains" } } },
+ { "lavaNet", new JObject { { "cls", "animates" }, { "canPass", true }, { "trigger", "null" }, { "script", "(function () {\n\t// 血网的伤害效果移动到 checkBlock 中处理\n\n\t// 如果要做一次性血网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();" }, { "name", "血网" } } },
+ { "poisonNet", new JObject { { "cls", "animates" }, { "canPass", true }, { "trigger", "null" }, { "script", "(function () {\n\t// 直接插入公共事件进行毒处理\n\tif (!core.hasItem('amulet')) {\n\t\tcore.insertAction({ \"type\": \"insert\", \"name\": \"毒衰咒处理\", \"args\": [0] });\n\t}\n\n\t// 如果要做一次性毒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()" }, { "name", "毒网" } } },
+ { "weakNet", new JObject { { "cls", "animates" }, { "canPass", true }, { "trigger", "null" }, { "script", "(function () {\n\t// 直接插入公共事件进行衰处理\n\tif (!core.hasItem('amulet')) {\n\t\tcore.insertAction({ \"type\": \"insert\", \"name\": \"毒衰咒处理\", \"args\": [1] });\n\t}\n\n\t// 如果要做一次性衰网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()" }, { "name", "衰网" } } },
+ { "curseNet", new JObject { { "cls", "animates" }, { "canPass", true }, { "trigger", "null" }, { "script", "(function () {\n\t// 直接插入公共事件进行咒处理\n\tif (!core.hasItem('amulet')) {\n\t\tcore.insertAction({ \"type\": \"insert\", \"name\": \"毒衰咒处理\", \"args\": [2] });\n\t}\n\n\t// 如果要做一次性咒网,可直接注释掉下面这句话:\n\t// core.removeBlock(core.getHeroLoc('x'), core.getHeroLoc('y'));\n})()" }, { "name", "咒网" } } },
+ { "snow", new JObject { { "cls", "items" } } },
+ { "arrowUp", new JObject { { "cls", "terrains" }, { "canPass", true }, { "cannotOut", new JArray { "left", "right", "down" } }, { "cannotIn", new JArray { "up" } } } },
+ { "arrowDown", new JObject { { "cls", "terrains" }, { "canPass", true }, { "cannotOut", new JArray { "left", "right", "up" } }, { "cannotIn", new JArray { "down" } } } },
+ { "arrowLeft", new JObject { { "cls", "terrains" }, { "canPass", true }, { "cannotOut", new JArray { "up", "down", "right" } }, { "cannotIn", new JArray { "left" } } } },
+ { "arrowRight", new JObject { { "cls", "terrains" }, { "canPass", true }, { "cannotOut", new JArray { "up", "down", "left" } }, { "cannotIn", new JArray { "right" } } } },
+ { "light", new JObject { { "cls", "terrains" }, { "trigger", "null" }, { "canPass", true }, { "script", "(function () {\n\tcore.setBlock(core.getNumberById('darkLight'), core.getHeroLoc('x'), core.getHeroLoc('y'));\n})();" } } },
+ };
+
+ // 原先不存在的新图块信息
+ Dictionary newIconsDictionary = new Dictionary {
+ { "yellowDoor", new JObject { { "cls", "animates" }, { "trigger", "openDoor" }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject { { "yellowKey", 1 } } } } }, { "name", "黄门" } } },
+ { "blueDoor", new JObject { { "cls", "animates" }, { "trigger", "openDoor" }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject { { "blueKey", 1 } } } } }, { "name", "蓝门" } } },
+ { "redDoor", new JObject { { "cls", "animates" }, { "trigger", "openDoor" }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject { { "redKey", 1 } } } } }, { "name", "红门" } } },
+ { "greenDoor", new JObject { { "cls", "animates" }, { "trigger", "openDoor" }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject { { "greenKey", 1 } } } } }, { "name", "绿门" } } },
+ { "specialDoor", new JObject { { "cls", "animates" }, { "trigger", "openDoor" }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject { { "specialKey", 1 } } } } }, { "name", "机关门" } } },
+ { "steelDoor", new JObject { { "cls", "animates" }, { "trigger", "openDoor" }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject { { "steelKey", 1 } } } } }, { "name", "铁门" } } },
+ { "yellowWallDoor", new JObject { { "cls", "animates" }, { "canBreak", true }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject() } } } } },
+ { "whiteWallDoor", new JObject { { "cls", "animates" }, { "canBreak", true }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject() } } } } },
+ { "blueWallDoor", new JObject { { "cls", "animates" }, { "canBreak", true }, { "animate", 1 }, { "doorInfo", new JObject { { "time", 160 }, { "openSound", "door.mp3" }, { "closeSound", "door.mp3" }, { "keys", new JObject() } } } } },
+ { "ground", new JObject { { "cls", "terrains" } } },
+ { "grass", new JObject { { "cls", "terrains" } } },
+ { "grass2", new JObject { { "cls", "terrains" } } },
+ { "snowGround", new JObject { { "cls", "terrains" } } },
+ { "ground2", new JObject { { "cls", "terrains" } } },
+ { "ground3", new JObject { { "cls", "terrains" } } },
+ { "ground4", new JObject { { "cls", "terrains" } } },
+ { "sand", new JObject { { "cls", "terrains" } } },
+ { "ground5", new JObject { { "cls", "terrains" } } },
+ { "yellowWall2", new JObject { { "cls", "terrains" } } },
+ { "whiteWall2", new JObject { { "cls", "terrains" } } },
+ { "blueWall2", new JObject { { "cls", "terrains" } } },
+ { "blockWall", new JObject { { "cls", "terrains" } } },
+ { "grayWall", new JObject { { "cls", "terrains" } } },
+ { "white", new JObject { { "cls", "terrains" } } },
+ { "ground6", new JObject { { "cls", "terrains" } } },
+ { "soil", new JObject { { "cls", "terrains" } } },
+ { "ground7", new JObject { { "cls", "terrains" } } },
+ { "ground8", new JObject { { "cls", "terrains" } } }
+ };
+
+ // 生成一个新图块序号的数组
+ mapsIndexArray = new string?[newIconsDictionary.Count];
+
+ // 从0-10000寻找可分配给新图块的序号
+ for (int i = 1, c = 0; i < 10000 && c < newIconsDictionary.Count; i++)
+ {
+ if (i == 17) continue; //不知道为什么,但是编辑器里17号被占用了
+ string str_i = i.ToString();
+ if (!jsonObject.ContainsKey(str_i))
+ {
+ mapsIndexArray[c++] = str_i;
+ }
+ if (i == 9999)
+ {
+ MessageBox.Show("警告!Maps.js中存在过多元素!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ {
+ int index = 0;
+ foreach (KeyValuePair perdata in newIconsDictionary)
+ {
+ string key = perdata.Key;
+ JObject valuePairs = perdata.Value;
+ string? iconsNumber = mapsIndexArray[index++];
+ if (String.IsNullOrEmpty(iconsNumber)) { break; }
+ JObject iconsValue = perdata.Value;
+ iconsValue = StringUtils.MergeJObjects(iconsValue, new JObject(new JProperty("id", key)));
+ jsonObject[iconsNumber] = perdata.Value;
+ }
+ }
+
+ foreach (JProperty prop in jsonObject.Properties())
+ {
+ JObject propObj = (JObject)prop.Value;
+
+ JToken noPass = propObj["noPass"];
+ if (noPass != null && noPass.ToString() == "True")
+ {
+ propObj["canPass"] = false;
+ }
+ if (noPass != null && noPass.ToString() == "False")
+ {
+ propObj["canPass"] = true;
+ }
+ propObj.Remove("noPass");
+
+ // 原先id存在的icons直接查找替换
+ string iconId = propObj["id"].ToString();
+ if (dictionary.ContainsKey(iconId))
+ {
+ propObj = StringUtils.MergeJObjects(propObj, dictionary[iconId]);
+ }
+ }
+
+ for (int i = 81; i <= 86; i++)
+ { // 删除terrains的几个门的索引,避免影响animates门的匹配
+ jsonObject.Remove(i.ToString());
+ }
+ }
+ }
+}
diff --git a/ViewModels/Migrator/MediaSourceMigrator.cs b/ViewModels/Migrator/MediaSourceMigrator.cs
new file mode 100644
index 0000000..5844af9
--- /dev/null
+++ b/ViewModels/Migrator/MediaSourceMigrator.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Drawing;
+
+namespace H5MotaUpdate.ViewModels
+{
+ public class MediaSourceMigrator
+ {
+ string oldProjectDirectory, newProjectDirectory;
+ Version version;
+
+ ///
+ /// 请输入新旧Project文件夹的路径
+ ///
+ public MediaSourceMigrator(string oldProjectDirectory, string newProjectDirectory, Version ver)
+ {
+ this.oldProjectDirectory = oldProjectDirectory;
+ this.newProjectDirectory = newProjectDirectory;
+ this.version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ if (version.CompareTo(new Version(2, 7)) >= 0)
+ {
+ MigrateDirect();
+ }
+ else
+ {
+ Convert();
+ }
+ MessageBox.Show("迁移素材文件完成。");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移素材文件过程中出现错误: " + $"{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ public void MigrateDirect()
+ {
+ string[] subFolderNames = ["animates", "autotiles", "bgms", "fonts", "images", "materials", "sounds", "tilesets"];
+ foreach (string folderName in subFolderNames)
+ {
+ try
+ {
+ string sourcePath = Path.Combine(oldProjectDirectory, folderName),
+ destPath = Path.Combine(newProjectDirectory, folderName);
+ FileUtils.CopyFolderContents(sourcePath, destPath);
+ MessageBox.Show("project/" + folderName + "文件夹迁移完成");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("project/" + folderName + $"文件夹迁移失败,原因:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ }
+
+ public void Convert()
+ {
+ try
+ {
+ string datasJsPath = Path.Combine(oldProjectDirectory, "data.js"),
+ iconsJsPath = Path.Combine(oldProjectDirectory, "icons.js");
+
+ List imagesList = [],
+ bgmsList = [],
+ soundsList = [],
+ autotilesList = [],
+ tilesetsList = [];
+ JObject datasJObject = StringUtils.getValidJson(datasJsPath),
+ iconsJObject = StringUtils.getValidJson(iconsJsPath);
+
+ if (datasJObject["main"]["images"] is JArray imagesJsonList && imagesJsonList.Count > 0)
+ {
+ imagesList = imagesJsonList.ToObject>();
+ imagesList.Add("hero.png"); // 2.7以前hero.png硬编码在loader.prototype._loadExtraImages中
+ imagesList.Add("ground.png");
+
+ }
+ if (datasJObject["main"]["bgms"] is JArray bgmsJsonList && bgmsJsonList.Count > 0)
+ {
+ bgmsList = bgmsJsonList.ToObject>();
+ }
+ if (datasJObject["main"]["sounds"] is JArray soundsJsonList && soundsJsonList.Count > 0)
+ {
+ soundsList = soundsJsonList.ToObject>();
+ }
+ if (datasJObject["main"]["tilesets"] is JArray tilesetsJsonList && tilesetsJsonList.Count > 0)
+ {
+ tilesetsList = tilesetsJsonList.ToObject>();
+ }
+ if (iconsJObject["autotile"] is JObject autotilesJsonList && autotilesJsonList.Count > 0)
+ {
+ autotilesList = autotilesJsonList.Properties().Select(prop => prop.Name + ".png").ToList(); //autotile只允许是.png格式
+ }
+ TransferOldAnimate();
+ TransferOldImages(imagesList, autotilesList, tilesetsList);
+ TransferSounds(bgmsList, soundsList);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"素材文件夹迁移出错,原因:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void TransferOldAnimate()
+ {
+ try
+ {
+ string inputDirectory = Path.Combine(oldProjectDirectory, "animates"),
+ outputDirectory = Path.Combine(newProjectDirectory, "animates");
+ FileUtils.CopyFolderContents(inputDirectory, outputDirectory);
+ MessageBox.Show("project/animates文件夹迁移完成");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"project/animates文件夹迁移出错,原因:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void TransferOldImages(List imagesList, List autotilesList, List tilesetsList)
+ {
+ try
+ {
+ string inputDirectory = Path.Combine(oldProjectDirectory, "images");
+ string[] files = Directory.GetFiles(inputDirectory);
+
+ string[] materialsFiles = ["airwall.png",
+ "animates.png",
+ "enemy48.png",
+ "enemys.png",
+ "fog.png",
+ "ground.png",
+ "icons.png",
+ "icons_old.png",
+ "items.png",
+ "keyboard.png",
+ "npc48.png",
+ "npcs.png",
+ "terrains.png"];
+ foreach (string filePath in files)
+ {
+ string fileName = Path.GetFileName(filePath);
+ if (fileName == "icons.png" && version bgmsList, List soundsList)
+ {
+ try
+ {
+ string inputDirectory = Path.Combine(oldProjectDirectory, "sounds");
+ string[] files = Directory.GetFiles(inputDirectory);
+
+ foreach (string filePath in files)
+ {
+ string fileName = Path.GetFileName(filePath);
+ if (bgmsList.Contains(fileName))
+ {
+ File.Copy(filePath, Path.Combine(newProjectDirectory, "bgms/" + fileName), true);
+ }
+ if (soundsList.Contains(fileName))
+ {
+ File.Copy(filePath, Path.Combine(newProjectDirectory, "sounds/" + fileName), true);
+ }
+ }
+ MessageBox.Show("project/sounds文件夹迁移完成");
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"project/sounds文件夹迁移出错,原因:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ }
+}
diff --git a/ViewModels/Migrator/ServerTableMigrator.cs b/ViewModels/Migrator/ServerTableMigrator.cs
new file mode 100644
index 0000000..39ccd60
--- /dev/null
+++ b/ViewModels/Migrator/ServerTableMigrator.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Media.Effects;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using Microsoft.VisualBasic.Devices;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal class ServerTableMigrator
+ {
+ string sourcePath, destPath;
+ Version version;
+ readonly string FILENAME = "_server/table";
+ public ServerTableMigrator(string oldRootDirectory, string newRootDirectory, Version ver)
+ {
+ sourcePath = System.IO.Path.Combine(oldRootDirectory, FILENAME);
+ destPath = System.IO.Path.Combine(newRootDirectory, FILENAME);
+ this.version = ver;
+ }
+
+ public void Migrate()
+ {
+ try
+ {
+ MigrateDirect();
+ MessageBox.Show("迁移" + FILENAME + "文件夹完成。");
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移" + FILENAME + $"过程中出现错误: {e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ void MigrateDirect()
+ {
+ FileUtils.CopyFolderContentsAndSubFolders(sourcePath, destPath);
+ }
+ }
+}
diff --git a/ViewModels/Utils/FileUtils.cs b/ViewModels/Utils/FileUtils.cs
new file mode 100644
index 0000000..b745df3
--- /dev/null
+++ b/ViewModels/Utils/FileUtils.cs
@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Drawing;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal static class FileUtils
+ {
+ ///
+ /// 检查文件夹路径是否合法
+ ///
+ public static bool IsFolderPathValid(string? folderPath, string folderName)
+ {
+ try
+ {
+ if (!Directory.Exists(folderPath))
+ {
+ MessageBox.Show(folderName + "文件夹不存在,请检查", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return false;
+ }
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(folderName + $"文件夹不存在,请检查,错误:{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// 检查指定文件夹是否存在指定的子文件夹,若不存在,创建一个
+ ///
+ public static bool checkFolderExist(string folderPath, string subFolderName)
+ {
+ string subFolderPath = Path.Combine(folderPath, subFolderName);
+ if (!Directory.Exists(subFolderPath))
+ {
+ return tryCreateFolder(subFolderName, subFolderPath);
+ }
+ else { return true; }
+ }
+
+ ///
+ /// 尝试在指定路径创建文件夹
+ ///
+ public static bool tryCreateFolder(string folderDirectory, string folderName)
+ {
+ try { Directory.CreateDirectory(folderDirectory); }
+ catch (Exception e)
+ {
+ MessageBox.Show($"错误:{e.Message},目标文件夹不存在" + folderName + "子文件夹,且创建失败,请检查", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return false;
+ }
+ if (!Directory.Exists(folderDirectory))
+ {
+ MessageBox.Show("目标文件夹不存在" + folderName + "子文件夹,且创建失败,请检查", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// 将源文件夹的所有文件拷贝至目标文件夹
+ ///
+ public static void CopyFolderContents(string sourceFolderPath, string destFolderPath)
+ {
+ string[] files = Directory.GetFiles(sourceFolderPath);
+ foreach (string file in files)
+ {
+ try
+ {
+ string targetFilePath = Path.Combine(destFolderPath, Path.GetFileName(file));
+ File.Copy(file, targetFilePath, true);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移" + file + $"过程中出现错误:{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ }
+
+ ///
+ /// 将源文件夹的所有文件(含子文件夹及其所有文件)拷贝至目标文件夹
+ ///
+ public static void CopyFolderContentsAndSubFolders(string sourceFolderPath, string destFolderPath)
+ {
+ string[] files = Directory.GetFiles(sourceFolderPath);
+ foreach (string file in files)
+ {
+ try
+ {
+ string targetFilePath = Path.Combine(destFolderPath, Path.GetFileName(file));
+ File.Copy(file, targetFilePath, true);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移" + file + $"过程中出现错误:{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+
+ string[] dirs = Directory.GetDirectories(sourceFolderPath);
+ foreach (string dir in dirs)
+ {
+ try
+ {
+ string targetFolderPath = Path.Combine(destFolderPath, Path.GetFileName(dir));
+ CopyFolderContentsAndSubFolders(dir, targetFolderPath);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移" + dir + $"过程中出现错误:{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ }
+
+ ///
+ /// 将源文件夹名为fileName的指定文件复制到目标文件夹
+ ///
+ public static void CopyFile(string sourceFolderPath, string destFolderPath, string fileName)
+ {
+ try
+ {
+ string SourcePath = sourceFolderPath;
+ string DestPath = destFolderPath;
+
+ if (!File.Exists(SourcePath))
+ {
+ MessageBox.Show("源文件夹不存在" + fileName + "子文件,请检查", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ else
+ {
+ System.IO.File.Copy(SourcePath, DestPath, true);
+ }
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show("迁移" + fileName + $"文件出现错误:{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ }
+}
diff --git a/ViewModels/Utils/StringUtils.cs b/ViewModels/Utils/StringUtils.cs
new file mode 100644
index 0000000..af388eb
--- /dev/null
+++ b/ViewModels/Utils/StringUtils.cs
@@ -0,0 +1,174 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Windows;
+using System.Windows.Forms;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal static class StringUtils
+ {
+ ///
+ /// 从指定路径的.js文件中提取JObject文件
+ ///
+ public static JObject getValidJson(string path)
+ {
+ try
+ {
+ string jsContent = File.ReadAllText(path);
+ string jsonContent;
+
+ int start = jsContent.IndexOf("{");
+ int end = jsContent.LastIndexOf("}") + 1;
+ if (end != -1)
+ {
+ jsonContent = jsContent.Substring(start, end - start);
+ }
+ else
+ {
+ jsonContent = jsContent.Substring(start);
+ }
+ JObject jsonObject = JObject.Parse(jsonContent);
+ return jsonObject;
+ }
+ catch (Exception e)
+ {
+ System.Windows.Forms.MessageBox.Show("从" + path + "中读取JSON时出错:" + $"{e.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ throw;
+ }
+ }
+
+ ///
+ /// 对于字符串str,找到${...}内部的内容,将其中的oldWord替换为newWord
+ ///
+ public static string ReplaceInBetweenCurlyBraces(string str, string oldWord, string newWord)
+ {
+ // 定义正则表达式模式,匹配 ${...} 中的内容
+ string pattern = @"\$\{([^}]+)\}";
+
+ // 使用 Regex.Replace 方法进行替换
+ string replacedStr = Regex.Replace(str, pattern, match =>
+ {
+ // 获取匹配的内容,去掉 ${ 和 }
+ string content = match.Groups[1].Value;
+ // 在匹配的内容中替换 oldWord 为 newWord
+ string modifiedContent = content.Replace(oldWord, newWord);
+ // 返回替换后的完整匹配字符串,包含 ${ 和 }
+ return "${" + modifiedContent + "}";
+ });
+
+ return replacedStr;
+ }
+
+ ///
+ /// 输入要加减的变量名varName,增减值value,符号+=或-=或=,返回一个执行该事件的JSON
+ ///
+ public static JObject getAddValueJson(string varName, string value, string o)
+ {
+ JObject adder = new JObject();
+ adder.Add(new JProperty("type", "setValue"));
+ adder.Add(new JProperty("name", varName));
+ if (o != "=") adder.Add(new JProperty("operator", o));
+ adder.Add(new JProperty("value", value));
+ return adder;
+ }
+
+ ///
+ /// 输入effect语句,返回一个包含所有事件的数组
+ ///
+ /// 参考: events.js/doEffect
+ public static JArray doEffect(string effects)
+ {
+ JArray newEffectsArray = new JArray();
+ string[] effectArr = effects.Split([';']);
+ foreach (string effect in effectArr)
+ {
+ string[] arr = effect.Split("+=");
+ JObject currEffectJson = getAddValueJson(arr[0], arr[1], "+=");
+ newEffectsArray.Add(currEffectJson);
+ }
+ return newEffectsArray;
+ }
+
+ public static string ReplaceOldNames(string input, Version version)
+ {
+ input = input.Replace("Jewel", "Gem");
+ input = input.Replace("ratio", "core.status.thisMap.ratio");
+ return input;
+ }
+
+ ///
+ /// 合并两个JObject,jobject2中的键值对覆盖jobject1的键值对(不论原先是否存在),返回jobject1。
+ ///
+ /// 要合并到并返回的对象。
+ /// 提供要合并内容的对象。
+ /// 合并后的targetJObject。
+ public static JObject MergeJObjects(JObject targetJObject, JObject sourceJObject)
+ {
+ foreach (JProperty prop in sourceJObject.Properties())
+ {
+ string key = prop.Name;
+ targetJObject[key] = prop.Value;
+ }
+ return targetJObject;
+ }
+
+ public static JArray CreateMatrix(int width, int height)
+ {
+ if (width <= 0 || height <= 0)
+ {
+ throw new ArgumentException("Width and height must be greater than 0.");
+ }
+
+ JArray matrix = new JArray();
+
+ for (int i = 0; i < height; i++)
+ {
+ JArray row = new JArray();
+ for (int j = 0; j < width; j++)
+ {
+ row.Add(0);
+ }
+ matrix.Add(row);
+ }
+
+ return matrix;
+ }
+
+ ///
+ /// 读取塔的地图尺寸,默认值为13
+ ///
+ public static int ReadMapWidth(string filePath)
+ {
+ int width;
+ try
+ {
+ string fileContent = File.ReadAllText(filePath);
+
+ string widthPattern = @"this\._WIDTH_\s*=\s*(\d+);";
+
+ Match widthMatch = Regex.Match(fileContent, widthPattern);
+
+ if (widthMatch.Success)
+ {
+ width = int.Parse(widthMatch.Groups[1].Value);
+ }
+ else
+ {
+ width = 13;
+ }
+ }
+ catch
+ {
+ width = 13;
+ }
+ return width;
+ }
+ }
+}
diff --git a/ViewModels/Utils/VersionUtils.cs b/ViewModels/Utils/VersionUtils.cs
new file mode 100644
index 0000000..43d88f2
--- /dev/null
+++ b/ViewModels/Utils/VersionUtils.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Eventing.Reader;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Xml.Linq;
+
+namespace H5MotaUpdate.ViewModels
+{
+ internal static class VersionUtils
+ {
+ public static bool IsValidVersion(string? version)
+ {
+ if (String.IsNullOrEmpty(version)) return false;
+ string[] segments = version.Split('.');
+ foreach (string segment in segments)
+ {
+ if (!int.TryParse(segment, out int part)) return false;
+ }
+ return true;
+ }
+
+ public static string GetVersion(string? folderPath)
+ {
+ if (folderPath == null) return "文件夹路径不合法";
+
+ string filePath = Path.Combine(folderPath, "main.js");
+ string version;
+ try
+ {
+ if (!File.Exists(filePath)) return "给定文件夹未找到文件main.js";
+
+ string fileContent = File.ReadAllText(filePath);
+
+ Regex versionRegex = new Regex(@"this\.version\s*=\s*['""](\d+(\.\d+)+)['""];");
+ Match match = versionRegex.Match(fileContent);
+
+ if (!match.Success) return "文件 main.js中未找到版本号!";
+ version = match.Groups[1].Value;
+ if (!IsValidVersion(version)) return "文件 main.js中未找到格式合法的版本号!";
+ }
+ catch (Exception ex)
+ {
+ return "读取版本号失败,原因: " + ex.Message;
+ }
+ return version;
+ }
+ }
+}