Utility Mod SFcore¶
Background¶
You can add custom achievements, charms and items without having a good understanding of the various aspects that run Hollow Knight and its mods. Therefore, this guide recommends and expects that you have already worked with the following topics:
- Understanding the structure of a basic mod.
- Adding embedded resources to a mod.
- Adding references to other mods.
Add custom achievements¶
- Initialize the AchievementHelper with 1 custom achievement and empty sprites.
Note
This step is the one where you can add your custom sprites. Simply load those instead of the new Sprite()
.
Note
For the Convo
’s to work properly, you need the ModHooks.Instance.LanguageGetHook
similar to the Helper above, but only listening to your custom convo keys.
public override void Initialize()
{
AchievementHelper.Initialize();
AchievementHelper.Add("YourCustomAchievementKey", new Sprite(), "YourCustomLanguageConvo", "YourCustomLanguageConvo", false);
}
- Done! Now you can at some point in your mod have
GameManager.instance.AwardAchievement("YourCustomAchievementKey");
to grant the player the achievement.
Add custom charms¶
The code for a CharmHelper_Example is also available.
- Preparation of your save settings of your mod to hold data for charms.
// insert default values here
public List<bool> gotCharms = new List<bool>() { true };
public List<bool> newCharms = new List<bool>() { false };
public List<bool> equippedCharms = new List<bool>() { false };
public List<int> charmCosts = new List<int>() { 1 };
- Add the CharmHelper as a member of your main mod class
//private Sprite testSprite;
public CharmHelper charmHelper { get; private set; }
- Initialize the
CharmHelper
with a custom charm and empty sprite, also initialize the callbacks needed.
Note
This step is the one where you can add your custom sprites. Simply load those instead of the new Sprite()
.
public override void Initialize()
{
//loadResources();
charmHelper = new CharmHelper();
charmHelper.customCharms = 4;
charmHelper.customSprites = new Sprite[] { new Sprite(), new Sprite(), new Sprite(), new Sprite() };
//charmHelper.customSprites = new Sprite[] { testSprite, testSprite, testSprite, testSprite };
initCallbacks();
}
- Initialize the callbacks needed.
private void initCallbacks()
{
ModHooks.Instance.LanguageGetHook += OnLanguageGetHook;
ModHooks.Instance.GetPlayerBoolHook += OnGetPlayerBoolHook;
ModHooks.Instance.SetPlayerBoolHook += OnSetPlayerBoolHook;
ModHooks.Instance.GetPlayerIntHook += OnGetPlayerIntHook;
ModHooks.Instance.SetPlayerIntHook += OnSetPlayerIntHook;
}
- The callbacks for language should include
if (key.StartsWith("CHARM_NAME_"))
{
int charmNum = int.Parse(key.Split('_')[2]);
if (charmHelper.charmIDs.Contains(charmNum))
{
return "CHARM NAME";
}
}
if (key.StartsWith("CHARM_DESC_"))
{
int charmNum = int.Parse(key.Split('_')[2]);
if (charmHelper.charmIDs.Contains(charmNum))
{
return "CHARM DESC";
}
}
- The callbacks for boolean checks should include
private bool OnGetPlayerBoolHook(string target)
{
if (target.StartsWith("gotCharm_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
return Settings.gotCharms[charmHelper.charmIDs.IndexOf(charmNum)];
}
}
if (target.StartsWith("newCharm_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
return Settings.newCharms[charmHelper.charmIDs.IndexOf(charmNum)];
}
}
if (target.StartsWith("equippedCharm_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
return Settings.equippedCharms[charmHelper.charmIDs.IndexOf(charmNum)];
}
}
return PlayerData.instance.GetBoolInternal(target);
}
private void OnSetPlayerBoolHook(string target, bool val)
{
if (target.StartsWith("gotCharm_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
Settings.gotCharms[charmHelper.charmIDs.IndexOf(charmNum)] = val;
return;
}
}
if (target.StartsWith("newCharm_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
Settings.newCharms[charmHelper.charmIDs.IndexOf(charmNum)] = val;
return;
}
}
if (target.StartsWith("equippedCharm_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
Settings.equippedCharms[charmHelper.charmIDs.IndexOf(charmNum)] = val;
return;
}
}
PlayerData.instance.SetBoolInternal(target, val);
}
- The callbacks for integer checks should include
private int OnGetPlayerIntHook(string target)
{
if (target.StartsWith("charmCost_"))
{
int charmNum = int.Parse(target.Split('_')[1]);
if (charmHelper.charmIDs.Contains(charmNum))
{
return Settings.charmCosts[charmHelper.charmIDs.IndexOf(charmNum)];
}
}
return PlayerData.instance.GetIntInternal(target);
}
private void OnSetPlayerIntHook(string target, int val)
{
// We don't need other mods to adjust the cost of our charms, but it could be added if wanted
PlayerData.instance.SetIntInternal(target, val);
}
Add custom enviroment particles¶
- Initialize the
EnviromentParticleHelper
with custom particles. We will use enviroment type 7, which has no sprites but the same audio as grass enviroment. As such we won’t be adding custom audio.
Note
This step is the one where you can add your custom sprites. Simply load those instead of the new Sprite()
.
public override void Initialize()
{
//loadResources();
EnviromentParticleHelper.Init();
EnviromentParticleHelper.AddCustomDashEffectsHook += AddCustomDashEffectsHook;
}
private (int enviromentType, GameObject dashEffects) AddCustomDashEffectsHook(DashEffect self)
{
var prefab = self.dashGrass;
var tmp = UObject.Instantiate(prefab, prefab.transform.parent);
var tmpPSR = tmp.GetComponentInChildren<ParticleSystemRenderer>();
var tmpPSR_M = tmpPSR.materials;
tmpPSR_M[0].SetTexture("_MainTex", new Texture());
return (7, tmp);
}
- Done! You can now have custom enviroment particles.
Add custom inventory items¶
- Initialize the
ItemHelper
with custom items and empty sprites.
Note
This step is the one where you can add your custom sprites. Simply load those instead of the new Sprite()
.
Note
For the Convo
’s to work properly, you need the ModHooks.Instance.LanguageGetHook
similar to the Helper above, but only listening to your custom convo keys.
Note
For the playerdataBool
to work properly, you need the ModHooks.Instance.GetPlayerBoolHook
& ModHooks.Instance.SetPlayerBoolHook
similar to the CharmHelper, but only listening to your custom bool key.
Note
For the playerdataInt
to work properly, you need the ModHooks.Instance.GetPlayerIntHook
& ModHooks.Instance.SetPlayerIntHook
similar to the CharmHelper, but only listening to your custom int key.
public override void Initialize()
{
// Normal Items, like the Kings Brand, Crystal Heart, etc.
ItemHelper.AddNormalItem("YourUniqueStateName", new Sprite(), "YourCustomPlayerDataBool", "YourCustomLanguageConvo", "YourCustomLanguageConvo");
// Counted Items, like Simple Keys, Rancid Eggs, etc.
ItemHelper.AddCountedItem("YourUniqueStateName", new Sprite(), "YourCustomPlayerDataInt", "YourCustomLanguageConvo", "YourCustomLanguageConvo");
// 1 2 Items, like the Map, Quill, but without Map and Quill
SFCore.ItemHelper.AddOneTwoItem("YourUniqueStateName",
new Sprite(), new Sprite(), // Sprites
"YourCustomPlayerDataBool", "YourCustomPlayerDataBool", // PlayerData Bools
"YourCustomLanguageConvo", "YourCustomLanguageConvo", // Name Convos
"YourCustomLanguageConvo", "YourCustomLanguageConvo"); // Description Convos
// 1 2 Both Items, like the Map, Quill and Map and Quill
SFCore.ItemHelper.AddOneTwoBothItem("YourUniqueStateName",
new Sprite(), new Sprite(), new Sprite(), // Sprites
"YourCustomPlayerDataBool", "YourCustomPlayerDataBool", // PlayerData Bools
"YourCustomLanguageConvo", "YourCustomLanguageConvo", "YourCustomLanguageConvo", // Name Convos
"YourCustomLanguageConvo", "YourCustomLanguageConvo", "YourCustomLanguageConvo"); // Description Convos
}
- Done! You can now have custom Inventory Items.
Add custom title logos¶
- Initialize the
TitleLogoHelper
with a logo spite.
Note
This step is is being done best in the constructor of your mod class.
Note
This step is the one where you can add your custom sprites. Simply load those instead of the new Sprite()
.
private int LogoId = -1;
public ModName()
{
TitleLogoHelper.Initialize();
LogoId = TitleLogoHelper.AddLogo(new Sprite());
}
- Done! You can now have custom title logos.
“Debugging” PlayMaker FSMs¶
Get your fsm and add a log action.
var fsm = gameObject.LocateMyFsm("FSM name here");
fsm.AddAction("FSM state name here", new ActualLogAction() { text = "Log message here" });
FsmUtil¶
Similar in functionality as the one found int ModCommon, but also works with the HK 1.5 beta.
var fsm = gameObject.LocateMyFsm("FSM name here");
fsm.AddFsmState("New empty state name");
fsm.AddTransition("from state", "event name", "to state"); // the state has to exist first
fsm.AddGlobalTransition("global event name", "to state"); // the state has to exist first
fsm.ChangeTransition("from state", "event name", "new to state"); // the state has to exist first
fsm.GetState("FSM state name here"); // returns a state, if you ever need it
fsm.CopyState("original state name", "name of new copied state"); // returns copied state
fsm.GetAction<ActionType>("FSM state name here", 0); // returns action instance
fsm.AddAction("FSM state name here", new FsmStateAction());
fsm.InsertAction("FSM state name here", new FsmStateAction(), 0);
fsm.RemoveAction("FSM state name here", 0);
fsm.AddFloatVariable("new variable name");
fsm.AddIntVariable("new variable name");
fsm.AddBoolVariable("new variable name");
fsm.AddStringVariable("new variable name");
fsm.AddVector2Variable("new variable name");
fsm.AddVector3Variable("new variable name");
fsm.AddColorVariable("new variable name");
fsm.AddRectVariable("new variable name");
fsm.AddQuaternionVariable("new variable name");
fsm.AddGameObjectVariable("new variable name");
MiscCreator¶
Currently allows to reset everything audio of an SceneManager
and set the values of a Vector3
.
var sm = GameObject.FindObjectOfType<SceneManager>();
MiscCreator.ResetSceneManagerAudio(sm);
sm.transform.position.Set(0, 0, 0);
USceneUtil¶
Currently allows to search in a UnityEngine.SceneManagement.Scene
for UnityEngine.GameObject
.
UnityEngine.SceneManagement.SceneManager.activeSceneChanged += OnSceneChanged;
...
private void OnSceneChanged(UnityEngine.SceneManagement.Scene from, UnityEngine.SceneManagement.Scene to)
{
var smGo = to.FindRoot("_SceneManager");
var someChildGo = to.Find("Whatever");
var someOtherChildGo = smGo.Find("Whatever");
}
Util¶
Allows miscellaneous stuff and also a GetVersion function that adds hash to version number.
someObject.SetAttr<ObjectClass, FieldType>("fieldName", fieldValue);
var value = someObject.GetAttr<ObjectClass, FieldType>("fieldName");
MonoBehaviours¶
Name | Functionality |
---|---|
BlurPlanePatcher | Fixes the blurplane in custom scenes that don’t include their own shaders. |
PatchMusicRegions | Fixes MusicRegions for easy use of custom BGM. |
SceneMapPatcher | Fixes scenemap in custom scenes that don’t include their own shaders. |
SpritePatcher | Fixes sprites in custom scenes that don’t include their own shaders. |
Generic Mods¶
In the modding api for the 1.5 version of hollow knight, generic mods will be removed, so they’re added in SFCore.
Name | Summary |
---|---|
FullSettingsMod | A mod with save and global settings |
GlobalSettingsMod | A mod with global settings |
SaveSettingsMod | A mod with save settings |