-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathExtendedDeployableParts.cs
More file actions
113 lines (91 loc) · 5.29 KB
/
ExtendedDeployableParts.cs
File metadata and controls
113 lines (91 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using UnityEngine;
namespace KSPCommunityFixes.BugFixes
{
class ExtendedDeployableParts : BasePatch
{
// Not entirely clear what is happening, but for a retracted ModuleDeployablePart, as of 1.12 stock fails to set the animation
// after instantiating the part, resulting in having the model in the extended state if the part model was exported in the extended
// state. It seems something has changed between Unity 2019.2.2f1 (KSP 1.11) and 2019.4.18f1 (KSP 1.12), likely the Animation.Stop()
// method not force-rewinding the animation anymore if it isn't actually playing. Setting the animation state with Animation.Stop()
// is (suspiciously) only done in two places, in many more cases the stock code is instead keeping AnimationState.enabled = true and
// setting AnimationState.speed = 0.
// We patch those two Animation.Stop() call sites, and use that latter method instead.
protected override Version VersionMin => new Version(1, 12, 0);
protected override void ApplyPatches()
{
AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM));
AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart));
}
static IEnumerable<CodeInstruction> ModuleDeployablePart_startFSM_Transpiler(IEnumerable<CodeInstruction> instructions)
{
// change :
// anim.Stop(animationName);
//IL_0091: ldarg.0
//IL_0092: ldfld class [UnityEngine.AnimationModule] UnityEngine.Animation ModuleDeployablePart::anim
//IL_0097: ldarg.0
//IL_0098: ldfld string ModuleDeployablePart::animationName
//IL_009d: callvirt instance void[UnityEngine.AnimationModule] UnityEngine.Animation::Stop(string)
// to :
// anim[animationName].speed = 0f;
//IL_0091: ldarg.0
//IL_0092: ldfld class [UnityEngine.AnimationModule]
//IL_0097: ldarg.0
//IL_0098: ldfld string ModuleDeployablePart::animationName
//IL_0082: callvirt instance class [UnityEngine.AnimationModule] UnityEngine.AnimationState[UnityEngine.AnimationModule] UnityEngine.Animation::get_Item(string)
//IL_0087: ldc.r4 0
//IL_008c: callvirt instance void[UnityEngine.AnimationModule] UnityEngine.AnimationState::set_Speed(float32)
MethodInfo Animation_Stop = AccessTools.Method(typeof(Animation), nameof(Animation.Stop), new Type[]{typeof(string)});
MethodInfo Animation_get_Item = AccessTools.Method(typeof(Animation), "get_Item");
MethodInfo AnimationState_set_Speed = AccessTools.PropertySetter(typeof(AnimationState), nameof(AnimationState.speed));
List<CodeInstruction> code = new List<CodeInstruction>(instructions);
for (int i = 0; i < code.Count; i++)
{
if (code[i].opcode == OpCodes.Callvirt && ReferenceEquals(code[i].operand, Animation_Stop))
{
code.Insert(i, new CodeInstruction(OpCodes.Callvirt, Animation_get_Item));
code.Insert(i + 1, new CodeInstruction(OpCodes.Ldc_R4, 0f));
code[i + 2].operand = AnimationState_set_Speed;
break;
}
}
return code;
}
static IEnumerable<CodeInstruction> ModuleDeployableSolarPanel_OnStart_Transpiler(IEnumerable<CodeInstruction> instructions)
{
// here no need to add anything, stock already does
//float normalizedTime = ((deployState == DeployState.EXTENDED) ? 1f : 0f);
//anim[animationName].normalizedTime = normalizedTime;
//anim[animationName].enabled = true;
//anim[animationName].weight = 1f;
// but after that it does :
//if (deployState == DeployState.RETRACTED)
//{
// anim.Stop(animationName);
//}
// We remove that entire if statement by replacing the if (Brtrue_S) by an unconditional jump (Br_S)
FieldInfo ModuleDeployablePart_deployState = AccessTools.Field(typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.deployState));
List<CodeInstruction> code = new List<CodeInstruction>(instructions);
for (int i = 0; i < code.Count; i++)
{
if (code[i].opcode == OpCodes.Ldarg_0
&& code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i+1].operand, ModuleDeployablePart_deployState)
&& code[i + 2].opcode == OpCodes.Brtrue_S)
{
code[i].opcode = OpCodes.Br_S;
code[i].operand = code[i + 2].operand; // grab the target instruction from the original jump
code[i + 1].opcode = OpCodes.Nop; // don't generate invalid IL
code[i + 1].operand = null;
code[i + 2].opcode = OpCodes.Nop; // don't generate invalid IL
code[i + 2].operand = null;
break;
}
}
return code;
}
}
}