From 66b3e174b6e545e65ce2af5a290fa9c7c3bf1d3f Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Thu, 22 May 2025 19:31:10 +0800 Subject: [PATCH] work in progress --- CheatEnabler/CheatEnabler.cs | 2 + .../Functions/DysonSphereFunctions.cs | 140 ++++++++++++++++-- CheatEnabler/Patches/DysonSpherePatch.cs | 1 - CheatEnabler/UIConfigWindow.cs | 35 ++++- 4 files changed, 164 insertions(+), 14 deletions(-) diff --git a/CheatEnabler/CheatEnabler.cs b/CheatEnabler/CheatEnabler.cs index 9764d07..991f2ed 100644 --- a/CheatEnabler/CheatEnabler.cs +++ b/CheatEnabler/CheatEnabler.cs @@ -85,6 +85,8 @@ public class CheatEnabler : BaseUnityPlugin "Unlock Dyson Sphere max orbit radius"); DysonSpherePatch.UnlockMaxOrbitRadiusValue = Config.Bind("DysonSphere", "MaxOrbitRadiusValue", 10_000_000f, "Unlocked Dyson Sphere max orbit radius value"); + Functions.DysonSphereFunctions.RetainShellsCount = Config.Bind("DysonSphere", "RetainShellsCount", 2048, + "Retain dyson shells count"); CombatPatch.MechaInvincibleEnabled = Config.Bind("Battle", "MechaInvincible", false, "Mecha and Drones/Fleets invincible"); CombatPatch.BuildingsInvincibleEnabled = Config.Bind("Battle", "BuildingsInvincible", false, diff --git a/CheatEnabler/Functions/DysonSphereFunctions.cs b/CheatEnabler/Functions/DysonSphereFunctions.cs index d6f278a..b96fd3d 100644 --- a/CheatEnabler/Functions/DysonSphereFunctions.cs +++ b/CheatEnabler/Functions/DysonSphereFunctions.cs @@ -1,18 +1,17 @@ using System; using System.Collections.Generic; -using System.Drawing.Text; using System.Linq; -using System.Runtime.InteropServices; -using CommonAPI; +using BepInEx.Configuration; using HarmonyLib; using UnityEngine; -using UnityEngine.Experimental.UIElements; using UXAssist.Common; namespace CheatEnabler.Functions; public static class DysonSphereFunctions { + public static ConfigEntry RetainShellsCount; + public static void Init() { I18N.Add("You are not in any system.", "You are not in any system.", "你不在任何星系中"); @@ -642,15 +641,14 @@ public static class DysonSphereFunctions shell.nodes.Add(dysonNode); shell.frames.Add(dysonFrame); } - if (!shell.MyGenerateGeometry()) + if (!shell.MyGenerateGeometry() || DysonShell.s_vmap.Count < 3000) { CheatEnabler.Logger.LogDebug($"{shellId} DysonShell.s_vmap.Count: {DysonShell.s_vmap.Count}"); shell.Free(); layer.shellPool[shellId] = null; - int[] array = layer.shellRecycle; int recycleIndex = layer.shellRecycleCursor; layer.shellRecycleCursor = recycleIndex + 1; - array[recycleIndex] = shellId; + layer.shellRecycle[recycleIndex] = shellId; return 0; } CheatEnabler.Logger.LogDebug($"{shellId} DysonShell.s_vmap.Count: {DysonShell.s_vmap.Count}"); @@ -741,7 +739,6 @@ public static class DysonSphereFunctions int nodeCount = layer.nodeCursor; List supposedShells = []; - var limitRadius = layer.orbitRadius * layer.orbitRadius * 2.5f; for (var j = 1; j < nodeCount; j++) { var nodeA = layer.nodePool[j]; @@ -750,13 +747,10 @@ public static class DysonSphereFunctions { var nodeB = layer.nodePool[k]; if (nodeB == null || nodeB.id != k) continue; - if ((nodeA.pos - nodeB.pos).sqrMagnitude > limitRadius) continue; for (var l = k + 1; l < nodeCount; l++) { var nodeC = layer.nodePool[l]; if (nodeC == null || nodeC.id != l) continue; - if ((nodeA.pos - nodeC.pos).sqrMagnitude > limitRadius) continue; - if ((nodeB.pos - nodeC.pos).sqrMagnitude > limitRadius) continue; var area = Vector3.Cross(nodeB.pos - nodeA.pos, nodeC.pos - nodeA.pos).sqrMagnitude; supposedShells.Add(new SupposedShell { nodeA = nodeA, nodeB = nodeB, nodeC = nodeC, area = area }); } @@ -786,7 +780,7 @@ public static class DysonSphereFunctions } } var total = 0; - for (var j = 0; j < count && total < 2048; j++) + for (var j = 0; j < count && total < 8192; j++) { var shell = supposedShells[j]; if (availableShells.TryGetValue((shell.nodeA.id, shell.nodeB.id, shell.nodeC.id), out _)) continue; @@ -794,6 +788,8 @@ public static class DysonSphereFunctions DysonNode[] nodes = [shell.nodeA, shell.nodeB, shell.nodeC]; if (layer.QuickAddDysonShell(0, nodes, frames) <= 0) continue; unusedFrameIds.Remove(frames[0].id); + unusedFrameIds.Remove(frames[1].id); + unusedFrameIds.Remove(frames[2].id); dirtyNodes.Add(shell.nodeA); dirtyNodes.Add(shell.nodeB); dirtyNodes.Add(shell.nodeC); @@ -821,4 +817,124 @@ public static class DysonSphereFunctions dysonSphere.modelRenderer.RebuildModels(); } } + + public static void RemoveLowProductionShells() + { + StarData star = null; + var dysonEditor = UIRoot.instance?.uiGame?.dysonEditor; + if (dysonEditor != null && dysonEditor.gameObject.activeSelf) + { + star = dysonEditor.selection.viewStar; + } + if (star == null) + { + star = GameMain.data?.localStar; + if (star == null) + { + UIMessageBox.Show("CheatEnabler".Translate(), "You are not in any system.".Translate(), "确定".Translate(), 3, null); + return; + } + } + var dysonSphere = GameMain.data?.dysonSpheres[star.index]; + if (dysonSphere == null || dysonSphere.layerCount == 0) + { + UIMessageBox.Show("CheatEnabler".Translate(), string.Format("There is no Dyson Sphere shell on \"{0}\".".Translate(), star.displayName), "确定".Translate(), 3, null); + return; + } + int retainCount = RetainShellsCount.Value; + for (var i = 1; i < dysonSphere.layersIdBased.Length; i++) + { + var layer = dysonSphere.layersIdBased[i]; + if (layer == null || layer.id != i) continue; + var shells = layer.shellPool.Where(shell => shell != null).OrderBy(shell => shell.vertexCount, new ReverserComparer()).ToArray(); + for (var j = retainCount; j < shells.Length; j++) + { + var shell = shells[j]; + var id = shell.id; + layer.shellPool[id] = null; + for (var k = 0; k < shell.nodes.Count; k++) + { + shell.nodes[k].shells.Remove(shell); + } + shell.Free(); + shells[j] = null; + } + var count = UpToPowerOfTwo(retainCount + 1); + layer.shellPool = new DysonShell[count]; + layer.shellRecycle = new int[count]; + layer.shellRecycleCursor = 0; + layer.shellCapacity = count; + layer.shellCursor = retainCount + 1; + HashSet retainNodes = []; + HashSet<(int, int)> retainFrames = []; + for (var j = 0; j < retainCount; j++) + { + int id = j + 1; + var shell = shells[j]; + retainNodes.UnionWith(shell.nodes.Select(node => node.id)); + layer.shellPool[id] = shell; + shell.id = id; + for (var k = 0; k < shell.nodes.Count; k++) + { + var idA = shell.nodes[k].id; + var idB = shell.nodes[(k + 1) % shell.nodes.Count].id; + retainFrames.Add((idA, idB)); + retainFrames.Add((idB, idA)); + } + } + var nodes = layer.nodePool.Where(node => node != null && retainNodes.Contains(node.id)).ToArray(); + var frames = layer.framePool.Where(frame => frame != null && retainFrames.Contains((frame.nodeA.id, frame.nodeB.id))).ToArray(); + count = UpToPowerOfTwo(frames.Length + 1); + layer.framePool = new DysonFrame[count]; + layer.frameRecycle = new int[count]; + layer.frameRecycleCursor = 0; + layer.frameCapacity = count; + layer.frameCursor = frames.Length + 1; + for (var j = 0; j < frames.Length; j++) + { + int id = j + 1; + layer.framePool[id] = frames[j]; + frames[j].id = id; + } + count = UpToPowerOfTwo(nodes.Length + 1); + layer.nodePool = new DysonNode[count]; + layer.nodeRecycle = new int[count]; + layer.nodeRecycleCursor = 0; + layer.nodeCapacity = count; + layer.nodeCursor = nodes.Length + 1; + for (var j = 0; j < nodes.Length; j++) + { + int id = j + 1; + layer.nodePool[id] = nodes[j]; + nodes[j].id = id; + nodes[j].RecalcSpReq(); + nodes[j].RecalcCpReq(); + } + } + dysonSphere.CheckAutoNodes(); + if (dysonSphere.autoNodeCount <= 0) + { + dysonSphere.PickAutoNode(); + } + dysonSphere.modelRenderer.RebuildModels(); + } + + private static int UpToPowerOfTwo(int value) + { + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + return value + 1; + } + + private class ReverserComparer : IComparer + { + public int Compare(int x, int y) + { + return y.CompareTo(x); + } + } } diff --git a/CheatEnabler/Patches/DysonSpherePatch.cs b/CheatEnabler/Patches/DysonSpherePatch.cs index 82f147e..f5f70a0 100644 --- a/CheatEnabler/Patches/DysonSpherePatch.cs +++ b/CheatEnabler/Patches/DysonSpherePatch.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Reflection.Emit; using BepInEx.Configuration; using HarmonyLib; -using UnityEngine; using UXAssist.Common; namespace CheatEnabler.Patches; diff --git a/CheatEnabler/UIConfigWindow.cs b/CheatEnabler/UIConfigWindow.cs index 58952c7..3443c18 100644 --- a/CheatEnabler/UIConfigWindow.cs +++ b/CheatEnabler/UIConfigWindow.cs @@ -100,6 +100,35 @@ public static class UIConfigWindow } } + class RetainShellsCountMapper : MyWindow.RangeValueMapper + { + public RetainShellsCountMapper() : base(1, 46) + { + } + + public override int ValueToIndex(int value) + { + return value switch + { + < 4 => value, + < 64 => value / 4 + 3, + < 256 => value / 16 + 15, + _ => value / 256 + 30, + }; + } + + public override int IndexToValue(int index) + { + return index switch + { + < 4 => index, + < 19 => (index - 3) * 4, + < 31 => (index - 15) * 16, + _ => (index - 30) * 256, + }; + } + } + private static void CreateUI(MyConfigWindow wnd, RectTransform trans) { _windowTrans = trans; @@ -247,7 +276,7 @@ public static class UIConfigWindow wnd.AddCheckBox(x, y, tab4, DysonSpherePatch.UnlockMaxOrbitRadiusEnabled, "Unlock Dyson Sphere max orbit radius"); y += 30f; { - var slider = wnd.AddSlider(x + 20, y, tab4, DysonSpherePatch.UnlockMaxOrbitRadiusValue, new MaxOrbitRadiusValueMapper(), "##,#m").WithSmallerHandle(-40f); + var slider = wnd.AddSlider(x + 20f, y, tab4, DysonSpherePatch.UnlockMaxOrbitRadiusValue, new MaxOrbitRadiusValueMapper(), "##,#m").WithSmallerHandle(-40f); DysonSpherePatch.UnlockMaxOrbitRadiusEnabled.SettingChanged += UnlockMaxOrbitRadiusChanged; wnd.OnFree += () => { DysonSpherePatch.UnlockMaxOrbitRadiusEnabled.SettingChanged -= UnlockMaxOrbitRadiusChanged; }; UnlockMaxOrbitRadiusChanged(null, null); @@ -262,6 +291,10 @@ public static class UIConfigWindow wnd.AddButton(x, y, 300f, tab4, "Complete Dyson Sphere shells instantly", 16, "button-complete-dyson-sphere-shells-instantly", DysonSphereFunctions.CompleteShellsInstantly); y += 36f; wnd.AddButton(x, y, 300f, tab4, "Generate tricky dyson shells", 16, "button-generate-tricky-dyson-shells", DysonSphereFunctions.CreatePossibleFramesAndShells); + y += 36f; + wnd.AddButton(x, y, 300f, tab4, "Remove low production dyson shells", 16, "button-remove-low-production-dyson-shells", () => DysonSphereFunctions.RemoveLowProductionShells()); + y += 30f; + wnd.AddSlider(x + 20f, y, tab4, DysonSphereFunctions.RetainShellsCount, new RetainShellsCountMapper()); var tab5 = wnd.AddTab(_windowTrans, "Mecha/Combat"); x = 0f;