From d5f09972b1d8a9d1f587aa864d75e893eedd9542 Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Tue, 20 May 2025 19:02:33 +0800 Subject: [PATCH] work in progress --- .../Functions/DysonSphereFunctions.cs | 270 +++++++++++++++++- CheatEnabler/UIConfigWindow.cs | 3 + 2 files changed, 271 insertions(+), 2 deletions(-) diff --git a/CheatEnabler/Functions/DysonSphereFunctions.cs b/CheatEnabler/Functions/DysonSphereFunctions.cs index 784d5b6..edd0b42 100644 --- a/CheatEnabler/Functions/DysonSphereFunctions.cs +++ b/CheatEnabler/Functions/DysonSphereFunctions.cs @@ -1,4 +1,9 @@ -using HarmonyLib; +using System; +using System.Collections.Generic; +using System.Drawing.Text; +using System.Linq; +using HarmonyLib; +using UnityEngine; using UXAssist.Common; namespace CheatEnabler.Functions; @@ -76,7 +81,7 @@ public static class DysonSphereFunctions dysonNode.spOrdered = 0; dysonNode._spReq = 0; totalNodeSp += dysonNode.spMax; - var diff = dysonNode.spMax - dysonNode.sp; + var diff = dysonNode.spMax - dysonNode.sp; if (diff > 0) { rocketCount += diff; @@ -149,4 +154,265 @@ public static class DysonSphereFunctions } }); } + + private static DysonFrame QuickAddDysonFrame(this DysonSphereLayer layer, int protoId, DysonNode nodeA, DysonNode nodeB, bool euler) + { + int newId; + if (layer.frameRecycleCursor > 0) + { + var array = layer.frameRecycle; + var index = layer.frameRecycleCursor - 1; + layer.frameRecycleCursor = index; + newId = array[index]; + } + else + { + var index = layer.frameCursor; + layer.frameCursor = index + 1; + newId = index; + if (newId == layer.frameCapacity) + { + layer.SetFrameCapacity(layer.frameCapacity * 2); + } + } + DysonFrame frame = layer.framePool[newId]; + if (frame == null) + { + frame = new DysonFrame(); + layer.framePool[newId] = frame; + } + else + { + frame = layer.framePool[newId]; + frame.SetEmpty(); + } + frame.id = newId; + frame.layerId = layer.id; + frame.protoId = protoId + DysonSphereSegmentRenderer.nodeProtoCount; + frame.reserved = false; + frame.nodeA = nodeA; + frame.nodeB = nodeB; + frame.euler = euler; + frame.spA = 0; + frame.spB = 0; + frame.spMax = frame.segCount * DysonFrame.kSpPerSeg; + nodeA.frames.Add(frame); + nodeB.frames.Add(frame); + return frame; + } + + private static int QuickAddDysonShell(this DysonSphereLayer layer, int protoId, DysonNode[] nodes, DysonFrame[] frames) + { + int shellId = 0; + if (layer.shellRecycleCursor > 0) + { + int[] array = layer.shellRecycle; + int index = layer.shellRecycleCursor - 1; + layer.shellRecycleCursor = index; + shellId = array[index]; + } + else + { + int index = layer.shellCursor; + layer.shellCursor = index + 1; + shellId = index; + if (shellId == layer.shellCapacity) + { + layer.SetShellCapacity(layer.shellCapacity * 2); + } + } + var shell = layer.shellPool[shellId]; + if (shell == null) + { + shell = new DysonShell(layer); + layer.shellPool[shellId] = shell; + } + else + { + shell.SetEmpty(); + } + shell.id = shellId; + shell.layerId = layer.id; + shell.protoId = protoId; + shell.randSeed = layer.id * 10000 + shellId; + for (int j = 0; j < nodes.Length; j++) + { + DysonNode dysonNode = nodes[j]; + DysonNode dysonNode2 = nodes[(j + 1) % nodes.Length]; + DysonFrame dysonFrame = frames[j]; + List segments = dysonFrame.GetSegments(); + if (dysonNode == dysonFrame.nodeA) + { + for (int k = 0; k < segments.Count - 1; k++) + { + shell.polygon.Add(segments[k]); + } + } + else + { + for (int l = segments.Count - 1; l >= 1; l--) + { + shell.polygon.Add(segments[l]); + } + } + shell.nodeIndexMap[nodes[j % nodes.Length].id] = shell.nodes.Count; + shell.nodes.Add(dysonNode); + shell.frames.Add(dysonFrame); + if (!dysonNode.shells.Contains(shell)) + { + dysonNode.shells.Add(shell); + } + } + shell.GenerateGeometry(); + shell.GenerateModelObjects(); + CheatEnabler.Logger.LogInfo($"QuickAddDysonShell: {DysonShell.s_vmap.Count}"); + return shellId; + } + + private struct SupposedShell + { + public DysonNode nodeA; + public DysonNode nodeB; + public DysonNode nodeC; + + public float area; + } + + public static void CreatePossibleFramesAndShells() + { + 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; + } + var framesChanged = false; + var shellsChanged = false; + for (var i = 1; i < dysonSphere.layersIdBased.Length; i++) + { + Dictionary<(int, int), int> availableFrames = []; + HashSet<(int, int, int)> availableShells = []; + HashSet spDirtyNodes = []; + HashSet cpDirtyNodes = []; + var layer = dysonSphere.layersIdBased[i]; + if (layer == null || layer.id != i) continue; + for (var j = 1; j < layer.frameCursor; j++) + { + var frame = layer.framePool[j]; + if (frame == null || frame.id != j) continue; + var idA = frame.nodeA.id; + var idB = frame.nodeB.id; + if (idA > idB) + { + (idA, idB) = (idB, idA); + } + availableFrames[(idA, idB)] = j; + } + for (var j = 1; j < layer.shellCursor; j++) + { + var shell = layer.shellPool[j]; + if (shell == null || shell.id != j) continue; + if (shell.nodes.Count != 3) continue; + var ids = shell.nodes.Select(node => node.id).OrderBy(id => id).ToArray(); + availableShells.Add((ids[0], ids[1], ids[2])); + } + int nodeCount = layer.nodeCursor; + + List supposedShells = []; + for (var j = 1; j < nodeCount; j++) + { + var nodeA = layer.nodePool[j]; + if (nodeA == null || nodeA.id != j) continue; + for (var k = j + 1; k < nodeCount; k++) + { + var nodeB = layer.nodePool[k]; + if (nodeB == null || nodeB.id != k) continue; + for (var l = k + 1; l < nodeCount; l++) + { + var nodeC = layer.nodePool[l]; + if (nodeC == null || nodeC.id != l) 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 }); + } + } + } + supposedShells.Sort((a, b) => b.area.CompareTo(a.area)); + var count = Math.Min(supposedShells.Count, 1); + for (var j = 0; j < count; j++) + { + var shell = supposedShells[j]; + if (availableShells.TryGetValue((shell.nodeA.id, shell.nodeB.id, shell.nodeC.id), out _)) continue; + if (!availableFrames.TryGetValue((shell.nodeA.id, shell.nodeB.id), out _)) { + var frame = layer.QuickAddDysonFrame(0, shell.nodeA, shell.nodeB, false); + availableFrames[(shell.nodeA.id, shell.nodeB.id)] = frame.id; + spDirtyNodes.Add(shell.nodeA); + spDirtyNodes.Add(shell.nodeB); + } + if (!availableFrames.TryGetValue((shell.nodeA.id, shell.nodeC.id), out _)) { + var frame = layer.QuickAddDysonFrame(0, shell.nodeA, shell.nodeC, false); + availableFrames[(shell.nodeA.id, shell.nodeC.id)] = frame.id; + spDirtyNodes.Add(shell.nodeA); + spDirtyNodes.Add(shell.nodeC); + } + if (!availableFrames.TryGetValue((shell.nodeB.id, shell.nodeC.id), out _)) { + var frame = layer.QuickAddDysonFrame(0, shell.nodeB, shell.nodeC, false); + availableFrames[(shell.nodeB.id, shell.nodeC.id)] = frame.id; + spDirtyNodes.Add(shell.nodeB); + spDirtyNodes.Add(shell.nodeC); + } + } + foreach (var node in spDirtyNodes) + { + node.RecalcSpReq(); + } + framesChanged = framesChanged || spDirtyNodes.Count > 0; + for (var j = 0; j < count; j++) + { + var shell = supposedShells[j]; + if (availableShells.TryGetValue((shell.nodeA.id, shell.nodeB.id, shell.nodeC.id), out _)) continue; + DysonFrame[] frames = [layer.framePool[availableFrames[(shell.nodeA.id, shell.nodeB.id)]], layer.framePool[availableFrames[(shell.nodeA.id, shell.nodeC.id)]], layer.framePool[availableFrames[(shell.nodeB.id, shell.nodeC.id)]]]; + DysonNode[] nodes = [shell.nodeA, shell.nodeB, shell.nodeC]; + layer.QuickAddDysonShell(0, nodes, frames); + cpDirtyNodes.Add(shell.nodeA); + cpDirtyNodes.Add(shell.nodeB); + cpDirtyNodes.Add(shell.nodeC); + shellsChanged = true; + } + foreach (var node in cpDirtyNodes) + { + node.RecalcCpReq(); + } + } + if (framesChanged) + { + dysonSphere.CheckAutoNodes(); + if (dysonSphere.autoNodeCount <= 0) + { + dysonSphere.PickAutoNode(); + } + } + if (shellsChanged) + { + GameMain.gameScenario.NotifyOnPlanDysonShell(); + } + if (framesChanged || shellsChanged) + { + dysonSphere.modelRenderer.RebuildModels(); + } + } } diff --git a/CheatEnabler/UIConfigWindow.cs b/CheatEnabler/UIConfigWindow.cs index 3841c9f..58952c7 100644 --- a/CheatEnabler/UIConfigWindow.cs +++ b/CheatEnabler/UIConfigWindow.cs @@ -65,6 +65,7 @@ public static class UIConfigWindow I18N.Add("Overclock Ejectors", "Overclock Ejectors (10x)", "高速弹射器(10倍射速)"); I18N.Add("Overclock Silos", "Overclock Silos (10x)", "高速发射井(10倍射速)"); I18N.Add("Unlock Dyson Sphere max orbit radius", "Unlock Dyson Sphere max orbit radius", "解锁戴森球最大轨道半径"); + I18N.Add("Generate tricky dyson shells", "Generate tricky dyson shells (Put/Paste nodes first)", "生成仙术戴森壳(请先设置/粘贴节点)"); I18N.Add("Complete Dyson Sphere shells instantly", "Complete Dyson Sphere shells instantly", "立即完成戴森壳建造"); I18N.Add("Terraform without enough soil piles", "Terraform without enough soil piles", "沙土不够时依然可以整改地形"); I18N.Add("Instant hand-craft", "Instant hand-craft", "快速手动制造"); @@ -259,6 +260,8 @@ public static class UIConfigWindow x = 300f; y = 10f; 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); var tab5 = wnd.AddTab(_windowTrans, "Mecha/Combat"); x = 0f;