Skip to content

Commit 1c5f2e5

Browse files
committed
Fixes for v49, folder migration
1 parent a8c655f commit 1c5f2e5

File tree

19 files changed

+484
-86
lines changed

19 files changed

+484
-86
lines changed

BloonsTD6 Mod Helper/Api/Enums/UpgradeType.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,21 @@ public static class UpgradeType
9797
public const string MOABGlue = "MOAB Glue";
9898
public const string RelentlessGlue = "Relentless Glue";
9999
public const string SuperGlue = "Super Glue";
100+
public const string Quickdraw = "Quickdraw";
101+
public const string Standoff = "Standoff";
102+
public const string BigIron = "Big Iron";
103+
public const string TwinSixes = "Twin Sixes";
104+
public const string TheBlazingSun = "The Blazing Sun";
105+
public const string EagleEye = "Eagle Eye";
106+
public const string Bullseye = "Bullseye";
107+
public const string Deadeye = "Deadeye";
108+
public const string BountyHunter = "Bounty Hunter";
109+
public const string GoldenJustice = "Golden Justice";
110+
public const string Wanderer = "Wanderer";
111+
public const string Nomad = "Nomad";
112+
public const string Enforcer = "Enforcer";
113+
public const string Avenger = "Avenger";
114+
public const string TheDesertPhantom = "The Desert Phantom";
100115
public const string FullMetalJacket = "Full Metal Jacket";
101116
public const string LargeCalibre = "Large Calibre";
102117
public const string DeadlyPrecision = "Deadly Precision";
@@ -773,6 +788,21 @@ public static class UpgradeType
773788
{ "MOABGlue", MOABGlue },
774789
{ "RelentlessGlue", RelentlessGlue },
775790
{ "SuperGlue", SuperGlue },
791+
{ "Quickdraw", Quickdraw },
792+
{ "Standoff", Standoff },
793+
{ "BigIron", BigIron },
794+
{ "TwinSixes", TwinSixes },
795+
{ "TheBlazingSun", TheBlazingSun },
796+
{ "EagleEye", EagleEye },
797+
{ "Bullseye", Bullseye },
798+
{ "Deadeye", Deadeye },
799+
{ "BountyHunter", BountyHunter },
800+
{ "GoldenJustice", GoldenJustice },
801+
{ "Wanderer", Wanderer },
802+
{ "Nomad", Nomad },
803+
{ "Enforcer", Enforcer },
804+
{ "Avenger", Avenger },
805+
{ "TheDesertPhantom", TheDesertPhantom },
776806
{ "FullMetalJacket", FullMetalJacket },
777807
{ "LargeCalibre", LargeCalibre },
778808
{ "DeadlyPrecision", DeadlyPrecision },

BloonsTD6 Mod Helper/Api/Enums/VanillaSprites.cs

Lines changed: 229 additions & 23 deletions
Large diffs are not rendered by default.

BloonsTD6 Mod Helper/Api/Helpers/AttackHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public AttackHelper(string name = "", bool airUnit = false) : this(airUnit
183183
{
184184
new FilterInvisibleModel("", true, false)
185185
})
186-
}, null, 0, 0, 0, false, false, 0, true, 0))
186+
}, null, 0, 0, 0, false, false, 0, true, 0, false)) // TODO should this be true
187187
{
188188
}
189189

