mirror of
https://github.com/soarqin/DSP_Mods.git
synced 2025-12-08 23:33:33 +08:00
388 lines
19 KiB
C#
388 lines
19 KiB
C#
using System.Collections.Generic;
|
|
using System.Reflection.Emit;
|
|
using BepInEx.Configuration;
|
|
using CommonAPI.Systems;
|
|
using HarmonyLib;
|
|
using UnityEngine;
|
|
using UXAssist.Common;
|
|
|
|
namespace UXAssist;
|
|
|
|
public static class PlayerPatch
|
|
{
|
|
public static ConfigEntry<bool> EnhancedMechaForgeCountControlEnabled;
|
|
public static ConfigEntry<bool> HideTipsForSandsChangesEnabled;
|
|
public static ConfigEntry<bool> AutoNavigationEnabled;
|
|
public static ConfigEntry<bool> AutoCruiseEnabled;
|
|
public static ConfigEntry<double> DistanceToWarp;
|
|
private static PressKeyBind _autoDriveKey;
|
|
|
|
public static void Init()
|
|
{
|
|
EnhancedMechaForgeCountControlEnabled.SettingChanged += (_, _) => EnhancedMechaForgeCountControl.Enable(EnhancedMechaForgeCountControlEnabled.Value);
|
|
HideTipsForSandsChangesEnabled.SettingChanged += (_, _) => HideTipsForSandsChanges.Enable(HideTipsForSandsChangesEnabled.Value);
|
|
AutoNavigationEnabled.SettingChanged += (_, _) => AutoNavigation.Enable(AutoNavigationEnabled.Value);
|
|
EnhancedMechaForgeCountControl.Enable(EnhancedMechaForgeCountControlEnabled.Value);
|
|
HideTipsForSandsChanges.Enable(HideTipsForSandsChangesEnabled.Value);
|
|
AutoNavigation.Enable(AutoNavigationEnabled.Value);
|
|
_autoDriveKey = KeyBindings.RegisterKeyBinding(new BuiltinKey
|
|
{
|
|
key = new CombineKey(0, 0, ECombineKeyAction.OnceClick, true),
|
|
conflictGroup = KeyBindConflict.MOVEMENT | KeyBindConflict.FLYING | KeyBindConflict.SAILING | KeyBindConflict.BUILD_MODE_1 | KeyBindConflict.KEYBOARD_KEYBIND,
|
|
name = "ToggleAutoCruise",
|
|
canOverride = true
|
|
});
|
|
I18N.Add("AutoCruiseOn", "Auto-cruise enabled", "已启用自动巡航");
|
|
I18N.Add("AutoCruiseOff", "Auto-cruise disabled", "已禁用自动巡航");
|
|
}
|
|
|
|
public static void OnUpdate()
|
|
{
|
|
if (_autoDriveKey.keyValue)
|
|
{
|
|
AutoNavigation.ToggleAutoCruise();
|
|
}
|
|
}
|
|
|
|
public static void Uninit()
|
|
{
|
|
EnhancedMechaForgeCountControl.Enable(false);
|
|
HideTipsForSandsChanges.Enable(false);
|
|
AutoNavigation.Enable(false);
|
|
}
|
|
|
|
private static class EnhancedMechaForgeCountControl
|
|
{
|
|
private static Harmony _patch;
|
|
|
|
public static void Enable(bool on)
|
|
{
|
|
if (on)
|
|
{
|
|
_patch ??= Harmony.CreateAndPatchAll(typeof(EnhancedMechaForgeCountControl));
|
|
}
|
|
else
|
|
{
|
|
_patch?.UnpatchSelf();
|
|
_patch = null;
|
|
}
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnOkButtonClick))]
|
|
private static IEnumerable<CodeInstruction> UIReplicatorWindow_OnOkButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
|
|
);
|
|
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 1000));
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnPlusButtonClick))]
|
|
[HarmonyPatch(typeof(UIReplicatorWindow), nameof(UIReplicatorWindow.OnMinusButtonClick))]
|
|
private static IEnumerable<CodeInstruction> UIReplicatorWindow_OnPlusButtonClick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var label1 = generator.DefineLabel();
|
|
var label2 = generator.DefineLabel();
|
|
var label3 = generator.DefineLabel();
|
|
var label4 = generator.DefineLabel();
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ldloc_0),
|
|
new CodeMatch(OpCodes.Ldc_I4_1),
|
|
new CodeMatch(o => o.opcode == OpCodes.Add || o.opcode == OpCodes.Sub)
|
|
).Advance(1).RemoveInstruction().InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.control))),
|
|
new CodeInstruction(OpCodes.Brfalse_S, label1),
|
|
new CodeInstruction(OpCodes.Ldc_I4_S, 10),
|
|
new CodeInstruction(OpCodes.Br_S, label4),
|
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.shift))).WithLabels(label1),
|
|
new CodeInstruction(OpCodes.Brfalse_S, label2),
|
|
new CodeInstruction(OpCodes.Ldc_I4_S, 100),
|
|
new CodeInstruction(OpCodes.Br_S, label4),
|
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(VFInput), nameof(VFInput.alt))).WithLabels(label2),
|
|
new CodeInstruction(OpCodes.Brfalse_S, label3),
|
|
new CodeInstruction(OpCodes.Ldc_I4, 1000),
|
|
new CodeInstruction(OpCodes.Br_S, label4),
|
|
new CodeInstruction(OpCodes.Ldc_I4_1).WithLabels(label3)
|
|
).Labels.Add(label4);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(ci => ci.opcode == OpCodes.Ldc_I4_S && ci.OperandIs(10))
|
|
);
|
|
matcher.Repeat(m => m.SetAndAdvance(OpCodes.Ldc_I4, 1000));
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
}
|
|
|
|
private static class HideTipsForSandsChanges
|
|
{
|
|
private static Harmony _patch;
|
|
|
|
public static void Enable(bool on)
|
|
{
|
|
if (on)
|
|
{
|
|
_patch ??= Harmony.CreateAndPatchAll(typeof(HideTipsForSandsChanges));
|
|
}
|
|
else
|
|
{
|
|
_patch?.UnpatchSelf();
|
|
_patch = null;
|
|
}
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(Player), nameof(Player.SetSandCount))]
|
|
private static IEnumerable<CodeInstruction> Player_SetSandCount_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Call, AccessTools.PropertySetter(typeof(Player), nameof(Player.sandCount)))
|
|
).Advance(1).Insert(new CodeInstruction(OpCodes.Ret));
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
}
|
|
|
|
public static class AutoNavigation
|
|
{
|
|
private static Harmony _patch;
|
|
|
|
private static bool _canUseWarper;
|
|
private static int _indicatorAstroId;
|
|
private static bool _speedUp;
|
|
private static Vector3 _direction;
|
|
|
|
public static void Enable(bool on)
|
|
{
|
|
if (on)
|
|
{
|
|
_patch ??= Harmony.CreateAndPatchAll(typeof(AutoNavigation));
|
|
}
|
|
else
|
|
{
|
|
_patch?.UnpatchSelf();
|
|
_patch = null;
|
|
}
|
|
}
|
|
|
|
public static void ToggleAutoCruise()
|
|
{
|
|
AutoCruiseEnabled.Value = !AutoCruiseEnabled.Value;
|
|
if (!DSPGame.IsMenuDemo && GameMain.isRunning)
|
|
{
|
|
UIRoot.instance.uiGame.generalTips.InvokeRealtimeTipAhead((AutoCruiseEnabled.Value ? "AutoCruiseOn" : "AutoCruiseOff").Translate());
|
|
}
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(PlayerController), nameof(PlayerController.GameTick))]
|
|
private static IEnumerable<CodeInstruction> PlayerController_GameTick_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BuildModel), nameof(BuildModel.EarlyGameTickIgnoreActive)))
|
|
).Advance(1).InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
Transpilers.EmitDelegate((PlayerController controller) =>
|
|
{
|
|
/* Update target astro if changed */
|
|
_speedUp = false;
|
|
var player = controller.player;
|
|
var navi = player.navigation;
|
|
if (navi.indicatorAstroId != _indicatorAstroId)
|
|
{
|
|
_indicatorAstroId = navi.indicatorAstroId;
|
|
if (_indicatorAstroId == 0) return;
|
|
}
|
|
else if (_indicatorAstroId == 0) return;
|
|
switch (controller.movementStateInFrame)
|
|
{
|
|
case EMovementState.Walk:
|
|
case EMovementState.Drift:
|
|
if (!AutoCruiseEnabled.Value) return;
|
|
if (GameMain.localStar?.astroId == _indicatorAstroId) return;
|
|
/* Press jump key to fly */
|
|
controller.input0.z = 1f;
|
|
break;
|
|
case EMovementState.Fly:
|
|
if (!AutoCruiseEnabled.Value) return;
|
|
if (GameMain.localStar?.astroId == _indicatorAstroId) return;
|
|
/* Keep pressing jump and pullup key to sail */
|
|
controller.input0.y = 1f;
|
|
controller.input1.y = 1f;
|
|
break;
|
|
case EMovementState.Sail:
|
|
if (VFInput._pullUp.pressing || VFInput._pushDown.pressing || VFInput._moveLeft.pressing || VFInput._moveRight.pressing ||
|
|
(!player.warping && UIRoot.instance.uiGame.disableLockCursor && (VFInput._moveForward.pressing || VFInput._moveBackward.pressing)))
|
|
return;
|
|
var playerPos = player.uPosition;
|
|
var isHive = _indicatorAstroId > 1000000;
|
|
ref var astro = ref isHive ? ref GameMain.spaceSector.astros[_indicatorAstroId - 1000000] : ref GameMain.galaxy.astrosData[_indicatorAstroId];
|
|
var astroVec = astro.uPos - playerPos;
|
|
var distance = astroVec.magnitude;
|
|
if (distance < astro.type switch
|
|
{
|
|
EAstroType.Planet => 800.0 + astro.uRadius,
|
|
EAstroType.Star => 4000.0 + astro.uRadius,
|
|
EAstroType.EnemyHive => 400.0,
|
|
_ => 2000.0 + astro.uRadius
|
|
})
|
|
{
|
|
if (isHive)
|
|
{
|
|
player.uVelocity = Vector3.zero;
|
|
}
|
|
return;
|
|
}
|
|
if (GameMain.instance.timei % 6 == 0 || _direction == Vector3.zero)
|
|
{
|
|
_direction = astroVec.normalized;
|
|
|
|
/* Check nearest astroes, try to bypass them */
|
|
var localStar = GameMain.localStar;
|
|
_canUseWarper = AutoCruiseEnabled.Value && !player.warping && player.mecha.warpStorage.GetItemCount(1210) > 0;
|
|
if (localStar != null)
|
|
{
|
|
var nearestRange = (playerPos - localStar.uPosition).sqrMagnitude;
|
|
var nearestPos = localStar.uPosition;
|
|
var nearestAstroId = localStar.astroId;
|
|
foreach (var p in localStar.planets)
|
|
{
|
|
var range = (playerPos - p.uPosition).sqrMagnitude;
|
|
if (range >= nearestRange) continue;
|
|
nearestRange = range;
|
|
nearestPos = p.uPosition;
|
|
nearestAstroId = p.astroId;
|
|
}
|
|
|
|
/* If targeting hives, do not bypass them */
|
|
if (!isHive)
|
|
{
|
|
var hiveSys = GameMain.spaceSector.dfHives[localStar.index];
|
|
while (hiveSys != null)
|
|
{
|
|
if (hiveSys.realized && hiveSys.hiveAstroId > 1000000)
|
|
{
|
|
ref var hiveAstro = ref GameMain.spaceSector.astros[hiveSys.hiveAstroId - 1000000];
|
|
/* Divide by 4, so that the real range is 2 times of the calculated range,
|
|
which means the minimal range allowed is 4000 */
|
|
var range = (playerPos - hiveAstro.uPos).sqrMagnitude / 4.0;
|
|
if (range < nearestRange)
|
|
{
|
|
nearestRange = range;
|
|
nearestPos = hiveAstro.uPos;
|
|
nearestAstroId = hiveSys.hiveAstroId;
|
|
}
|
|
}
|
|
|
|
hiveSys = hiveSys.nextSibling;
|
|
}
|
|
}
|
|
|
|
if (nearestAstroId != _indicatorAstroId && nearestRange < 2000.0 * 2000.0)
|
|
{
|
|
Vector3 leavingDirection = (playerPos - nearestPos).normalized;
|
|
var dot = Vector3.Dot(leavingDirection, _direction);
|
|
if (dot < 0)
|
|
{
|
|
var cross = Vector3.Cross(_direction, leavingDirection);
|
|
_direction = Vector3.Cross(leavingDirection, cross).normalized;
|
|
}
|
|
else
|
|
{
|
|
_direction = leavingDirection;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 uVel = player.uVelocity;
|
|
var speed = uVel.magnitude;
|
|
if (player.warping)
|
|
{
|
|
_speedUp = false;
|
|
if (AutoCruiseEnabled.Value)
|
|
{
|
|
/* Speed down if too close */
|
|
var actionSail = controller.actionSail;
|
|
if (distance < GalaxyData.LY * 1.5)
|
|
{
|
|
if (distance < actionSail.currentWarpSpeed * distance switch
|
|
{
|
|
> GalaxyData.LY * 0.6 => 0.33,
|
|
> GalaxyData.LY * 0.3 => 0.5,
|
|
> GalaxyData.LY * 0.1 => 0.66,
|
|
_ => 1.0
|
|
})
|
|
{
|
|
controller.input0.y = -1f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_canUseWarper && GameMain.localPlanet == null && distance > GalaxyData.AU * DistanceToWarp.Value && player.mecha.UseWarper())
|
|
{
|
|
player.warpCommand = true;
|
|
VFAudio.Create("warp-begin", player.transform, Vector3.zero, true);
|
|
}
|
|
else
|
|
{
|
|
/* Speed up if needed */
|
|
_speedUp = speed + 0.2f < player.mecha.maxSailSpeed;
|
|
}
|
|
}
|
|
|
|
/* Update direction, gracefully rotate for 2 degrees for each frame */
|
|
var angle = Vector3.Angle(uVel, _direction);
|
|
if (angle < 2f)
|
|
{
|
|
player.uVelocity = _direction * speed;
|
|
}
|
|
else
|
|
{
|
|
player.uVelocity = Vector3.Slerp(uVel, _direction * speed, 2f / angle);
|
|
}
|
|
break;
|
|
default:
|
|
_speedUp = false;
|
|
break;
|
|
}
|
|
})
|
|
);
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
[HarmonyTranspiler]
|
|
[HarmonyPatch(typeof(VFInput), nameof(VFInput._sailSpeedUp), MethodType.Getter)]
|
|
private static IEnumerable<CodeInstruction> VFInput_sailSpeedUp_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var matcher = new CodeMatcher(instructions, generator);
|
|
matcher.MatchForward(false,
|
|
new CodeMatch(OpCodes.Ret)
|
|
);
|
|
matcher.Repeat(m => m.InsertAndAdvance(
|
|
new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(AutoNavigation), nameof(_speedUp))),
|
|
new CodeInstruction(OpCodes.Or)
|
|
).Advance(1));
|
|
return matcher.InstructionEnumeration();
|
|
}
|
|
|
|
/* Disable Lock Cursor Mode on entering sail panel
|
|
[HarmonyPrefix]
|
|
[HarmonyPatch(typeof(UISailPanel), nameof(UISailPanel._OnOpen))]
|
|
public static void OnOpen_Prefix()
|
|
{
|
|
if (_aimingEnabled)
|
|
{
|
|
UIRoot.instance.uiGame.disableLockCursor = true;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|