Skip to content

Commit

Permalink
Merge branch 'gurrenm3:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkTerraYT authored Oct 24, 2024
2 parents 810931b + ac7d1c2 commit c5815f3
Show file tree
Hide file tree
Showing 25 changed files with 680 additions and 82 deletions.
34 changes: 30 additions & 4 deletions BloonsTD6 Mod Helper/Api/Commands/ExportDisplayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
using Il2CppAssets.Scripts.Models.GenericBehaviors;
using Il2CppAssets.Scripts.Unity;
using Il2CppAssets.Scripts.Unity.Display;
using Il2CppAssets.Scripts.Unity.Display.Animation;
using Il2CppAssets.Scripts.Unity.UI_New.InGame.TowerSelectionMenu;
using Il2CppNinjaKiwi.Common.ResourceUtils;
using UnityEngine;
namespace BTD_Mod_Helper.Api.Commands;

internal class ExportDisplayCommand : ModCommand<ExportCommand>
{
public override string Command => "display";
public override string Help => "Exports the textures and UnityDisplayNode information for the selected tower / other GUIDs";

public override string Help =>
"Exports the textures and UnityDisplayNode information for the selected tower / other GUIDs";

[Option('o', "open", Default = false, HelpText = "Also open the folder where pngs is exported")]
public bool Open { get; set; }
Expand Down Expand Up @@ -54,10 +58,32 @@ private void Export(UnityDisplayNode node)
{
if (renderer?.material?.mainTexture == null) continue;

var path = Path.Combine(FileIOHelper.sandboxRoot, renderer.name + ".png");
renderer.material.mainTexture.TrySaveToPNG(path);
if (renderer.gameObject.HasComponent(out CustomSpriteFrameAnimator customSpriteFrameAnimator))
{
var i = 0;
foreach (var frame in customSpriteFrameAnimator.frames)
{
var path = Path.Combine(FileIOHelper.sandboxRoot, renderer.name + ".png");
frame.TrySaveToPNG(path);
ModHelper.Msg($"Saved {path}");
i++;
}
}
else
{
var path = Path.Combine(FileIOHelper.sandboxRoot, renderer.name + ".png");
if (renderer.Is(out SpriteRenderer spriteRenderer))
{
spriteRenderer.sprite.texture?.TrySaveToPNG(path);
}
else
{
renderer.material.mainTexture.TrySaveToPNG(path);
}

ModHelper.Msg($"Saved {path}");
}

ModHelper.Msg($"Saved {path}");
}

node.PrintInfo();
Expand Down
54 changes: 54 additions & 0 deletions BloonsTD6 Mod Helper/Api/Commands/ExportImageCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.IO;
using BTD_Mod_Helper.Api.Helpers;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace BTD_Mod_Helper.Api.Commands;

internal class ExportImageCommand : ModCommand<ExportCommand>
{
public override string Command => "image";
public override string Help => "Exports UI images raycasted from the current mouse position";

public override bool Execute(ref string resultText)
{
try
{
var exported = new List<string>();
var raycastResults = new Il2CppSystem.Collections.Generic.List<RaycastResult>();
EventSystem.current.RaycastAll(new PointerEventData(EventSystem.current)
{
position = Input.mousePosition
}, raycastResults);

var imagesFolder = Path.Combine(FileIOHelper.sandboxRoot, "Images");

foreach (var result in raycastResults)
{
if (result.gameObject.Is(out var gameObject) &&
gameObject.HasComponent(out Image image) &&
image.sprite != null)
{
var path = Path.Combine(imagesFolder, image.sprite.name + ".png");
if (image.sprite.TrySaveToPNG(path))
{
ModHelper.Msg($"Exported {path}");
exported.Add(image.sprite.name);
}
}
}

resultText = $"Exported {exported.Join()} to {imagesFolder}";

}
catch (Exception e)
{
resultText = e.Message;
ModHelper.Warning(e);
}

return true;
}
}
187 changes: 187 additions & 0 deletions BloonsTD6 Mod Helper/Api/Display/ModBloonOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Il2CppAssets.Scripts.Data;
using Il2CppAssets.Scripts.Data.Bloons;
using Il2CppAssets.Scripts.Models.Towers.Projectiles;
using Il2CppNinjaKiwi.Common;
using Il2CppNinjaKiwi.Common.ResourceUtils;
using MelonLoader.Utils;
using UnityEngine;
namespace BTD_Mod_Helper.Api.Display;

/// <summary>
/// A special ModDisplay for Bloon Overlays. Handles automatically loading different instances of itself for each BloonOverlayClass
/// </summary>
public abstract class ModBloonOverlay : ModDisplay
{
/// <summary>
/// Whether the current MelonLoader version will support registering a BloonOverlay
/// </summary>
public static bool WorksOnCurrentMelonLoader => typeof(MelonEnvironment).Assembly.GetName().Version is
{Major: 0, Minor: 6, Build: >= 6} or {Major: 0, Minor: 6, Build: <= 1} or {Major: 0, Minor: > 6} or {Major: > 0};

/// <summary>
/// Quick getter for all overlays
/// </summary>
protected static SerializableDictionary<string, BloonOverlayScriptable> AllOverlayTypes =>
GameData.Instance.bloonOverlays.overlayTypes;

/// <summary>
/// The overlay class that this is for
/// </summary>
public BloonOverlayClass OverlayClass { get; private set; }

/// <summary>
/// The base Bloon Overlay to copy from.
/// <br/>
/// These come from the <see cref="ProjectileBehaviorWithOverlayModel.overlayType"/> fields of certain projectile behavior models
/// <br/>
/// To not copy from any Base Overlay, keep this as null/empty and modify <see cref="ModDisplay.BaseDisplay"/> or <see cref="ModDisplay.BaseDisplayReference"/> instead
/// </summary>
public virtual string BaseOverlay => null;

/// <summary>
/// <inheritdoc />
/// <br/>
/// If <see cref="BaseOverlay"/> is defined, will automatically get the correct display for each OverlayClass from there
/// </summary>
public override string BaseDisplay => string.IsNullOrEmpty(BaseOverlay)
? ""
: AllOverlayTypes[BaseOverlay].assets[BaseOverlayClass].guidRef;

/// <summary>
/// The <see cref="BloonOverlayScriptable.displayLayer"/> of the overlay
/// </summary>
public virtual int DisplayLayer =>
!string.IsNullOrEmpty(BaseOverlay) && AllOverlayTypes.TryGetValue(BaseOverlay, out var o) ? o.displayLayer : 0;

private bool hasOverlayClass;
private protected override string ID => hasOverlayClass ? BaseId + "-" + OverlayClass : BaseId;
private string BaseId => GetId(mod, Name);

/// <summary>
/// Lets you control which BloonOverlayClass from the BaseOverlay is used for each BloonOverlayClass of your custom overlay.
/// By default, uses the same <see cref="OverlayClass"/> as the base.
/// <br/>
/// <example>
/// To make the non regrow bloons use the same overlays as the regrow bloons
/// <code>
/// public override BloonOverlayClass BaseOverlayClass => OverlayClass switch
/// {
/// BloonOverlayClass.Red => BloonOverlayClass.RedRegrow,
/// BloonOverlayClass.Blue => BloonOverlayClass.BlueRegrow,
/// BloonOverlayClass.Green => BloonOverlayClass.GreenRegrow,
/// BloonOverlayClass.Yellow => BloonOverlayClass.YellowRegrow,
/// BloonOverlayClass.Pink => BloonOverlayClass.PinkRegrow,
/// BloonOverlayClass.White => BloonOverlayClass.WhiteRegrow,
/// _ => OverlayClass
/// };
/// </code>
/// </example>
/// </summary>
public virtual BloonOverlayClass BaseOverlayClass => OverlayClass;

/// <summary>
/// Full list of BloonOverlayClasses to try to load this overlay for, defaults to all of them
/// </summary>
public virtual IEnumerable<BloonOverlayClass> BloonOverlayClasses => Enum.GetValues<BloonOverlayClass>();

/// <summary>
/// Which overlay type this Overlay uses
/// </summary>
public string OverlayType => WorksOnCurrentMelonLoader ? BaseId : BaseOverlay;

/// <summary>
/// Load different instances of this type for each difference BloonOverlayClass within <see cref="BloonOverlayClasses"/>
/// </summary>
public override IEnumerable<ModContent> Load() => WorksOnCurrentMelonLoader
? BloonOverlayClasses.Select(bc =>
{
try
{
var overlay = (ModBloonOverlay) Activator.CreateInstance(GetType());
if (overlay != null)
{
overlay.mod = mod;
overlay.OverlayClass = bc;
overlay.hasOverlayClass = true;
return overlay;
}
}
catch (Exception e)
{
ModHelper.Error(e);
}

ModHelper.Error($"Unable to create {Id} {bc}, does it ");
return null;
})
: [];

/// <inheritdoc />
public override void Register()
{
if (!WorksOnCurrentMelonLoader)
{
ModHelper.Warning("Custom Bloon Overlays sadly can't be added on this version of MelonLoader due to a bug");
return;
}

if (!hasOverlayClass)
{
ModHelper.Error("Should not reach point of registering base ModBloonOverlay with no overlay class");
return;
}

if (!string.IsNullOrEmpty(BaseOverlay))
{
if (!AllOverlayTypes.TryGetValue(BaseOverlay, out var baseOverlay))
{
ModHelper.Error($"Failed to register {Id}, no base overlay {BaseOverlay}");
return;
}

if (!baseOverlay.assets.ContainsKey(BaseOverlayClass))
{
// Not an error, just doesn't have one for this overlay class
return;
}
}

try
{
var guid = BaseDisplayReference.guidRef;
if (string.IsNullOrEmpty(guid))
{
ModHelper.Warning($"ModBloonOverlay {Id} has no base display");
}
}
catch (Exception e)
{
ModHelper.Error($"Unable to get BaseDisplayReference for {Id}");
ModHelper.Error(e);
return;
}

base.Register();

if (!AllOverlayTypes.TryGetValue(BaseId, out var overlay))
{
overlay = AllOverlayTypes[BaseId] = ScriptableObject.CreateInstance<BloonOverlayScriptable>();
overlay.displayLayer = DisplayLayer;
overlay.assets = new SerializableDictionary<BloonOverlayClass, PrefabReference>();
}

overlay.assets[OverlayClass] = CreatePrefabReference(Id);
}

/// <summary>
/// Applies this overlay to a projectile behavior model
/// </summary>
/// <param name="projectileBehaviorWithOverlayModel">model to add to</param>
public virtual void Apply(ProjectileBehaviorWithOverlayModel projectileBehaviorWithOverlayModel)
{
projectileBehaviorWithOverlayModel.overlayType = OverlayType;
}
}
23 changes: 17 additions & 6 deletions BloonsTD6 Mod Helper/Api/Helpers/GameModelExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ internal static void ExportAll()
}

