Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ build/BaseLib.pck
.editorconfig
*.uid
*.user
*.import
*.import
.vscode/*
6 changes: 5 additions & 1 deletion Abstracts/CustomCardPoolModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace BaseLib.Abstracts;
//All card pools must either be a character pool or a shared pool, otherwise they will not be found.
//Character pools are found from CharacterModel.CardPool
//Shared pools are defined in ModelDb.AllSharedCardPools
public abstract class CustomCardPoolModel : CardPoolModel, ICustomModel
public abstract class CustomCardPoolModel : CardPoolModel, ICustomModel, ICustomEnergyIconPool
{
public CustomCardPoolModel()
{
Expand Down Expand Up @@ -46,6 +46,10 @@ public CustomCardPoolModel()
protected override CardModel[] GenerateAllCards() => []; //Content added through ModHelper.ConcatModelsFromMods

public virtual bool IsShared => false;

public override string EnergyColorName => CustomEnergyIconPatches.GetEnergyColorName(Id);
public virtual string? BigEnergyIconPath => null;
public virtual string? TextEnergyIconPath => null;
}

[HarmonyPatch(typeof(CardPoolModel), "FrameMaterial", MethodType.Getter)]
Expand Down
63 changes: 62 additions & 1 deletion Abstracts/CustomCharacterModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
using MegaCrit.Sts2.Core.Bindings.MegaSpine;
using MegaCrit.Sts2.Core.Models;
using MegaCrit.Sts2.Core.Nodes.Combat;
using System.Collections.Generic;
using BaseLib.Utils;
using Godot;
using MegaCrit.Sts2.Core.Entities.Players;
using System.Reflection;
using MegaCrit.Sts2.Core.Assets;
using MegaCrit.Sts2.Core.Helpers;
using BaseLib.Utils.Patching;
using System.Reflection.Emit;

namespace BaseLib.Abstracts;

Expand All @@ -22,6 +28,7 @@ public CustomCharacterModel()
public virtual string? CustomTrailPath => null;
public virtual string? CustomIconTexturePath => null; //smaller icon used in popup showing saved run info
public virtual string? CustomIconPath => null; //top left while in run, and also icon for compendium pool filter
public virtual CustomEnergyCounter? CustomEnergyCounter => null;
public virtual string? CustomEnergyCounterPath => null;
public virtual string? CustomRestSiteAnimPath => null;
public virtual string? CustomMerchantAnimPath => null;
Expand Down Expand Up @@ -119,6 +126,60 @@ public static CreatureAnimator SetupAnimationState(MegaSprite controller, string
return animator;
}
}

public readonly struct CustomEnergyCounter(Func<int, string> pathFunc, Color outlineColor, Color burstColor) {
private readonly Func<int, string> _getPath = pathFunc;
public readonly Color OutlineColor = outlineColor;
public readonly Color BurstColor = burstColor;

public string LayerImagePath(int layer) => _getPath(layer);
}

[HarmonyPatch(typeof(NEnergyCounter), "OutlineColor", MethodType.Getter)]
public class EnergyCounterOutlineColorPatch {
private static readonly FieldInfo? PlayerProp = typeof(NEnergyCounter).GetField("_player", BindingFlags.NonPublic | BindingFlags.Instance);

static bool Prefix(NEnergyCounter __instance, ref Color __result) {
if (PlayerProp?.GetValue(__instance) is Player player && player.Character is CustomCharacterModel model && model.CustomEnergyCounter is CustomEnergyCounter counter) {
__result = counter.OutlineColor;
return false;
}
return true;
}
}

[HarmonyPatch(typeof(NEnergyCounter), nameof(NEnergyCounter.Create))]
class EnergyCounterPatch {
static List<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
return new InstructionPatcher(instructions)
.Match(new InstructionMatcher()
.ldc_i4_0()
.opcode(OpCodes.Conv_I8)
.callvirt(null)
.stloc_0()
)
.Insert([
CodeInstruction.LoadLocal(0),
CodeInstruction.LoadArgument(0),
CodeInstruction.Call(typeof(EnergyCounterPatch), nameof(ChangeIroncladEnergy)),
CodeInstruction.StoreLocal(0),
]);
}

static NEnergyCounter ChangeIroncladEnergy(NEnergyCounter defaultCounter, Player player) {
if (player.Character is not CustomCharacterModel model || model.CustomEnergyCounter is not CustomEnergyCounter counter)
return defaultCounter;
NEnergyCounter energyCounter = PreloadManager.Cache.GetScene(SceneHelper.GetScenePath(string.Concat("combat/energy_counters/ironclad_energy_counter"))).Instantiate<NEnergyCounter>(0);
energyCounter.GetNode<TextureRect>("%Layers/Layer1").Texture = ResourceLoader.Load<Texture2D>(counter.LayerImagePath(1));
energyCounter.GetNode<TextureRect>("%RotationLayers/Layer2").Texture = ResourceLoader.Load<Texture2D>(counter.LayerImagePath(2));
energyCounter.GetNode<TextureRect>("%RotationLayers/Layer3").Texture = ResourceLoader.Load<Texture2D>(counter.LayerImagePath(3));
energyCounter.GetNode<TextureRect>("%Layers/Layer4").Texture = ResourceLoader.Load<Texture2D>(counter.LayerImagePath(4));
energyCounter.GetNode<TextureRect>("%Layers/Layer5").Texture = ResourceLoader.Load<Texture2D>(counter.LayerImagePath(5));
energyCounter.GetNode<CpuParticles2D>("%BurstBack").Color = counter.BurstColor;
energyCounter.GetNode<CpuParticles2D>("%BurstFront").Color = counter.BurstColor;
return energyCounter;
}
}

[HarmonyPatch(typeof(ModelDb), nameof(ModelDb.AllCharacters), MethodType.Getter)]
public class ModelDbCustomCharacters
Expand Down
6 changes: 5 additions & 1 deletion Abstracts/CustomPotionPoolModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace BaseLib.Abstracts;

public abstract class CustomPotionPoolModel : PotionPoolModel, ICustomModel
public abstract class CustomPotionPoolModel : PotionPoolModel, ICustomModel, ICustomEnergyIconPool
{
public CustomPotionPoolModel()
{
Expand All @@ -18,4 +18,8 @@ public CustomPotionPoolModel()
/// You shouldn't need this (just use SharedRelicPool), but it is allowed.
/// </summary>
public virtual bool IsShared => false;

public override string EnergyColorName => CustomEnergyIconPatches.GetEnergyColorName(Id);
public virtual string? BigEnergyIconPath => null;
public virtual string? TextEnergyIconPath => null;
}
6 changes: 5 additions & 1 deletion Abstracts/CustomRelicPoolModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace BaseLib.Abstracts;

public abstract class CustomRelicPoolModel : RelicPoolModel, ICustomModel
public abstract class CustomRelicPoolModel : RelicPoolModel, ICustomModel, ICustomEnergyIconPool
{
public CustomRelicPoolModel()
{
Expand All @@ -18,4 +18,8 @@ public CustomRelicPoolModel()
/// You shouldn't need this (just use SharedRelicPool), but it is allowed.
/// </summary>
public virtual bool IsShared => false;

public override string EnergyColorName => CustomEnergyIconPatches.GetEnergyColorName(Id);
public virtual string? BigEnergyIconPath => null;
public virtual string? TextEnergyIconPath => null;
}
6 changes: 6 additions & 0 deletions Abstracts/ICustomEnergyIconPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace BaseLib.Abstracts;

public interface ICustomEnergyIconPool {
public string? BigEnergyIconPath { get; }
public string? TextEnergyIconPath { get; }
}
Empty file.
50 changes: 50 additions & 0 deletions Patches/Content/CustomEnergyIconPatches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using BaseLib.Abstracts;
using BaseLib.Utils.Patching;
using HarmonyLib;
using MegaCrit.Sts2.Core.Helpers;
using MegaCrit.Sts2.Core.Localization.Formatters;
using MegaCrit.Sts2.Core.Models;

namespace BaseLib.Patches.Content;

public class CustomEnergyIconPatches {
public const char Delimiter = '∴';
public static string GetEnergyColorName(ModelId id) => id.Category + CustomEnergyIconPatches.Delimiter + id.Entry;

[HarmonyPatch(typeof(EnergyIconHelper), nameof(EnergyIconHelper.GetPath), typeof(string))]
private static class IconPatch {
static bool Prefix(string prefix, ref string __result) {
int index = prefix.IndexOf(Delimiter);
if (index < 0) return true;
AbstractModel model = ModelDb.GetById<AbstractModel>(new(prefix[..index], prefix[(index+1)..]));
if (model is not ICustomEnergyIconPool custom || custom.BigEnergyIconPath is not string path) return true;
__result = path;
return false;
}
}

[HarmonyPatch(typeof(EnergyIconsFormatter), nameof(EnergyIconsFormatter.TryEvaluateFormat))]
private static class TextIconPatch {
static List<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
return new InstructionPatcher(instructions)
.Match(new InstructionMatcher()
.call(AccessTools.Method(typeof(string), nameof(string.Concat), [typeof(string), typeof(string), typeof(string)]))
.stloc_3()
)
.Insert([
CodeInstruction.LoadLocal(0),
CodeInstruction.LoadLocal(3),
CodeInstruction.Call(typeof(CustomEnergyIconPatches), nameof(GetTextIcon)),
CodeInstruction.StoreLocal(3),
]);
}
}

static string GetTextIcon(string prefix, string oldText) {
int index = prefix.IndexOf(Delimiter);
if (index < 0) return oldText;
AbstractModel model = ModelDb.GetById<AbstractModel>(new(prefix[..index], prefix[(index+1)..]));
if (model is not ICustomEnergyIconPool custom || custom.TextEnergyIconPath is not string path) return oldText;
return $"[img]{path}[/img]";
}
}
5 changes: 5 additions & 0 deletions Utils/Patching/InstructionMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public override string ToString()

//Building
//https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.add?view=net-10.0
public InstructionMatcher opcode(OpCode opCode)
{
_target.Add(new(opCode));
return this;
}
public InstructionMatcher nop()
{
_target.Add(new(opcode: OpCodes.Nop));
Expand Down