BloonsTD6 Mod Helper/Api/Helpers/FileIOHelper.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,31 @@ public static string LoadFile(string fileName)
8484
var path = Path.Combine(sandboxRoot, fileName);
8585
return File.Exists(path) ? File.ReadAllText(path) : null;
8686
}
87+
88+
/// <summary>
89+
/// Recursively copies all files and subdirectories from the source directory
90+
/// to the destination directory. Creates directories as needed.
91+
/// </summary>
92+
/// <param name="sourceDir">The path to the directory to copy from.</param>
93+
/// <param name="destinationDir">The path to the directory to copy to.</param>
94+
/// <param name="overwrite">If true, existing files in the destination will be overwritten.</param>
95+
public static void CopyDirectory(string sourceDir, string destinationDir, bool overwrite = true)
96+
{
97+
Directory.CreateDirectory(destinationDir);
98+
99+
foreach (var filePath in Directory.GetFiles(sourceDir))
100+
{
101+
var fileName = Path.GetFileName(filePath);
102+
var destFilePath = Path.Combine(destinationDir, fileName);
103+
File.Copy(filePath, destFilePath, overwrite);
104+
}
105+
106+
foreach (var dirPath in Directory.GetDirectories(sourceDir))
107+
{
108+
var dirName = Path.GetFileName(dirPath);
109+
var destSubDir = Path.Combine(destinationDir, dirName);
110+
CopyDirectory(dirPath, destSubDir, overwrite);
111+
}
112+
}
113+
87114
}

BloonsTD6 Mod Helper/Api/Internal/ModContentTask.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,8 @@ public override IEnumerator Coroutine()
5151
}
5252
catch (Exception e)
5353
{
54-
ModHelper.Error($"Failed to register {modContent.Id}");
54+
mod.LoadError($"Failed to register {modContent.Name}");
5555
ModHelper.Error(e);
56-
mod.loadErrors.Add($"Failed to register {modContent.Name}");
5756

5857
foreach (var rollbackAction in modContent.rollbackActions)
5958
{

BloonsTD6 Mod Helper/Api/Internal/TemplateMod.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
using System.IO.Compression;
55
using System.Linq;
66
using System.Threading.Tasks;
7+
using BTD_Mod_Helper.Api.Helpers;
78
using BTD_Mod_Helper.Api.ModMenu;
89
using Il2CppAssets.Scripts.Unity.UI_New.Popups;
9-
using Microsoft.VisualBasic.FileIO;
1010
using SearchOption = System.IO.SearchOption;
1111
#pragma warning disable CS4014
1212
namespace BTD_Mod_Helper.Api.Internal;
@@ -78,7 +78,7 @@ private static async Task CreateProject(string path, string name)
7878
{
7979
if (e.Message.Contains("across volumes"))
8080
{
81-
FileSystem.CopyDirectory(directory.FullName, path, true);
81+
FileIOHelper.CopyDirectory(directory.FullName, path);
8282
directory.Delete(true);
8383
}
8484
else

BloonsTD6 Mod Helper/Api/Legends/ModArtifact.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ internal sealed override void SetArtifactModel(ItemArtifactData artifact, ItemAr
399399
baseId = insta,
400400
tiers = new Il2CppStructArray<int>(tiers)
401401
}
402-
: new RogueInstaMonkey());
402+
: new RogueInstaMonkey(), 1);
403403
}
404404

405405
/// <summary>
@@ -436,5 +436,5 @@ internal sealed override void SetArtifactModel(MapArtifactData artifact, MapArti
436436
/// <inheritdoc />
437437
protected sealed override MapArtifactModel CreateArtifactModel(int tier, int index) => new(GetId(index),
438438
tier, Id, new Il2CppReferenceArray<MapArtifactBehaviorModel>(0), GetId(index),
439-
GetId(index) + "Description", new Il2CppStringArray(0), GetIcon(tier), RarityFrameType.ToString(), false, false);
439+
GetId(index) + "Description", new Il2CppStringArray(0), GetIcon(tier), RarityFrameType.ToString(), false, false, 1);
440440
}

