-
Notifications
You must be signed in to change notification settings - Fork 5
/
JammingEnabler.cs
224 lines (199 loc) · 8.77 KB
/
JammingEnabler.cs
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BattleTech;
using Harmony;
using Localize;
using UnityEngine;
using Random = UnityEngine.Random;
namespace WeaponRealizer
{
internal static class StatisticHelper
{
internal static Statistic GetOrCreateStatisic<StatisticType>(StatCollection collection, string statName,
StatisticType defaultValue)
{
Statistic statistic = collection.GetStatistic(statName);
if (statistic == null)
{
statistic = collection.AddStatistic<StatisticType>(statName, defaultValue);
}
return statistic;
}
}
[HarmonyPatch(typeof(AbstractActor), "OnActivationEnd", MethodType.Normal)]
static class JammingEnabler
{
public static bool Prepare()
{
return Core.ModSettings.Jamming;
}
static void Prefix(AbstractActor __instance)
{
var actor = __instance;
if (actor.IsShutDown) return;
foreach (Weapon weapon in actor.Weapons)
{
if (!IsJammable(weapon)) continue;
if (IsJammed(weapon))
{
var removedJam = AttemptToRemoveJam(actor, weapon);
Logger.Debug($"Removed Jam? {removedJam}");
}
else if (weapon.roundsSinceLastFire == 0) // fired this round
{
var addedJam = AttemptToAddJam(actor, weapon);
Logger.Debug($"Added Jam? {addedJam}");
}
}
}
private static float GetRefireModifier(Weapon weapon)
{
if (weapon.RefireModifier > 0 && weapon.roundsSinceLastFire < 2)
return (float) weapon.RefireModifier;
return 0.0f;
}
private static bool AttemptToAddJam(AbstractActor actor, Weapon weapon)
{
// TODO: can we exponentially increase refiremodifier?
// LadyAlekto: every turn fired, the refiremodifier gets added
// LadyAlekto: either as toggle or global
// LadyAlekto: and when you brace a turn, it resets
// LadyAlekto: brace as in "dont shoot"
var refireModifier = GetRefireModifier(weapon);
var roll = Random.Range(1, 100);
var skill = actor.SkillGunnery;
var mitigationRoll = Random.Range(2, 11);
var multiplier = JamMultipliers[weapon.defId];
StringBuilder sb = new StringBuilder();
sb.AppendLine($"damaged: {weapon.DamageLevel}");
sb.AppendLine($"refiremod: {refireModifier}");
sb.AppendLine($"roll: {roll}");
sb.AppendLine($"gunneryskill: {skill}");
sb.AppendLine($"mitigationRoll: {mitigationRoll}");
sb.AppendLine($"multiplier: {multiplier}");
if (roll >= refireModifier * multiplier)
{
Logger.Debug(sb.ToString());
return false;
}
if (skill >= mitigationRoll)
{
Logger.Debug(sb.ToString());
return false;
}
Logger.Debug(sb.ToString());
AddJam(actor, weapon);
return true;
}
private static bool AttemptToRemoveJam(AbstractActor actor, Weapon weapon)
{
var skill = actor.SkillGunnery;
var mitigationRoll = Random.Range(1, 10);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"gunneryskill: {skill}");
sb.AppendLine($"mitigationRoll: {mitigationRoll}");
if (skill >= mitigationRoll)
{
Logger.Debug(sb.ToString());
RemoveJam(actor, weapon);
return true;
}
Logger.Debug(sb.ToString());
return false;
}
private const string TemporarilyDisabledStatisticName = "TemporarilyDisabled";
private const string DamageableWeaponTag = "wr-damage_when_jam";
internal const string JammedWeaponStatisticName = "WR-JammedWeapon";
private const string JammableWeaponTag = "wr-jammable_weapon";
private static void AddJam(AbstractActor actor, Weapon weapon)
{
if (!DamagesWhenJams(weapon))
{
weapon.StatCollection.Set<bool>(JammedWeaponStatisticName, true);
weapon.StatCollection.Set<bool>(TemporarilyDisabledStatisticName, true);
actor.Combat.MessageCenter.PublishMessage(
new AddSequenceToStackMessage(
new ShowActorInfoSequence(actor, $"{weapon.Name} Jammed!", FloatieMessage.MessageNature.Debuff,
true)));
}
else
{
var isDestroying = weapon.DamageLevel != ComponentDamageLevel.Functional;
var damageLevel = isDestroying ? ComponentDamageLevel.Destroyed : ComponentDamageLevel.Penalized;
var fakeHit = new WeaponHitInfo(-1, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, AttackDirection.None, Vector2.zero, null);
weapon.DamageComponent(fakeHit, damageLevel, true);
var message = isDestroying
? $"{weapon.Name} misfire: Destroyed!"
: $"{weapon.Name} misfire: Damaged!";
actor.Combat.MessageCenter.PublishMessage(
new AddSequenceToStackMessage(
new ShowActorInfoSequence(actor, message, FloatieMessage.MessageNature.Debuff, true)));
}
}
private static void RemoveJam(AbstractActor actor, Weapon weapon)
{
weapon.StatCollection.Set<bool>(JammedWeaponStatisticName, false);
weapon.StatCollection.Set<bool>(TemporarilyDisabledStatisticName, false);
actor.Combat.MessageCenter.PublishMessage(
new AddSequenceToStackMessage(
new ShowActorInfoSequence(actor, $"{weapon.Name} Unjammed!", FloatieMessage.MessageNature.Buff,
true)));
}
private static bool IsJammed(Weapon weapon)
{
var statistic =
StatisticHelper.GetOrCreateStatisic<bool>(weapon.StatCollection, JammedWeaponStatisticName, false);
return statistic.Value<bool>();
}
private static bool IsJammable(Weapon weapon)
{
return weapon.weaponDef.ComponentTags.Contains(JammableWeaponTag, StringComparer.InvariantCultureIgnoreCase)
&& HasJamMultiplier(weapon);
}
private static readonly Dictionary<string, float> JamMultipliers = new Dictionary<string, float>();
private static bool HasJamMultiplier(Weapon weapon)
{
if (!JamMultipliers.ContainsKey(weapon.defId))
JamMultipliers[weapon.defId] = ParseBaseMultiplier(weapon);
return JamMultipliers[weapon.defId] > Calculator.Epsilon;
}
private static bool DamagesWhenJams(Weapon weapon)
{
return weapon.weaponDef.ComponentTags.Contains(DamageableWeaponTag, StringComparer.InvariantCultureIgnoreCase);
}
private const string JamMultiplierTagPrefix = "wr-jam_chance_multiplier";
private static readonly char[] TagDelimiter = new char[] {'-'};
private static float ParseBaseMultiplier(Weapon weapon)
{
if (!weapon.weaponDef.ComponentTags.Any(tag =>
tag.StartsWith(JamMultiplierTagPrefix, StringComparison.InvariantCultureIgnoreCase)))
return 0.0f;
var rawTag = weapon.weaponDef.ComponentTags.First(tag =>
tag.StartsWith(JamMultiplierTagPrefix, StringComparison.InvariantCultureIgnoreCase));
var multiplier =
rawTag.Equals(JamMultiplierTagPrefix, StringComparison.InvariantCultureIgnoreCase)
? Core.ModSettings.JamChanceMultiplier
: float.Parse(rawTag.Split(TagDelimiter, 3).Last());
return multiplier;
}
}
[HarmonyPatch(typeof(MechComponent), "UIName", MethodType.Getter)]
static class JammedWeaponDisplayChanger
{
public static bool Prepare()
{
return Core.ModSettings.Jamming;
}
public static void Postfix(MechComponent __instance, ref Text __result)
{
if (!__instance.IsFunctional) return;
if (__instance.GetType() != typeof(Weapon)) return;
if (!StatisticHelper
.GetOrCreateStatisic<bool>(__instance.StatCollection, JammingEnabler.JammedWeaponStatisticName, false)
.Value<bool>()) return;
__result.Append(" (JAM)", new object[0]);
}
}
}