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
106 changes: 106 additions & 0 deletions Abstracts/CustomEncounterModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Collections.Generic;
using Godot;
using HarmonyLib;
using MegaCrit.Sts2.Core.Assets;
using MegaCrit.Sts2.Core.Helpers;
using MegaCrit.Sts2.Core.Models;
using MegaCrit.Sts2.Core.Nodes.Rooms;
using MegaCrit.Sts2.Core.Random;
using MegaCrit.Sts2.Core.Rooms;
using MegaCrit.Sts2.Core.Runs;

namespace BaseLib.Abstracts;

public abstract class CustomEncounterModel : EncounterModel, ICustomModel
{
protected override bool HasCustomBackground => false;
public virtual string? CustomBackgroundScenePath => null;
public virtual string AssetsName => Id.Entry.ToLowerInvariant();
public virtual bool UseVanillaBackground => false;
private string CustomBackgroundScenePathFallback => SceneHelper.GetScenePath($"backgrounds/{AssetsName}/{AssetsName}_background");
public virtual NCombatBackground CreateCustomBackground(ActModel parentAct, Rng rng) {
if(UseVanillaBackground)
return NCombatBackground.Create(CreateBackgroundAssetsForCustom(rng));
return CreateNCombatBackground(CustomBackgroundScenePath);
}
public NCombatBackground CreateNCombatBackground(string? path)
{
Control control = PreloadManager.Cache.GetScene(path ?? CustomBackgroundScenePathFallback).Instantiate<Control>();
NCombatBackground? ncombatBackground = control as NCombatBackground;
if(ncombatBackground != null)
return ncombatBackground;
ncombatBackground = new NCombatBackground();
AddCustomLayer(ncombatBackground,control);
return ncombatBackground;
}

private static void AddCustomLayer(NCombatBackground bg, Control layer) {
layer.Visible = true;
bg.AddChildSafely(layer);
}

public IEnumerable<string> GetAssetPaths(IRunState runState)
{
HashSet<string> assetPaths = new HashSet<string>();
if (this.HasScene) {
string ScenePath = Traverse.Create(this).Property<string>("ScenePath").Value;
assetPaths.Add(CustomScenePath ?? ScenePath);
}
if (this.ExtraAssetPaths != null)
assetPaths.UnionWith(this.ExtraAssetPaths);
foreach ((MonsterModel monsterModel, string _) in (IEnumerable<(MonsterModel, string)>) this.MonstersWithSlots)
assetPaths.UnionWith(monsterModel.AssetPaths);
return (IEnumerable<string>) assetPaths;
}
private BackgroundAssets CreateBackgroundAssetsForCustom(Rng rng)//CreateBackgroundAssetsForCustom
{
return new BackgroundAssets(AssetsName, rng);//TODO create from custom path
}

public override bool HasScene => false;
public virtual string? CustomScenePath => null;
public virtual Control CreateCustomScene()
{
string ScenePath = Traverse.Create(this).Property<string>("ScenePath").Value;
return PreloadManager.Cache.GetScene(CustomScenePath ?? ScenePath).Instantiate<Control>();
}

[HarmonyPatch(typeof(EncounterModel), nameof(EncounterModel.CreateScene))]
private static class ScenePatch {
static bool Prefix(EncounterModel __instance, ref Control __result) {
CustomEncounterModel? model = __instance as CustomEncounterModel;
if (__instance is not CustomEncounterModel)
return true;
__result = model!.CreateCustomScene();
return false;
}
}

[HarmonyPatch(typeof(EncounterModel), nameof(EncounterModel.GetAssetPaths))]
private static class AssetPathsPatch {
[HarmonyPrefix]
static bool Prefix(EncounterModel __instance, ref IEnumerable<string> __result, IRunState runState) {
CustomEncounterModel? model = __instance as CustomEncounterModel;
if (__instance is not CustomEncounterModel)
return true;
if (model!.UseVanillaBackground)
return true;
__result = model.GetAssetPaths(runState);
return false;
}
}
[HarmonyPatch(typeof(EncounterModel), nameof(EncounterModel.CreateBackground))]
private static class BackgroundPatch {
[HarmonyPrefix]
static bool Prefix(EncounterModel __instance, ref NCombatBackground __result, ActModel parentAct, Rng rng) {
CustomEncounterModel? model = __instance as CustomEncounterModel;
if (__instance is not CustomEncounterModel)
return true;
bool HasCustomBackground = Traverse.Create(model).Property<bool>("HasCustomBackground").Value;
if(!HasCustomBackground)
return true;
__result = model!.CreateCustomBackground(parentAct, rng);
return false;
}
}
}
154 changes: 154 additions & 0 deletions Abstracts/CustomMonsterModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using BaseLib.Utils;
using BaseLib.Utils.NodeFactories;
using Godot;
using HarmonyLib;
using MegaCrit.Sts2.Core.Animation;
using MegaCrit.Sts2.Core.Assets;
using MegaCrit.Sts2.Core.Bindings.MegaSpine;
using MegaCrit.Sts2.Core.Entities.Creatures;
using MegaCrit.Sts2.Core.Models;
using MegaCrit.Sts2.Core.MonsterMoves.MonsterMoveStateMachine;
using MegaCrit.Sts2.Core.Nodes.Combat;

namespace BaseLib.Abstracts;