BloonsTD6 Mod Helper/BloonsMod.cs

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Linq;
55
using System.Linq.Expressions;
66
using System.Reflection;
7-
87
using BTD_Mod_Helper.Api;
98
using BTD_Mod_Helper.Api.Data;
109
using BTD_Mod_Helper.Api.Hooks;
@@ -196,59 +195,70 @@ public void ApplyHarmonyPatches(Type type)
196195
/// <summary>
197196
/// Tries to apply all mod hooks in the calling assembly, failing gracefully on errors.
198197
/// </summary>
199-
public void ApplyModHooks() {
198+
public void ApplyModHooks()
199+
{
200200
var allHookMethods =
201201
GetType().Assembly.DefinedTypes
202202
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
203-
.Select(m => (Method: m, Attr: m.GetCustomAttribute<HookTargetAttribute>())))
203+
.Select(m => (Method: m, Attr: m.GetCustomAttribute<HookTargetAttribute>())))
204204
.Where(x => x.Attr != null)
205-
.Where(x => {
205+
.Where(x =>
206+
{
206207
var ok = x.Attr!.TargetType.IsAssignableToGenericType(typeof(ModHook<,>));
207208
if (!ok)
208209
MelonLogger.Warning(
209210
$"Failed to apply hook {x.Method.Name}: {x.Attr!.TargetType.FullName} is not a ModHook<,>");
210211
return ok;
211212
});
212213

213-
foreach (var (methodInfo, hook) in allHookMethods) {
214-
try {
214+
foreach (var (methodInfo, hook) in allHookMethods)
215+
{
216+
try
217+
{
215218
var hookType = hook!.TargetType;
216219
var genArgs = hookType.BaseType!.GetGenericArguments();
217220
var delegateType = genArgs[1];
218-
var invokeInfo = delegateType.GetMethod("Invoke")
219-
?? throw new InvalidOperationException($"Delegate {delegateType} has no Invoke()");
221+
var invokeInfo = delegateType.GetMethod("Invoke") ??
222+
throw new InvalidOperationException($"Delegate {delegateType} has no Invoke()");
220223

221224
var userParams = methodInfo.GetParameters();
222225
var delegateParams = invokeInfo.GetParameters();
223226

224-
var exactMatch = userParams.Length == delegateParams.Length
225-
&& !delegateParams.Where((dp, i) =>
226-
dp.ParameterType.IsByRef != userParams[i].ParameterType.IsByRef
227-
|| (
228-
dp.ParameterType != userParams[i].ParameterType
229-
&& dp.ParameterType.GetElementType() != userParams[i].ParameterType.GetElementType()
230-
)).Any();
227+
var exactMatch = userParams.Length == delegateParams.Length &&
228+
!delegateParams.Where((dp, i) =>
229+
dp.ParameterType.IsByRef != userParams[i].ParameterType.IsByRef ||
230+
(
231+
dp.ParameterType != userParams[i].ParameterType &&
232+
dp.ParameterType.GetElementType() != userParams[i].ParameterType.GetElementType()
233+
)).Any();
231234

232235
Delegate hookDelegate;
233-
if (exactMatch) {
236+
if (exactMatch)
237+
{
234238
hookDelegate = methodInfo.CreateDelegate(delegateType);
235-
} else {
239+
}
240+
else
241+
{
236242
var lambdaParams = delegateParams
237243
.Select(p => Expression.Parameter(p.ParameterType, p.Name ?? $"arg{p.Position}"))
238244
.ToArray();
239245

240246
var byName = lambdaParams.ToDictionary(pe => pe.Name!, pe => pe, StringComparer.OrdinalIgnoreCase);
241247

242248
var instEntry = byName
243-
.FirstOrDefault(kvp => kvp.Key.Equals("instance", StringComparison.OrdinalIgnoreCase)
244-
|| kvp.Key.Equals("this", StringComparison.OrdinalIgnoreCase));
245-
if (instEntry.Value != null) {
249+
.FirstOrDefault(kvp =>
250+
kvp.Key.Equals("instance", StringComparison.OrdinalIgnoreCase) ||
251+
kvp.Key.Equals("this", StringComparison.OrdinalIgnoreCase));
252+
if (instEntry.Value != null)
253+
{
246254
byName["@this"] = instEntry.Value;
247255
byName["__instance"] = instEntry.Value;
248256
}
249257

250-
var resEntry = byName.FirstOrDefault(kvp => kvp.Key.Equals("result", StringComparison.OrdinalIgnoreCase));
251-
if (resEntry.Value != null) {
258+
var resEntry =
259+
byName.FirstOrDefault(kvp => kvp.Key.Equals("result", StringComparison.OrdinalIgnoreCase));
260+
if (resEntry.Value != null)
261+
{
252262
byName["@result"] = resEntry.Value;
253263
byName["__result"] = resEntry.Value;
254264
}
@@ -257,28 +267,33 @@ public void ApplyModHooks() {
257267
var preAssigns = new List<Expression>();
258268
var callArgs = new Expression[userParams.Length];
259269

260-
for (var i = 0; i < userParams.Length; i++) {
270+
for (var i = 0; i < userParams.Length; i++)
271+
{
261272
var up = userParams[i];
262273
if (!byName.TryGetValue(up.Name!, out var src))
263274
throw new InvalidOperationException($"Cannot bind parameter '{up.Name}' in {methodInfo.Name}");
264275

265276
var srcType = src.Type;
266277
var tgtType = up.ParameterType;
267278

268-
switch (srcType.IsByRef) {
269-
case true when !tgtType.IsByRef: {
279+
switch (srcType.IsByRef)
280+
{
281+
case true when !tgtType.IsByRef:
282+
{
270283
var elem = srcType.GetElementType()!;
271284
var unary = Expression.Convert(src, elem);
272285
callArgs[i] = elem != tgtType ? Expression.Convert(unary, tgtType) : (Expression) unary;
273286
break;
274287
}
275-
case true when tgtType.IsByRef: {
288+
case true when tgtType.IsByRef:
289+
{
276290
if (tgtType.GetElementType() != srcType.GetElementType())
277291
throw new InvalidOperationException($"Mismatched by-ref for '{up.Name}'");
278292
callArgs[i] = src;
279293
break;
280294
}
281-
case false when tgtType.IsByRef: {
295+
case false when tgtType.IsByRef:
296+
{
282297
var elem = tgtType.GetElementType()!;
283298
var localVar = Expression.Variable(elem, up.Name + "_local");
284299
locals.Add(localVar);
@@ -299,7 +314,8 @@ public void ApplyModHooks() {
299314
if (methodInfo.ReturnType != invokeInfo.ReturnType)
300315
body = Expression.Convert(body, invokeInfo.ReturnType);
301316

302-
if (locals.Count > 0) {
317+
if (locals.Count > 0)
318+
{
303319
var blockExpressions = new List<Expression>();
304320
blockExpressions.AddRange(preAssigns);
305321
blockExpressions.Add(body);
@@ -312,13 +328,16 @@ public void ApplyModHooks() {
312328
var addName = hook.HookType == HookTargetAttribute.EHookType.Prefix
313329
? "AddPrefix"
314330
: "AddPostfix";
315-
var addMethod = hook.TargetType.GetMethod(addName, BindingFlags.Instance | BindingFlags.Public)
316-
?? throw new MissingMethodException(hook.TargetType.FullName, addName);
331+
var addMethod = hook.TargetType.GetMethod(addName, BindingFlags.Instance | BindingFlags.Public) ??
332+
throw new MissingMethodException(hook.TargetType.FullName, addName);
317333

318334
var hookInstance = ModContent.GetInstance(hook.TargetType)!;
319335
addMethod.Invoke(hookInstance, [hookDelegate]);
320-
} catch (Exception ex) {
321-
MelonLogger.Error($"Exception while applying hook {methodInfo.DeclaringType!.FullName}::{methodInfo.Name}: {ex}");
336+
}
337+
catch (Exception ex)
338+
{
339+
MelonLogger.Error(
340+
$"Exception while applying hook {methodInfo.DeclaringType!.FullName}::{methodInfo.Name}: {ex}");
322341
}
323342
}
324343
}
@@ -349,6 +368,12 @@ public sealed override void OnInitializeMelon()
349368
/// </summary>
350369
public void SaveModSettings() => ModSettingsHandler.SaveModSettings(this);
351370

371+
internal void LoadError(object message)
372+
{
373+
LoggerInstance.Error(message);
374+
loadErrors.Add(message?.ToString());
375+
}
376+
352377
/// <inheritdoc cref="OnInitializeMelon" />
353378
public new virtual void OnApplicationStart()
354379
{

BloonsTD6 Mod Helper/Extensions/BehaviorExtensions/ArtifactModelBehaviorExt.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public static void AddTowerBehaviors(this ItemArtifactModel artifact,
164164
var boost = new AddTowerBehaviorsArtifactModel("", new Il2CppStringArray(0),
165165
new Il2CppStringArray(0), new Il2CppStringArray(0), new Il2CppStringArray(0),
166166
new Il2CppStringArray([]), false, new Il2CppStructArray<TowerSet>(0), false,
167-
new Il2CppStructArray<int>([]), false, false, false)
167+
new Il2CppStructArray<int>([]), false, false, false, false)
168168
{
169169
behaviorModels = towerBehaviors.ToIl2CppReferenceArray()
170170
};
@@ -198,7 +198,7 @@ public static void AddProjectileBehaviors(this ItemArtifactModel artifact,
198198
var boost = new AddProjectileBehaviorsArtifactModel("", new Il2CppStringArray(0),
199199
new Il2CppStringArray(0), new Il2CppStringArray(0), new Il2CppStringArray(0),
200200
new Il2CppStringArray([]), false, new Il2CppStructArray<TowerSet>(0), false,
201-
new Il2CppStructArray<int>([]), false, false, false)
201+
new Il2CppStructArray<int>([]), false, false, false, false)
202202
{
203203
behaviorModels = projectileBehaviors.ToIl2CppReferenceArray()
204204
};

BloonsTD6 Mod Helper/Extensions/SimulationExtensions/BloonExt.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public static BloonToSimulation GetBloonToSim(this Bloon bloon)
7777
/// <param name="projectile"></param>
7878
/// <returns></returns>
7979
public static bool WillPopBloon(this Bloon bloon, Projectile projectile) =>
80-
bloon.WillPopBloon(projectile.projectileModel.GetDamageModel());
80+
bloon.WillPopBloon(projectile.projectileModel.GetDamageModel(), projectile);
8181

8282
/// <summary>
8383
/// Returns whether or not the bloon was popped rather than leaked.

BloonsTD6 Mod Helper/LATEST.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,15 @@
1-
- Internal fixes and improvements for the ModHook system from @MatthewOGuerra
1+
- Fixes for BTD6 v49
2+
- Updated VanillaSprites and UpgradeType references for the new Desperado tower
3+
- [Added a few new helpful api methods](https://github.com/gurrenm3/BTD-Mod-Helper/pull/301) (Thanks @DarkTerraYT !)
4+
5+
### Folder Location Change
6+
7+
To prepare for an upcoming new version of MelonLoader, some directories created by Mod Helper will be moving to a new
8+
location.
9+
This should happen automatically on startup; any issues will display as load warning for Mod Helper in the mods menu
10+
that can be addressed by just moving the folders manually if need be.
11+
12+
- The `BloonsTD6 Mod Helper` folder that previously was within the `Mods` folder is now the `BTD6ModHelper` folder
13+
within the root `BloonsTD6` directory
14+
- The `Disabled` folder that previously was within the `Mods` folder is now the `Disabled Mods` folder within the root
15+
`BloonsTD6` directory

BloonsTD6 Mod Helper/MelonMain.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ internal partial class MelonMain : BloonsTD6Mod
3333
public override void OnInitialize()
3434
{
3535
ModContentInstances.AddInstance(GetType(), this);
36+
ModHelper.MigrateFolders();
3637

3738
// Create all and load default mod settings
3839
ModSettingsHandler.InitializeModSettings();

0 commit comments

Comments
 (0)