var resourcesPath = Path.Combine(FileIOHelper.sandboxRoot, "resources.json");


total = success = 0;
foreach (var geraldoItem in Game.instance.model.geraldoItemModels)
{
Expand All @@ -187,11 +187,22 @@ internal static void ExportAll()
}
ModHelper.Log(
$"Exported {success}/{total} GeraldoItemModels to {Path.Combine(FileIOHelper.sandboxRoot, "GeraldoItems")}");



total = success = 0;
foreach (var overlayType in GameData.Instance.bloonOverlays.overlayTypes.keys)
{
if (TryExport(GameData.Instance.bloonOverlays.overlayTypes[overlayType],
$"BloonOverlays/{overlayType}.json")) success++;
total++;
}
ModHelper.Log(
$"Exported {success}/{total} BloonOverlays to {Path.Combine(FileIOHelper.sandboxRoot, "BloonOverlays")}");

Export(LocalizationManager.Instance.textTable, "textTable.json");

Export(Game.instance.model.paragonDegreeDataModel, "paragonDegreeData.json");

File.WriteAllText(resourcesPath, resourcesJson.ToString(Formatting.Indented));
ModHelper.Log($"Exported resources to {resourcesPath}");
}
Expand All @@ -208,7 +219,7 @@ public static bool TryExport(Object data, string path)
if (data != null && data.Is(out Model model))
{
ModelSerializer.MakeConsistent(model);
}
}
#endif
FileIOHelper.SaveObject(path, data);
return true;
Expand Down
17 changes: 5 additions & 12 deletions BloonsTD6 Mod Helper/Api/Internal/ModContentInstances.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace BTD_Mod_Helper.Api.Internal;

