mirror of
https://github.com/soarqin/DSP_Mods.git
synced 2026-08-05 20:00:20 +08:00
236 lines
11 KiB
C#
236 lines
11 KiB
C#
using System.Collections.Generic;
|
|
using System.Reflection.Emit;
|
|
using HarmonyLib;
|
|
using UnityEngine;
|
|
using UXAssist.Common;
|
|
using GameLogicProc = UXAssist.Common.GameLogic;
|
|
|
|
namespace UXAssist.Patches.Factory;
|
|
|
|
internal static class FactoryBuildPatches
|
|
{
|
|
internal class AutoConstructPatch : PatchImpl<AutoConstructPatch>
|
|
{
|
|
protected override void OnEnable()
|
|
{
|
|
Functions.UIFunctions.UpdateToggleAutoConstructCheckButtonVisiblility();
|
|
// Diagnostic aid for reports of the auto-construct button not showing up:
|
|
// log the visibility predicate inputs once per enable.
|
|
var planet = GameMain.localPlanet;
|
|
var factoryLoaded = planet != null && planet.factoryLoaded;
|
|
UXAssist.Logger.LogInfo(
|
|
$"AutoConstruct button enabled: buttonCreated={Functions.UI.AutoConstructUI.ToggleAutoConstruct != null}, " +
|
|
$"localPlanet={planet != null}, factoryLoaded={factoryLoaded}, " +
|
|
$"prebuildCount={(factoryLoaded ? planet.factory.prebuildCount : 0)}");
|
|
}
|
|
|
|
protected override void OnDisable()
|
|
{
|
|
Functions.UIFunctions.UpdateToggleAutoConstructCheckButtonVisiblility();
|
|
}
|
|
|
|
// Button visibility and the pending-construction count text are reconciled periodically
|
|
// in AutoConstructUI.OnUpdate() (independent of Harmony patch state), so this postfix
|
|
// only implements the auto-construct fly-to-target behavior. This also keeps the patch
|
|
// surface small: PlanetData.NotifyFactoryLoaded/UnloadFactory no longer need postfixes.
|
|
[HarmonyPostfix]
|
|
[HarmonyPatch(typeof(PlayerAction_Rts), nameof(PlayerAction_Rts.GameTick))]
|
|
private static void PlayerAction_Rts_GameTick_Postfix(PlayerAction_Rts __instance, long timei)
|
|
{
|
|
if (timei % 60L != 0) return;
|
|
if (!FactoryPatch.AutoConstructEnabled.Value) return;
|
|
var planet = GameMain.localPlanet;
|
|
if (planet == null || !planet.factoryLoaded) return;
|
|
var factory = planet.factory;
|
|
var prebuildCount = factory.prebuildCount;
|
|
if (prebuildCount <= 0) return;
|
|
var player = __instance.player;
|
|
if (prebuildCount <= player.mecha.constructionModule.buildTargetTotalCount) return;
|
|
if (player.orders.orderCount > 0) return;
|
|
if (player.controller.horzVelocity.sqrMagnitude > 0.01f)
|
|
{
|
|
return;
|
|
}
|
|
var prebuilds = factory.prebuildPool;
|
|
var minDist = float.MaxValue;
|
|
var minIndex = 0;
|
|
var playerPos = player.position;
|
|
for (var i = factory.prebuildCursor - 1; i > 0; i--)
|
|
{
|
|
ref var prebuild = ref prebuilds[i];
|
|
if (prebuild.id != i || prebuild.isDestroyed) continue;
|
|
if (prebuild.itemRequired > 0)
|
|
{
|
|
if (player.package.GetItemCount(prebuild.protoId) < prebuild.itemRequired) continue;
|
|
}
|
|
var dist = (prebuild.pos - playerPos).sqrMagnitude;
|
|
if (dist < minDist)
|
|
{
|
|
minDist = dist;
|
|
minIndex = i;
|
|
}
|
|
}
|
|
if (minIndex == 0) return;
|
|
var diff = prebuilds[minIndex].pos - playerPos;
|
|
if (diff.sqrMagnitude < 400f) return;
|
|
if (player.movementState == EMovementState.Walk && player.mecha.thrusterLevel >= 1)
|
|
{
|
|
player.controller.actionWalk.SwitchToFly();
|
|
return;
|
|
}
|
|
player.Order(OrderNode.MoveTo(prebuilds[minIndex].pos + diff.normalized * 6f), false);
|
|
}
|
|
}
|
|
|
|
internal class QuickBuildAndDismantleLab : PatchImpl<QuickBuildAndDismantleLab>
|
|
{
|
|
private static bool DetermineMoreLabsForDismantle(BuildTool dismantle, int id)
|
|
{
|
|
if (!VFInput._chainReaction) return true;
|
|
var factory = dismantle.factory;
|
|
var proto = dismantle.GetItemProto(id);
|
|
var protoId = proto.ID;
|
|
var prefDesc = proto.prefabDesc;
|
|
if (!prefDesc.isLab && !prefDesc.isTank && (!prefDesc.isStorage || prefDesc.isBattleBase)) return true;
|
|
factory.ReadObjectConn(id, 14, out _, out var nextId, out _);
|
|
/* We keep last lab if selected lab is not the ground one */
|
|
if (nextId > 0)
|
|
{
|
|
while (true)
|
|
{
|
|
factory.ReadObjectConn(nextId, 14, out _, out var nextNextId, out _);
|
|
if (nextNextId <= 0) break;
|
|
var itemProto = dismantle.GetItemProto(nextId);
|
|
if (itemProto.ID != protoId) break;
|
|
var desc = itemProto.prefabDesc;
|
|
var pose = dismantle.GetObjectPose(nextId);
|
|
var preview = new BuildPreview
|
|
{
|
|
item = itemProto,
|
|
desc = desc,
|
|
lpos = pose.position,
|
|
lrot = pose.rotation,
|
|
lpos2 = pose.position,
|
|
lrot2 = pose.rotation,
|
|
objId = nextId,
|
|
needModel = desc.lodCount > 0 && desc.lodMeshes[0] != null,
|
|
isConnNode = true
|
|
};
|
|
dismantle.buildPreviews.Add(preview);
|
|
nextId = nextNextId;
|
|
}
|
|
}
|
|
|
|
nextId = id;
|
|
while (true)
|
|
{
|
|
factory.ReadObjectConn(nextId, 15, out _, out var nextId2, out _);
|
|
if (nextId2 <= 0)
|
|
{
|
|
factory.ReadObjectConn(nextId, 13, out _, out nextId2, out _);
|
|
if (nextId2 <= 0) break;
|
|
}
|
|
|
|
nextId = nextId2;
|
|
var itemProto = dismantle.GetItemProto(nextId);
|
|
var desc = itemProto.prefabDesc;
|
|
var pose = dismantle.GetObjectPose(nextId);
|
|
var preview = new BuildPreview
|
|
{
|
|
item = itemProto,
|
|
desc = desc,
|
|
lpos = pose.position,
|
|
lrot = pose.rotation,
|
|
lpos2 = pose.position,
|
|
lrot2 = pose.rotation,
|
|
objId = nextId,
|
|
needModel = desc.lodCount > 0 && desc.lodMeshes[0] != null,
|
|
isConnNode = true
|
|
};
|
|
dismantle.buildPreviews.Add(preview);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static void BuildLabsToTop(BuildTool_Click click)
|
|
{
|
|
if (!click.multiLevelCovering || !VFInput._chainReaction) return;
|
|
var prefDesc = click.GetPrefabDesc(click.castObjectId);
|
|
if (!prefDesc.isLab && !prefDesc.isTank && (!prefDesc.isStorage || prefDesc.isBattleBase)) return;
|
|
var levelMax = prefDesc.isLab ? GameMain.history.labLevel : GameMain.history.storageLevel;
|
|
var factory = click.factory;
|
|
var currLevel = 2;
|
|
var nid = click.castObjectId;
|
|
do
|
|
{
|
|
factory.ReadObjectConn(nid, 14, out _, out nid, out _);
|
|
if (nid <= 0) break;
|
|
currLevel++;
|
|
} while (true);
|
|
|
|
while (currLevel < levelMax)
|
|
{
|
|
click.UpdateRaycast();
|
|
click.DeterminePreviews();
|
|
click.UpdateCollidersForCursor();
|
|
click.UpdateCollidersForGiantBp();
|
|
var model = click.actionBuild.model;
|
|
click.UpdatePreviewModels(model);
|
|
if (!click.CheckBuildConditions())
|
|
{
|
|
model.ClearAllPreviewsModels();
|
|
model.EarlyGameTickIgnoreActive();
|
|
return;
|
|
}
|
|
|
|
click.UpdatePreviewModelConditions(model);
|
|
click.UpdateGizmos(model);
|
|
click.CreatePrebuilds();
|
|
currLevel++;
|
|
}
|
|
}
|
|
// Harmony transpiler: BuildTool_Dismantle_DeterminePreviews_Transpiler
|
|
// Target: BuildTool_Dismantle.DeterminePreviews
|
|
// Fallback: None — patch will fail loudly if the target method body changes.
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(BuildTool_Dismantle), nameof(BuildTool_Dismantle.DeterminePreviews))]
|
|
private static IEnumerable<CodeInstruction> BuildTool_Dismantle_DeterminePreviews_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldloc_3),
|
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))),
|
|
new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isBattleBase))),
|
|
new CodeMatch(OpCodes.Brfalse)
|
|
).Advance(-1);
|
|
matcher.InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Ldloc_3),
|
|
new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.objId))),
|
|
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(QuickBuildAndDismantleLab), nameof(DetermineMoreLabsForDismantle))),
|
|
new CodeInstruction(OpCodes.And)
|
|
);
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
// Harmony transpiler: BuildTool_Click__OnTick_Transpiler
|
|
// Target: BuildTool_Click._OnTick
|
|
// Fallback: None — patch will fail loudly if the target method body changes.
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(BuildTool_Click), nameof(BuildTool_Click._OnTick))]
|
|
private static IEnumerable<CodeInstruction> BuildTool_Click__OnTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldarg_0),
|
|
new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BuildTool_Click), nameof(BuildTool_Click.CreatePrebuilds)))
|
|
).Advance(2);
|
|
matcher.InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(QuickBuildAndDismantleLab), nameof(BuildLabsToTop)))
|
|
);
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
}
|
|
}
|