diff --git a/UXAssist/CHANGELOG.md b/UXAssist/CHANGELOG.md index f54e1ca..af1aa92 100644 --- a/UXAssist/CHANGELOG.md +++ b/UXAssist/CHANGELOG.md @@ -4,8 +4,11 @@ ## Changlog * 1.3.2 + + New feature: `Disable battle-related techs in Peace mode` + + New button: `Unlock all techs with metadata` + Add a checkbox to make union of results in starmap filter. + Fix some starmap vein/planet filter conditions. + + Fix a crash caused by `Re-initialize planet` in peace mode. + Fix compatibility with `NebulaMultiplayerMod`. * 1.3.1 + Fix an issue that some UI elements are hidden while hitting the newly added combobox on Starmap. @@ -294,8 +297,11 @@ ## 更新日志 * 1.3.2 - + 添加了一个勾选框用于对星图过滤器的结果进行并集操作 - + 修复了一些星图过滤条件 + + 新功能:`在和平模式下隐藏战斗相关科技` + + 新按钮:`使用元数据解锁所有科技` + + 在星图过滤器中添加复选框以合并结果 + + 修复了一些星图矿脉/行星过滤条件 + + 修复了在和平模式下`初始化本行星`导致的崩溃问题 + 修复了与`NebulaMultiplayerMod`的兼容性问题 * 1.3.1 + 修复了在星图上点击新增的下拉框时部分UI元素被隐藏的问题 diff --git a/UXAssist/Functions/PlanetFunctions.cs b/UXAssist/Functions/PlanetFunctions.cs index 7521c44..caeaf52 100644 --- a/UXAssist/Functions/PlanetFunctions.cs +++ b/UXAssist/Functions/PlanetFunctions.cs @@ -151,20 +151,25 @@ public static class PlanetFunctions if (warningPool[i].id == i && warningPool[i].factoryId == index) warningSystem.RemoveWarningData(warningPool[i].id); } - var hive = GameMain.spaceSector.dfHives[planet.star.index]; - var relays = hive.relays.buffer; - var astroId = planet.astroId; - for (var i = relays.Length - 1; i >= 0; i--) + var hives = GameMain.spaceSector?.dfHives; + if (hives != null) { - var relay = relays[i]; - if (relay.id != i) continue; - if (relay.targetAstroId == astroId || relay.searchAstroId == astroId) + var hive = hives[planet.star.index]; + var relays = hive?.relays?.buffer; + if (relays != null) { - relay.targetAstroId = 0; - relay.searchAstroId = 0; - if (relay.baseId > 0) - hive.relayNeutralizedCounter++; - relay.LeaveBase(); + var astroId = planet.astroId; + for (var i = relays.Length - 1; i >= 0; i--) + { + var relay = relays[i]; + if (relay != null && relay.id != i) continue; + if (relay.targetAstroId != astroId && relay.searchAstroId != astroId) continue; + relay.targetAstroId = 0; + relay.searchAstroId = 0; + if (relay.baseId > 0) + hive.relayNeutralizedCounter++; + relay.LeaveBase(); + } } } var isCombatMode = factory.gameData.gameDesc.isCombatMode; diff --git a/UXAssist/Functions/TechFunctions.cs b/UXAssist/Functions/TechFunctions.cs new file mode 100644 index 0000000..0cc271a --- /dev/null +++ b/UXAssist/Functions/TechFunctions.cs @@ -0,0 +1,313 @@ +namespace UXAssist.Functions; + +using System; +using System.Collections.Generic; +using Common; +using UnityEngine; + +public static class TechFunctions +{ + public static void Init() + { + I18N.Add("Do you want to use metadata to buyout the following tech?", "Do you want to use metadata to buyout the following tech?", "要使用元数据买断以下科技吗?"); + I18N.Add("The following is the required metadata for buyout:", "The following is the required metadata for buyout:", "以下是买断所需元数据:"); + I18N.Add("Batch buyout tech", "Batch buyout tech", "批量买断科技"); + } + + public static void GenerateTechListWithPrerequisites(GameHistoryData history, int techId, List techIdList) + { + var techProto = LDB.techs.Select(techId); + if (techProto == null || !techProto.Published) return; + var flag = true; + for (var i = 0; i < 2; i++) + { + foreach (var preTechId in (i == 1 ? techProto.PreTechsImplicit : techProto.PreTechs)) + { + if (!history.techStates.ContainsKey(preTechId) || history.techStates[preTechId].unlocked) continue; + if (history.techStates[preTechId].maxLevel > history.techStates[preTechId].curLevel) + { + flag = false; + } + GenerateTechListWithPrerequisites(history, preTechId, techIdList); + } + } + if (history.techStates.ContainsKey(techId) && !history.techStates[techId].unlocked && flag) + { + techIdList.Add(techId); + } + } + + private static void CheckTechUnlockProperties(GameHistoryData history, TechProto techProto, SortedList properties, List> techList, int maxLevel = 10000, bool withPrerequisites = true) + { + var techStates = history.techStates; + var techID = techProto.ID; + if (techStates == null || !techStates.TryGetValue(techID, out var value)) return; + if (value.unlocked) return; + + var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel); + if (withPrerequisites) + { + foreach (var preid in techProto.PreTechs) + { + var preProto = LDB.techs.Select(preid); + if (preProto != null) + CheckTechUnlockProperties(history, preProto, properties, techList, techProto.PreTechsMax ? 10000 : preProto.Level, true); + } + foreach (var preid in techProto.PreTechsImplicit) + { + var preProto = LDB.techs.Select(preid); + if (preProto != null) + CheckTechUnlockProperties(history, preProto, properties, techList, techProto.PreTechsMax ? 10000 : preProto.Level, true); + } + } + + if (value.curLevel < techProto.Level) value.curLevel = techProto.Level; + techList.Add(new Tuple(techProto, value.curLevel, techProto.Level)); + while (value.curLevel <= maxLvl) + { + if (techProto.PropertyOverrideItemArray != null) + { + var propertyOverrideItemArray = techProto.PropertyOverrideItemArray; + for (var i = 0; i < propertyOverrideItemArray.Length; i++) + { + var id = propertyOverrideItemArray[i].id; + var count = (float)propertyOverrideItemArray[i].count; + var ratio = Mathf.Clamp01((float)((double)value.hashUploaded / value.hashNeeded)); + var consume = Mathf.CeilToInt(count * (1f - ratio)); + if (properties.TryGetValue(id, out var existingCount)) + properties[id] = existingCount + consume; + else + properties.Add(id, consume); + } + } + else + { + for (var i = 0; i < techProto.itemArray.Length; i++) + { + var id = techProto.itemArray[i].ID; + var consume = (int)(techProto.ItemPoints[i] * (value.hashNeeded - value.hashUploaded) / 3600L); + if (properties.TryGetValue(id, out var existingCount)) + properties[id] = existingCount + consume; + else + properties.Add(id, consume); + } + } + value.curLevel++; + value.hashUploaded = 0; + value.hashNeeded = techProto.GetHashNeeded(value.curLevel); + } + } + + private static int UnlockTechImpl(GameHistoryData history, TechProto techProto, int maxLevel = 10000, bool withPrerequisites = true) + { + var techStates = history.techStates; + var techID = techProto.ID; + if (techStates == null || !techStates.TryGetValue(techID, out var value)) + { + return -1; + } + + if (value.unlocked) + { + return -1; + } + + var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel); + + if (withPrerequisites) + { + foreach (var preid in techProto.PreTechs) + { + var preProto = LDB.techs.Select(preid); + if (preProto != null) + UnlockTechImpl(history, preProto, techProto.PreTechsMax ? 10000 : preProto.Level, true); + } + foreach (var preid in techProto.PreTechsImplicit) + { + var preProto = LDB.techs.Select(preid); + if (preProto != null) + UnlockTechImpl(history, preProto, techProto.PreTechsMax ? 10000 : preProto.Level, true); + } + } + + if (value.curLevel < techProto.Level) value.curLevel = techProto.Level; + while (value.curLevel <= maxLvl) + { + if (value.curLevel == 0) + { + foreach (var recipe in techProto.UnlockRecipes) + { + history.UnlockRecipe(recipe); + } + } + + for (var j = 0; j < techProto.UnlockFunctions.Length; j++) + { + history.UnlockTechFunction(techProto.UnlockFunctions[j], techProto.UnlockValues[j], value.curLevel); + } + + for (var k = 0; k < techProto.AddItems.Length; k++) + { + history.GainTechAwards(techProto.AddItems[k], techProto.AddItemCounts[k]); + } + + value.curLevel++; + } + + value.unlocked = maxLvl >= value.maxLevel; + value.curLevel = value.unlocked ? maxLvl : maxLvl + 1; + value.hashNeeded = techProto.GetHashNeeded(value.curLevel); + value.hashUploaded = value.unlocked ? value.hashNeeded : 0; + techStates[techID] = value; + return maxLvl; + } + + private static bool UnlockTechImmediately(TechProto techProto, int maxLevel = 10000, bool withPrerequisites = true) + { + if (techProto == null) return false; + var history = GameMain.history; + var ulvl = UnlockTechImpl(history, techProto, maxLevel, withPrerequisites); + if (ulvl < 0) return false; + history.RegFeatureKey(1000100); + history.NotifyTechUnlock(techProto.ID, ulvl); + return true; + } + + public static void UnlockAllProtoWithMetadataAndPrompt() + { + var history = GameMain.history; + List techProtos = []; + foreach (var techProto in LDB.techs.dataArray) + { + if (techProto.Published && !techProto.IsObsolete && !techProto.IsHiddenTech && !history.TechUnlocked(techProto.ID) && (techProto.MaxLevel <= techProto.Level || techProto.MaxLevel <= 16) && (techProto.ID is (< 3301 or > 3309) and (< 3601 or > 3609) and (< 3901 or > 3909)) && techProto.ID != 1508) + { + techProtos.Add(techProto); + } + } + UnlockProtoWithMetadataAndPrompt([.. techProtos], false); + } + + public static void UnlockProtoWithMetadataAndPrompt(TechProto[] techProtos, bool withPrerequisites = true) + { + var techList = new List>(); + var properties = new SortedList(); + var history = GameMain.history; + foreach (var techProto in techProtos) + { + CheckTechUnlockProperties(history, techProto, properties, techList, -1, withPrerequisites); + } + var propertySystem = DSPGame.propertySystem; + var clusterSeedKey = history.gameData.GetClusterSeedKey(); + var enough = true; + foreach (var consumption in properties) + { + if (propertySystem.GetItemAvaliableProperty(clusterSeedKey, consumption.Key) >= consumption.Value) continue; + enough = false; + break; + } + if (!enough) + { + UIRealtimeTip.Popup("元数据不足".Translate(), true, 0); + return; + } + + if (!history.hasUsedPropertyBanAchievement) + { + UIMessageBox.Show("初次使用元数据标题".Translate(), "初次使用元数据描述".Translate(), "取消".Translate(), "确定".Translate(), 2, null, DoUnlockCalculatedTechs); + return; + } + + DoUnlockCalculatedTechs(); + return; + + void DoUnlockCalculatedTechs() + { + if (techList.Count <= 1) + { + UnlockWithPropertiesImmediately(); + return; + } + var msg = "Do you want to use metadata to buyout the following tech?".Translate(); + + if (techList.Count <= 10) + { + foreach (var tuple in techList) + { + AddToMsg(ref msg, tuple); + } + } + else + { + for (var i = 0; i < 5; i++) + { + AddToMsg(ref msg, techList[i]); + } + msg += "\n ..."; + for (var i = techList.Count - 5; i < techList.Count; i++) + { + AddToMsg(ref msg, techList[i]); + } + } + + msg += "\n\n"; + msg += "The following is the required metadata for buyout:".Translate(); + foreach (var consumption in properties) + { + if (consumption.Value <= 0) continue; + msg += $"\n {LDB.items.Select(consumption.Key).propertyName}x{consumption.Value}"; + } + UIMessageBox.Show("Batch buyout tech".Translate(), msg, "取消".Translate(), "确定".Translate(), 2, null, UnlockWithPropertiesImmediately); + return; + + void AddToMsg(ref string str, Tuple tuple) + { + if (tuple.Item2 == tuple.Item3) + { + if (tuple.Item2 <= 0) + str += $"\n {tuple.Item1.name}"; + else + str += $"\n {tuple.Item1.name}{"杠等级".Translate()}{tuple.Item2}"; + } + else + str += $"\n {tuple.Item1.name}{"杠等级".Translate()}{tuple.Item2}->{tuple.Item3}"; + } + } + + void UnlockWithPropertiesImmediately() + { + var mainPlayer = GameMain.mainPlayer; + foreach (var consumption in properties) + { + if (consumption.Value <= 0) continue; + propertySystem.AddItemConsumption(clusterSeedKey, consumption.Key, consumption.Value); + history.AddPropertyItemConsumption(consumption.Key, consumption.Value, true); + mainPlayer.mecha.AddProductionStat(consumption.Key, consumption.Value, mainPlayer.nearestFactory); + mainPlayer.mecha.AddConsumptionStat(consumption.Key, consumption.Value, mainPlayer.nearestFactory); + } + foreach (var techProto in techProtos) + { + UnlockTechImmediately(techProto, -1, withPrerequisites); + } + history.VarifyTechQueue(); + if (history.currentTech != history.techQueue[0]) + { + history.currentTech = history.techQueue[0]; + } + } + } + + public static void RemoveCargoStackingTechs() + { + var history = GameMain.data?.history; + if (history == null) return; + history.inserterStackCountObsolete = 1; + for (var id = 3301; id <= 3305; id++) + { + history.techStates.TryGetValue(id, out var state); + if (!state.unlocked) continue; + state.unlocked = false; + state.hashUploaded = 0; + history.techStates[id] = state; + } + } +} diff --git a/UXAssist/Patches/TechPatch.cs b/UXAssist/Patches/TechPatch.cs index 2ef6d49..07c4f45 100644 --- a/UXAssist/Patches/TechPatch.cs +++ b/UXAssist/Patches/TechPatch.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection.Emit; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; +using UnityEngine.UI; using UXAssist.Common; namespace UXAssist.Patches; @@ -11,47 +13,54 @@ namespace UXAssist.Patches; public static class TechPatch { public static ConfigEntry SorterCargoStackingEnabled; + public static ConfigEntry DisableBattleRelatedTechsInPeaceModeEnabled; public static ConfigEntry BatchBuyoutTechEnabled; - + public static void Init() { I18N.Add("分拣器运货量", "Sorter Mk.III cargo stacking : ", "极速分拣器每次可运送 "); SorterCargoStackingEnabled.SettingChanged += (_, _) => SorterCargoStacking.Enable(SorterCargoStackingEnabled.Value); + DisableBattleRelatedTechsInPeaceModeEnabled.SettingChanged += (_, _) => DisableBattleRelatedTechsInPeaceMode.Enable(DisableBattleRelatedTechsInPeaceModeEnabled.Value); BatchBuyoutTechEnabled.SettingChanged += (_, _) => BatchBuyoutTech.Enable(BatchBuyoutTechEnabled.Value); } public static void Start() { SorterCargoStacking.Enable(SorterCargoStackingEnabled.Value); + DisableBattleRelatedTechsInPeaceMode.Enable(DisableBattleRelatedTechsInPeaceModeEnabled.Value); BatchBuyoutTech.Enable(BatchBuyoutTechEnabled.Value); } public static void Uninit() { BatchBuyoutTech.Enable(false); + DisableBattleRelatedTechsInPeaceMode.Enable(false); SorterCargoStacking.Enable(false); } - - private class SorterCargoStacking: PatchImpl + + private class SorterCargoStacking { private static bool _protoPatched; - - protected override void OnEnable() + + public static void Enable(bool enable) { - TryPatchProto(true); - GameLogic.OnDataLoaded += VFPreload_InvokeOnLoadWorkEnded_Postfix; - } - - protected override void OnDisable() - { - GameLogic.OnDataLoaded -= VFPreload_InvokeOnLoadWorkEnded_Postfix; - TryPatchProto(false); + if (enable) + { + TryPatchProto(true); + GameLogic.OnDataLoaded += VFPreload_InvokeOnLoadWorkEnded_Postfix; + } + else + { + GameLogic.OnDataLoaded -= VFPreload_InvokeOnLoadWorkEnded_Postfix; + TryPatchProto(false); + } } private static void TryPatchProto(bool on) { + if (DSPGame.IsMenuDemo) return; var techs = LDB.techs; - if (techs == null || techs.dataArray == null || techs.dataArray.Length == 0) return; + if (techs == null || techs?.dataArray == null || techs.dataArray.Length == 0) return; if (on) { var delim = -26.0f; @@ -114,162 +123,145 @@ public static class TechPatch } } + private static class DisableBattleRelatedTechsInPeaceMode + { + private static bool _protoPatched; + private static HashSet _techsToDisableSet; + private static Dictionary _originTechPosts = []; + + public static void Enable(bool enable) + { + if (_techsToDisableSet == null) + { + (int, int)[] techListToDisable = + [ + // Combustible Unit, Explosive Unit, Crystal Explosive Unit + // 燃烧单元,爆破单元,晶石爆破单元 + (1802, 1804), + // Implosion Cannon + // 聚爆加农炮 + (1807, 1807), + // Signal Tower, Planetary Defense System, Jammer Tower, Plasma Turret, Titanium Ammo Box, Superalloy Ammo Box, High-Explosive Shell Set, Supersonic Missile Set, Crystal Shell Set, Gravity Missile Set, Antimatter Capsule, Precision Drone, Prototype, Attack Drone, Corvette, Destroyer, Suppressing Capsule, EM Capsule Mk.III + // 信号塔, 行星防御系统, 干扰塔, 磁化电浆炮, 钛化弹箱, 超合金弹箱, 高爆炮弹组, 超音速导弹组, 晶石炮弹组, 引力导弹组, 反物质胶囊, 地面战斗机-A型, 地面战斗机-E型, 地面战斗机-F型, 太空战斗机-A型, 太空战斗机-F型, 电磁胶囊II, 电磁胶囊III + (1809, 1825), + // Auto Reconstruction Marking + // 自动标记重建 + (2951, 2956), + // Energy Shield + // 能量护盾 + (2801, 2807), + // Kinetic Weapon Damage + // 动能武器伤害 + (5001, 5006), + // Energy Weapon Damage + // 能量武器伤害 + (5101, 5106), + // Explosive Weapon Damage + // 爆炸武器伤害 + (5201, 5206), + // Combat Drone Damage + // 战斗无人机伤害 + (5301, 5305), + // Combat Drone Attack Speed + // 战斗无人机攻击速度 + (5401, 5405), + // Combat Drone Engine + // 战斗无人机引擎 + (5601, 5605), + // Combat Drone Durability + // 战斗无人机耐久 + (5701, 5705), + // Ground Squadron Expansion + // 地面编队扩容 + (5801, 5807), + // Space Fleet Expansion + // 太空编队扩容 + (5901, 5907), + // Enhanced Structure + // 结构强化 + (6001, 6006), + // Planetary Shield + // 行星护盾 + (6101, 6106), + ]; + _techsToDisableSet = [.. techListToDisable.SelectMany(t => Enumerable.Range(t.Item1, t.Item2 - t.Item1 + 1))]; + } + if (enable) + { + if (DSPGame.GameDesc != null) + TryPatchProto(DSPGame.GameDesc.isPeaceMode); + GameLogic.OnGameBegin += OnGameBegin; + } + else + { + GameLogic.OnGameBegin -= OnGameBegin; + TryPatchProto(false); + } + } + + private static void OnGameBegin() + { + TryPatchProto(DSPGame.GameDesc.isPeaceMode); + } + + private static void TryPatchProto(bool on) + { + if (DSPGame.IsMenuDemo || on == _protoPatched) return; + var techs = LDB.techs; + if (techs?.dataArray == null || techs.dataArray.Length == 0) return; + if (on) + { + foreach (var tp in techs.dataArray) + { + if (_techsToDisableSet.Contains(tp.ID)) + { + tp.IsObsolete = true; + } + var postTechs = tp.postTechArray; + for (var i = postTechs.Length - 1; i >= 0; i--) + { + if (_techsToDisableSet.Contains(postTechs[i].ID)) + { + _originTechPosts[tp.ID] = postTechs; + tp.postTechArray = [.. postTechs.Where(p => !_techsToDisableSet.Contains(p.ID))]; + break; + } + } + } + _protoPatched = true; + } + else + { + foreach (var tp in techs.dataArray) + { + if (_techsToDisableSet.Contains(tp.ID)) + { + tp.IsObsolete = false; + } + if (_originTechPosts.TryGetValue(tp.ID, out var postTechs)) + { + tp.postTechArray = postTechs; + } + } + _originTechPosts.Clear(); + _protoPatched = false; + } + foreach (var item in UIRoot.instance.uiGame.techTree.nodes) + { + var node = item.Value; + foreach (var arrow in node.arrows) + { + arrow.gameObject.SetActive(false); + } + var active = node.techProto.postTechArray.Length > 0; + node.connGroup.gameObject.SetActive(active); + node.connImages = active ? node.connGroup.GetComponentsInChildren(true) : []; + } + } + } + private class BatchBuyoutTech: PatchImpl { - private static void GenerateTechList(GameHistoryData history, int techId, List techIdList) - { - var techProto = LDB.techs.Select(techId); - if (techProto == null || !techProto.Published) return; - var flag = true; - for (var i = 0; i < 2; i++) - { - var array = techProto.PreTechs; - if (i == 1) - { - array = techProto.PreTechsImplicit; - } - for (var j = 0; j < array.Length; j++) - { - if (!history.techStates.ContainsKey(array[j]) || history.techStates[array[j]].unlocked) continue; - if (history.techStates[array[j]].maxLevel > history.techStates[array[j]].curLevel) - { - flag = false; - } - GenerateTechList(history, array[j], techIdList); - } - } - if (history.techStates.ContainsKey(techId) && !history.techStates[techId].unlocked && flag) - { - techIdList.Add(techId); - } - } - - - private static void CheckTechUnlockProperties(GameHistoryData history, TechProto techProto, int[] properties, List> techList, int maxLevel = 10000) - { - var techStates = history.techStates; - var techID = techProto.ID; - if (techStates == null || !techStates.TryGetValue(techID, out var value)) return; - if (value.unlocked) return; - - var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel); - - foreach (var preid in techProto.PreTechs) - { - var preProto = LDB.techs.Select(preid); - if (preProto != null) - CheckTechUnlockProperties(history, preProto, properties, techList, techProto.PreTechsMax ? 10000 : preProto.Level); - } - foreach (var preid in techProto.PreTechsImplicit) - { - var preProto = LDB.techs.Select(preid); - if (preProto != null) - CheckTechUnlockProperties(history, preProto, properties, techList, techProto.PreTechsMax ? 10000 : preProto.Level); - } - - if (value.curLevel < techProto.Level) value.curLevel = techProto.Level; - techList.Add(new Tuple(techProto, value.curLevel, techProto.Level)); - while (value.curLevel <= maxLvl) - { - if (techProto.PropertyOverrideItemArray != null) - { - var propertyOverrideItemArray = techProto.PropertyOverrideItemArray; - for (var i = 0; i < propertyOverrideItemArray.Length; i++) - { - var id = propertyOverrideItemArray[i].id; - var count = (float)propertyOverrideItemArray[i].count; - var ratio = Mathf.Clamp01((float)((double)value.hashUploaded / value.hashNeeded)); - var consume = Mathf.CeilToInt(count * (1f - ratio)); - properties[id - 6001] += consume; - } - } - else - { - for (var j = 0; j < techProto.itemArray.Length; j++) - { - var id = techProto.itemArray[j].ID; - var consume = (int)(techProto.ItemPoints[j] * (value.hashNeeded - value.hashUploaded) / 3600L); - properties[id - 6001] += consume; - } - } - value.curLevel++; - value.hashUploaded = 0; - value.hashNeeded = techProto.GetHashNeeded(value.curLevel); - } - } - - private static int UnlockTechRecursiveImpl(GameHistoryData history, TechProto techProto, int maxLevel = 10000) - { - var techStates = history.techStates; - var techID = techProto.ID; - if (techStates == null || !techStates.TryGetValue(techID, out var value)) - { - return -1; - } - - if (value.unlocked) - { - return -1; - } - - var maxLvl = Math.Min(maxLevel < 0 ? value.curLevel - maxLevel - 1 : maxLevel, value.maxLevel); - - foreach (var preid in techProto.PreTechs) - { - var preProto = LDB.techs.Select(preid); - if (preProto != null) - UnlockTechRecursiveImpl(history, preProto, techProto.PreTechsMax ? 10000 : preProto.Level); - } - foreach (var preid in techProto.PreTechsImplicit) - { - var preProto = LDB.techs.Select(preid); - if (preProto != null) - UnlockTechRecursiveImpl(history, preProto, techProto.PreTechsMax ? 10000 : preProto.Level); - } - - if (value.curLevel < techProto.Level) value.curLevel = techProto.Level; - while (value.curLevel <= maxLvl) - { - if (value.curLevel == 0) - { - foreach (var recipe in techProto.UnlockRecipes) - { - history.UnlockRecipe(recipe); - } - } - - for (var j = 0; j < techProto.UnlockFunctions.Length; j++) - { - history.UnlockTechFunction(techProto.UnlockFunctions[j], techProto.UnlockValues[j], value.curLevel); - } - - for (var k = 0; k < techProto.AddItems.Length; k++) - { - history.GainTechAwards(techProto.AddItems[k], techProto.AddItemCounts[k]); - } - - value.curLevel++; - } - - value.unlocked = maxLvl >= value.maxLevel; - value.curLevel = value.unlocked ? maxLvl : maxLvl + 1; - value.hashNeeded = techProto.GetHashNeeded(value.curLevel); - value.hashUploaded = value.unlocked ? value.hashNeeded : 0; - techStates[techID] = value; - return maxLvl; - } - - private static bool UnlockTechRecursive(TechProto techProto, int maxLevel = 10000) - { - if (techProto == null) return false; - var history = GameMain.history; - var ulvl = UnlockTechRecursiveImpl(history, techProto, maxLevel); - if (ulvl < 0) return false; - history.RegFeatureKey(1000100); - history.NotifyTechUnlock(techProto.ID, ulvl); - return true; - } - [HarmonyTranspiler] [HarmonyPatch(typeof(UITechNode), nameof(UITechNode.UpdateInfoDynamic))] private static IEnumerable UITechNode_UpdateInfoDynamic_Transpiler(IEnumerable instructions) @@ -285,7 +277,7 @@ public static class TechPatch ); return matcher.InstructionEnumeration(); } - + [HarmonyPrefix] [HarmonyPatch(typeof(UITechNode), nameof(UITechNode.OnBuyoutButtonClick))] private static bool UITechNode_OnBuyoutButtonClick_Prefix(UITechNode __instance) @@ -296,109 +288,8 @@ public static class TechPatch } var techProto = __instance.techProto; if (techProto == null) return false; - var properties = new int[6]; - List> techList = new(); - var history = GameMain.history; - var maxLevel = -1; - CheckTechUnlockProperties(history, techProto, properties, techList, maxLevel); - var propertySystem = DSPGame.propertySystem; - var clusterSeedKey = history.gameData.GetClusterSeedKey(); - var enough = true; - for (var i = 0; i < 6; i++) - { - if (propertySystem.GetItemAvaliableProperty(clusterSeedKey, 6001 + i) >= properties[i]) continue; - enough = false; - break; - } - if (!enough) - { - UIRealtimeTip.Popup("元数据不足".Translate()); - return false; - } - - if (!history.hasUsedPropertyBanAchievement) - { - UIMessageBox.Show("初次使用元数据标题".Translate(), "初次使用元数据描述".Translate(), "取消".Translate(), "确定".Translate(), 2, null, DoUnlockFunc); - return false; - } - - DoUnlockFunc(); + Functions.TechFunctions.UnlockProtoWithMetadataAndPrompt([techProto], true); return false; - - void DoUnlockFunc() - { - if (techList.Count <= 1) - { - DoUnlockFuncInternal(); - return; - } - var msg = "要使用元数据买断以下科技吗?"; - - if (techList.Count <= 10) - { - foreach (var tuple in techList) - { - AddToMsg(ref msg, tuple); - } - } - else - { - for (var i = 0; i < 5; i++) - { - AddToMsg(ref msg, techList[i]); - } - msg += " ...\n"; - for (var i = techList.Count - 5; i < techList.Count; i++) - { - AddToMsg(ref msg, techList[i]); - } - } - - msg += "\n\n"; - msg += "以下是买断所需元数据:"; - for (var i = 0; i < 6; i++) - { - var itemCount = properties[i]; - if (itemCount <= 0) continue; - msg += $"\n {LDB.items.Select(6001 + i).propertyName}x{itemCount}"; - } - UIMessageBox.Show("批量买断科技", msg, "取消".Translate(), "确定".Translate(), 2, null, DoUnlockFuncInternal); - return; - - void AddToMsg(ref string str, Tuple tuple) - { - if (tuple.Item2 == tuple.Item3) - { - if (tuple.Item2 <= 0) - str += $"\n {tuple.Item1.name}"; - else - str += $"\n {tuple.Item1.name}{"杠等级".Translate()}{tuple.Item2}"; - } - else - str += $"\n {tuple.Item1.name}{"杠等级".Translate()}{tuple.Item2}->{tuple.Item3}"; - } - } - - void DoUnlockFuncInternal() - { - var mainPlayer = GameMain.mainPlayer; - for (var i = 0; i < 6; i++) - { - var itemCount = properties[i]; - if (itemCount <= 0) continue; - var itemId = 6001 + i; - propertySystem.AddItemConsumption(clusterSeedKey, itemId, itemCount); - history.AddPropertyItemConsumption(itemId, itemCount, true); - mainPlayer.mecha.AddProductionStat(itemId, itemCount, mainPlayer.nearestFactory); - mainPlayer.mecha.AddConsumptionStat(itemId, itemCount, mainPlayer.nearestFactory); - } - UnlockTechRecursive(__instance.techProto, maxLevel); - history.VarifyTechQueue(); - if (history.currentTech != history.techQueue[0]) - { - history.currentTech = history.techQueue[0]; - } - } } } } diff --git a/UXAssist/UIConfigWindow.cs b/UXAssist/UIConfigWindow.cs index 67ac8ed..affd40f 100644 --- a/UXAssist/UIConfigWindow.cs +++ b/UXAssist/UIConfigWindow.cs @@ -149,8 +149,10 @@ public static class UIConfigWindow I18N.Add("Dismantle selected layer Confirm", "This operation will dismantle selected layer, are you sure?", "此操作将会拆除选中的戴森壳,确定吗?"); I18N.Add("Auto Fast Build Speed Multiplier", "Auto Fast Build Speed Multiplier", "自动快速建造速度倍率"); I18N.Add("Restore upgrades of \"Sorter Cargo Stacking\" on panel", "Restore upgrades of \"Sorter Cargo Stacking\" on panel", "在升级面板上恢复\"分拣器货物叠加\"的升级"); + I18N.Add("Disable battle-related techs in Peace mode", "Disable battle-related techs in Peace mode", "在和平模式下隐藏战斗相关科技"); I18N.Add("Buy out techs with their prerequisites", "Buy out techs with their prerequisites", "购买科技也同时购买所有前置科技"); I18N.Add("Set \"Sorter Cargo Stacking\" to unresearched state", "Set \"Sorter Cargo Stacking\" to unresearched state", "将\"分拣器货物叠加\"设为未研究状态"); + I18N.Add("Unlock all techs with metadata", "Unlock all techs with metadata", "使用元数据解锁所有科技"); I18N.Add("Open Dark Fog Communicator", "Open Dark Fog Communicator", "打开黑雾通讯器"); I18N.Apply(); MyConfigWindow.OnUICreated += CreateUI; @@ -727,20 +729,11 @@ public static class UIConfigWindow y += 36f; wnd.AddCheckBox(x, y, tab6, TechPatch.SorterCargoStackingEnabled, "Restore upgrades of \"Sorter Cargo Stacking\" on panel"); y += 36f; - wnd.AddButton(x, y, 300f, tab6, "Set \"Sorter Cargo Stacking\" to unresearched state", 16, "button-remove-cargo-stacking", () => - { - var history = GameMain.data?.history; - if (history == null) return; - history.inserterStackCountObsolete = 1; - for (var id = 3301; id <= 3305; id++) - { - history.techStates.TryGetValue(id, out var state); - if (!state.unlocked) continue; - state.unlocked = false; - state.hashUploaded = 0; - history.techStates[id] = state; - } - }); + wnd.AddCheckBox(x, y, tab6, TechPatch.DisableBattleRelatedTechsInPeaceModeEnabled, "Disable battle-related techs in Peace mode"); + y += 36f; + wnd.AddButton(x, y, 300f, tab6, "Set \"Sorter Cargo Stacking\" to unresearched state", 16, "button-remove-cargo-stacking", TechFunctions.RemoveCargoStackingTechs); + y += 36f; + wnd.AddButton(x, y, 300f, tab6, "Unlock all techs with metadata", 16, "button-unlock-all-techs-with-metadata", TechFunctions.UnlockAllProtoWithMetadataAndPrompt); y += 36f; y += 36f; wnd.AddButton(x, y, 300f, tab6, "Open Dark Fog Communicator", 16, "button-open-df-communicator", () => diff --git a/UXAssist/UXAssist.cs b/UXAssist/UXAssist.cs index 62127ea..efe32b8 100644 --- a/UXAssist/UXAssist.cs +++ b/UXAssist/UXAssist.cs @@ -188,6 +188,8 @@ public class UXAssist : BaseUnityPlugin, IModCanSave PlayerPatch.DistanceToWarp = Config.Bind("Player", "DistanceToWarp", 5.0, "Distance to warp (in AU)"); TechPatch.SorterCargoStackingEnabled = Config.Bind("Tech", "SorterCargoStacking", false, "Restore upgrades of `Sorter Cargo Stacking` on panel"); + TechPatch.DisableBattleRelatedTechsInPeaceModeEnabled = Config.Bind("Tech", "DisableBattleRelatedTechsInPeaceMode", false, + "Disable battle-related techs in Peace mode"); TechPatch.BatchBuyoutTechEnabled = Config.Bind("Tech", "BatchBuyoutTech", false, "Can buy out techs with their prerequisites"); DysonSpherePatch.StopEjectOnNodeCompleteEnabled = Config.Bind("DysonSphere", "StopEjectOnNodeComplete", false,