mirror of
https://github.com/soarqin/DSP_Mods.git
synced 2026-08-05 10:50:10 +08:00
fix(CheatEnabler): retry invalid Dyson shell candidates
This commit is contained in:
@@ -147,6 +147,7 @@ The sync is implemented as an inline PowerShell `Exec` step inside the `ZipMod`
|
||||
- **Internationalization:** `UXAssist/Common/I18N.cs` provides bilingual (EN + ZH) string lookup used across UXAssist and CheatEnabler. Localization keys are declared as `public const string` in per-project registration classes (`UXAssist/Common/I18NKeys.cs`, `CheatEnabler/Localization.cs`, `UniverseGenTweaks/Localization.cs`) and registered through a single `Register()` call from each mod's `Awake()`. Do not pass Chinese string literals to `.Translate()` at call sites.
|
||||
- **Centralized game constants:** Hard-coded item IDs, tech IDs, logistics capacities, and Dyson sphere geometry defaults live in `UXAssist/Common/GameConstants` (`ItemIds`, `TechIds`, `LogisticsConstants`, `DysonSphereConstants`). Prefer these constants over inline literals in UXAssist patches.
|
||||
- **Game source facts:** In the original DSP `Assembly-CSharp.dll`, `PlanetFactory.prebuildCount` is computed as `prebuildCursor - prebuildRecycleCursor - 1`, and normal prebuild add/remove paths maintain those cursors. If Auto Construct UI reports zero while visible construction ghosts exist, first suspect the wrong planet/factory, non-prebuild preview state, or a missed UI refresh path before replacing this property with a pool scan.
|
||||
- **Illegal Dyson shell generation:** Estimated triangle vertex counts mirror the original `DysonShell.GenerateGeometry` rasterization as closely as possible, but can still differ from the final frame-generated shell polygon at numerical boundaries. Max-output generation must treat `QuickAddDysonShell` failure as a candidate rejection and try the next candidate rather than assuming the estimate is exact.
|
||||
- **Fail-soft patch application:** `PatchImpl<T>.Enable(true)` applies Harmony patches inside a try/catch. Runtime patching can fail through no fault of ours (Harmony re-runs other mods' transpilers on shared target methods), and an escaping exception would abort the calling `ConfigEntry.SettingChanged` delegate chain, desyncing config UI from config values. On failure it logs a `LogError` with the feature type name, rolls back via `UnpatchSelf()`, and leaves `_patch` null so a later `Enable(true)` can retry.
|
||||
- **Convergent in-game UI state:** In-game overlay widgets whose visibility depends on game state (e.g. `AutoConstructUI`) must not rely solely on one-shot event-driven refreshes (`SettingChanged` handlers, patch `OnEnable`/`OnDisable`), because a thrown exception earlier in a delegate chain or a failed patch application silently drops the refresh. `AutoConstructUI.OnUpdate()` reconciles button visibility and the pending-construction count with actual game state every 30 frames (also effective while paused); the `AutoConstructPatch` postfix on `PlayerAction_Rts.GameTick` only implements the fly-to-target behavior, and `AutoConstructPatch.OnEnable` logs its visibility predicate inputs once per enable as a remote-diagnosis aid.
|
||||
- **Transpiler patches:** Performance-critical mods (LabOpt, MechaDronesTweaks) use `[HarmonyTranspiler]` to rewrite IL instructions directly for maximum efficiency. All transpilers in UXAssist, CheatEnabler, and UniverseGenTweaks carry a standard header comment (`// Harmony transpiler:`, `// Target:`, `// Fallback:`) documenting the target method and fallback behavior. `UXAssist.Common.Patching.TranspilerGuard` provides a reusable `CodeMatcher.Finish` helper that returns original instructions when a matcher becomes invalid.
|
||||
|
||||
@@ -162,17 +162,18 @@ public static class GeometryHelpers
|
||||
double num = 0.0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
double num2 = Vector3.Distance(pos[i], pos[(i + 1) % 3]);
|
||||
float num2 = Vector3.Distance(pos[i], pos[(i + 1) % 3]);
|
||||
VectorLF3 vectorLF2 = ((VectorLF3)pos[i] + (VectorLF3)pos[(i + 1) % 3]) * 0.5;
|
||||
sum += vectorLF2 * num2;
|
||||
num += num2;
|
||||
num += (double)num2;
|
||||
}
|
||||
var radius = Math.Round(polygon[0].magnitude * 10.0) / 10.0;
|
||||
for (int j = 0; j < polygon.Length; j++)
|
||||
{
|
||||
polygon[j] = polygon[j].normalized * radius;
|
||||
}
|
||||
var center = (sum / num).normalized * radius;
|
||||
var normalized = (sum / num).normalized;
|
||||
var center = normalized * radius;
|
||||
float num3 = 0f;
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
@@ -189,12 +190,12 @@ public static class GeometryHelpers
|
||||
var cpPerVertex = gridScale * gridScale * DysonSphereConstants.CpPerVertexFactor;
|
||||
|
||||
var num5 = (int)((double)num3 / factor3 / gridSizeDouble + 2.5);
|
||||
var xaxis = VectorLF3.Cross(center, Vector3.up).normalized;
|
||||
var xaxis = VectorLF3.Cross(normalized, Vector3.up).normalized;
|
||||
if (xaxis.magnitude < 0.1)
|
||||
{
|
||||
xaxis = new VectorLF3(0f, 0f, 1f);
|
||||
}
|
||||
var yaxis = VectorLF3.Cross(xaxis, center).normalized;
|
||||
var yaxis = VectorLF3.Cross(xaxis, normalized).normalized;
|
||||
var raydir = xaxis * factor0 + yaxis * factor1;
|
||||
var w1axis = xaxis * (0.5 * gridSizeDouble) - yaxis * (factor3 * gridSizeDouble);
|
||||
var w2axis = xaxis * (0.5 * gridSizeDouble) + yaxis * (factor3 * gridSizeDouble);
|
||||
@@ -206,7 +207,9 @@ public static class GeometryHelpers
|
||||
var polynu = new double[3];
|
||||
for (int l = 0; l < 3; l++)
|
||||
{
|
||||
polyn[l] = VectorLF3.Cross(polygon[l], polygon[(l + 1) % 3]).normalized;
|
||||
Vector3 vector = polygon[l];
|
||||
Vector3 vector2 = polygon[(l + 1) % 3];
|
||||
polyn[l] = VectorLF3.Cross(vector, vector2).normalized;
|
||||
polynu[l] = polyn[l].x * raydir.x + polyn[l].y * raydir.y + polyn[l].z * raydir.z;
|
||||
}
|
||||
var vmap = _vmap.Value;
|
||||
|
||||
@@ -362,6 +362,7 @@ public static class IllegalShellFunctions
|
||||
{
|
||||
var sshell = supposedShells[j];
|
||||
var vertCount = GeometryHelpers.CalculateTriangleVertCount([sshell.posA, sshell.posB, sshell.posC]);
|
||||
sshell.vertCount = vertCount <= barrier ? vertCount : -1;
|
||||
if (vertCount <= barrier)
|
||||
{
|
||||
lock (mutex)
|
||||
@@ -385,16 +386,32 @@ public static class IllegalShellFunctions
|
||||
if (maxJ >= 0)
|
||||
{
|
||||
ResetLayerPools(layer);
|
||||
var sshell = supposedShells[maxJ];
|
||||
DysonNode[] newNodes = [layer.QuickAddDysonNode(0, sshell.posA), layer.QuickAddDysonNode(0, sshell.posB), layer.QuickAddDysonNode(0, sshell.posC)];
|
||||
DysonFrame[] newFrames = [layer.QuickAddDysonFrame(0, newNodes[0], newNodes[1], false), layer.QuickAddDysonFrame(0, newNodes[1], newNodes[2], false), layer.QuickAddDysonFrame(0, newNodes[2], newNodes[0], false)];
|
||||
layer.QuickAddDysonShell(0, newNodes, newFrames, false);
|
||||
foreach (var node in newNodes)
|
||||
foreach (var sshell in supposedShells.OrderByDescending(shell => shell.vertCount))
|
||||
{
|
||||
node.RecalcSpReq();
|
||||
node.RecalcCpReq();
|
||||
if (sshell.vertCount <= 0) break;
|
||||
|
||||
DysonNode[] newNodes = [layer.QuickAddDysonNode(0, sshell.posA), layer.QuickAddDysonNode(0, sshell.posB), layer.QuickAddDysonNode(0, sshell.posC)];
|
||||
DysonFrame[] newFrames = [layer.QuickAddDysonFrame(0, newNodes[0], newNodes[1], false), layer.QuickAddDysonFrame(0, newNodes[1], newNodes[2], false), layer.QuickAddDysonFrame(0, newNodes[2], newNodes[0], false)];
|
||||
if (layer.QuickAddDysonShell(0, newNodes, newFrames, false) == 0)
|
||||
{
|
||||
foreach (var frame in newFrames)
|
||||
{
|
||||
layer.QuickRemoveDysonFrame(frame.id);
|
||||
}
|
||||
foreach (var node in newNodes)
|
||||
{
|
||||
layer.QuickRemoveDysonNode(node.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
foreach (var node in newNodes)
|
||||
{
|
||||
node.RecalcSpReq();
|
||||
node.RecalcCpReq();
|
||||
}
|
||||
shellsChanged = true;
|
||||
break;
|
||||
}
|
||||
shellsChanged = true;
|
||||
}
|
||||
return shellsChanged;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user