internal static class ModContentInstances
{

static ModContentInstances()
{
Instances = new Dictionary<Type, List<IModContent>>();
Expand All @@ -18,19 +17,14 @@ static ModContentInstances()

internal static void AddInstance(Type type, IModContent instance)
{
AddInstances(type, new List<IModContent> {instance});
AddInstances(type, [instance]);
}

internal static void AddInstances(Type type, List<ModContent> instances)
{
AddInstances(type, instances.Cast<IModContent>().ToList());
}

internal static void AddInstances(Type type, List<IModContent> instances)
internal static void AddInstances(Type type, IEnumerable<IModContent> instances)
{
if (typeof(IModContent).IsAssignableFrom(type))
{
if (!Instances.TryGetValue(type, out var list)) list = Instances[type] = new List<IModContent>();
if (!Instances.TryGetValue(type, out var list)) list = Instances[type] = [];
list.AddRange(instances);
if (Actions.TryGetValue(type, out var action))
{
Expand All @@ -50,7 +44,6 @@ internal static void AddInstances(Type type, List<IModContent> instances)
/// <typeparam name="T"></typeparam>
internal static class ModContentInstance<T> where T : IModContent
{

static ModContentInstance()
{
ModContentInstances.Actions[typeof(T)] = AddInstances;
Expand All @@ -62,11 +55,11 @@ static ModContentInstance()

internal static T Instance { get; private set; }

internal static List<T> Instances { get; private set; } = new();
internal static List<T> Instances { get; private set; } = [];

private static void AddInstances(IEnumerable<IModContent> instances)
{
Instances ??= new List<T>();
Instances ??= [];
Instances.AddRange(instances.Cast<T>());
Instance ??= Instances[0];
}
Expand Down
Loading

0 comments on commit c5815f3

Please sign in to comment.