public abstract class CustomMonsterModel : MonsterModel, ICustomModel
{
/// <summary>
/// Override this or place your scene at res://scenes/creature_visuals/class_name.tscn
/// </summary>
public virtual string? CustomVisualPath => null;

public virtual string? CustomAttackSfx => null;
public virtual string? CustomCastSfx => null;
public virtual string? CustomDeathSfx => null;


/// <summary>
/// By default, will convert a scene containing the necessary nodes into a NCreatureVisuals even if it is not one.
/// </summary>
/// <returns></returns>
public virtual NCreatureVisuals? CreateCustomVisuals() {
string? path = (CustomVisualPath ?? VisualsPath);
if (path == null) return null;
return NodeFactory<NCreatureVisuals>.CreateFromScene(path);
}


/// <summary>
/// Override and return a CreatureAnimator if you need to set up states that differ from the default for the monster.
/// Using <seealso cref="SetupAnimationState"/> is suggested.
/// </summary>
/// <returns></returns>
public virtual CreatureAnimator? SetupCustomAnimationStates(MegaSprite controller)
{
return null;
}

/// <summary>
/// If you have a spine animation without all the required animations,
/// use this method to set up a controller that will use animations of your choice for each animation.
/// Any omitted animation parameters will default to the idle animation.
/// </summary>
/// <param name="controller"></param>
/// <param name="idleName"></param>
/// <param name="deadName"></param>
/// <param name="deadLoop"></param>
/// <param name="hitName"></param>
/// <param name="hitLoop"></param>
/// <param name="attackName"></param>
/// <param name="attackLoop"></param>
/// <param name="castName"></param>
/// <param name="castLoop"></param>
/// <returns></returns>
public static CreatureAnimator SetupAnimationState(MegaSprite controller, string idleName,
string? deadName = null, bool deadLoop = false,
string? hitName = null, bool hitLoop = false,
string? attackName = null, bool attackLoop = false,
string? castName = null, bool castLoop = false)
{
var idleAnim = new AnimState(idleName, true);
var deadAnim = deadName == null ? idleAnim : new AnimState(deadName, deadLoop);
var hitAnim = hitName == null ? idleAnim :
new AnimState(hitName, hitLoop)
{
NextState = idleAnim
};
var attackAnim = attackName == null ? idleAnim :
new AnimState(attackName, attackLoop)
{
NextState = idleAnim
};
var castAnim = castName == null ? idleAnim :
new AnimState(castName, castLoop)
{
NextState = idleAnim
};

var animator = new CreatureAnimator(idleAnim, controller);

animator.AddAnyState("Idle", idleAnim);
animator.AddAnyState("Dead", deadAnim);
animator.AddAnyState("Hit", hitAnim);
animator.AddAnyState("Attack", attackAnim);
animator.AddAnyState("Cast", castAnim);

return animator;
}
}

[HarmonyPatch(typeof(MonsterModel), nameof(MonsterModel.GenerateAnimator))]
class GenerateAnimatorPatchMonster
{
[HarmonyPrefix]
static bool CustomAnimator(MonsterModel __instance, MegaSprite controller, ref CreatureAnimator? __result)
{
if (__instance is not CustomMonsterModel customMon)
return true;

__result = customMon.SetupCustomAnimationStates(controller);
return __result == null;
}
}

[HarmonyPatch(typeof(MonsterModel), "AttackSfx", MethodType.Getter)]
class AttackSfxMonster
{
[HarmonyPrefix]
static bool Custom(MonsterModel __instance, ref string? __result)
{
if (__instance is not CustomMonsterModel customMon)
return true;

__result = customMon.CustomAttackSfx;
return __result == null;
}
}

[HarmonyPatch(typeof(MonsterModel), "CastSfx", MethodType.Getter)]
class CastSfxMonster
{
[HarmonyPrefix]
static bool Custom(MonsterModel __instance, ref string? __result)
{
if (__instance is not CustomMonsterModel customMon)
return true;

__result = customMon.CustomCastSfx;
return __result == null;
}
}

[HarmonyPatch(typeof(MonsterModel), "DeathSfx", MethodType.Getter)]
class DeathSfxMonster
{
[HarmonyPrefix]
static bool Custom(MonsterModel __instance, ref string? __result)
{
if (__instance is not CustomMonsterModel customMon)
return true;

__result = customMon.CustomDeathSfx;
return __result == null;
}
}
41 changes: 41 additions & 0 deletions Abstracts/CustomPetModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using BaseLib.Utils;
using BaseLib.Utils.NodeFactories;
using MegaCrit.Sts2.Core.Animation;
using MegaCrit.Sts2.Core.Bindings.MegaSpine;
using MegaCrit.Sts2.Core.Entities.Creatures;
using MegaCrit.Sts2.Core.Models;
using MegaCrit.Sts2.Core.MonsterMoves.MonsterMoveStateMachine;
using MegaCrit.Sts2.Core.Nodes.Combat;

namespace BaseLib.Abstracts;

public class CustomPetModel: CustomMonsterModel ,ICustomModel{
/// <summary>
/// Override this or place your scene at res://scenes/creature_visuals/class_name.tscn
/// </summary>
public new virtual string? CustomVisualPath => null;

public override int MinInitialHp => 9999;

public override int MaxInitialHp => 9999;

public override bool IsHealthBarVisible => false;

/// <summary>
/// By default, will convert a scene containing the necessary nodes into a NCreatureVisuals even if it is not one.
/// </summary>
/// <returns></returns>
public override NCreatureVisuals? CreateCustomVisuals()
{
string? path = (CustomVisualPath ?? VisualsPath);
if (path == null) return null;
return NodeFactory<NCreatureVisuals>.CreateFromScene(path);
}

protected override MonsterMoveStateMachine GenerateMoveStateMachine()
{
MoveState nothingState = new MoveState("NOTHING_MOVE", (IReadOnlyList<Creature> _) => Task.CompletedTask);
nothingState.FollowUpState = nothingState;
return new MonsterMoveStateMachine([nothingState], nothingState);
}
}