A custom node editor to make mods for Sons of the Forest
📙 Documentation 📙
- Made specifically for RedLoader
- Shareable projects: share you mod projects with others
- Runtime execution: try node connections in realtime while in game
- Error detection: detects common errors when building the mod
- Colored graph comments: comment your node sections with what they do
- Node descriptions: each node when hovered shows a description about what it does or how should be used
- Zoom: zoomable graph editor
- Variables: store values in containers to later use them
- Custom events: alter the node flow and keeps a tidy editor visual
- Generic and Unity nodes: expand the modding capabilities beyond the provided nodes
- 200+ available nodes to use
- Download the latest RedNodeEditor Setup.exe from the release section
- Run the setup as administrator and install the application
- Open the app and start making mods
- Install RedNodeLoader as a standard RedLoader mod
- Place the
.rmod
file and eventual mod folder in theSonsOfTheForest/Mods/RedNodeLoader/Mods
folder
Administrator privileges are required to write mod files to disk
Two project examples are provided with the editor installation:
Project name | Description | Real mod link | Difficulty |
---|---|---|---|
ModMenuExample.rproj | Simplified version of my mod menu | link | easy-intermediate |
PlaneModExample.rproj | Mod where you can pilot a plane | link | harder |
Mod project files end with .rproj
extension
Mod files end with .rmod
extension
The custom extensions are only there for clarity, in reality they are .json and .xml respectively
While it's surely not possible to make all types of mods, it should offer a vast arsenal to make easy and more complex type of mods like the two provided mod examples. By providing both premade and generic nodes it pretty much depends on the user capabilities and understanding of the tool.
Can I still make something out of this if I don't know how to program and/or unity game development?
Absolutely yes! Some nodes may sound unfamiliar like the Unity node category, but a big chunk of them are self-explanatory and are shortly described when hovered.
The application was built with a focus on RedLoader by using it's functionalities and making nodes based on it. The mods themselves are read and resolved by a RedLoader mod (RedNodeLoader), which executes each node accordingly.
Containers like arrays and list aren't currently supported and so aren't for loops.
Depending if the mod used some nodes which where greatly changed in functionalities the mod may break or not. (Reverting to an older version of the loader is always possible to keep using it).
EditorShowcase.mp4
To create a new node we need to make a new class which derives from the SonsNode class and populate it's informations like shown below.
public class SetWalkSpeedNode : SonsNode
{
public float Speed { get; set; } // the argument input displayed as an input field
public SetWalkSpeedNode()
{
Name = "SetWalkSpeed"; // the name of the node which will be displayed in the editor
Description = "Sets player walk speed"; // description about what the node does or how should be used
NodeCategory = NodeCategories.Player; // the node category in which to put the node
// the input argument displayed as a circle (ArgName is optional but preferred; nameof should always be used to maintain the right references)
ArgsIn.Add(new ArgIn { Type = typeof(float), ArgName = nameof(Speed) });
}
}
public class GetRunSpeedNode : SonsNode
{
public GetRunSpeedNode()
{
Name = "GetRunSpeed"; // the name of the node which will be displayed in the editor
Description = "Returns player run speed"; // description about what the node does or how should be used
NodeCategory = NodeCategories.Player; // the node category in which to put the node
// the output argument displayed as a circle
ArgsOut.Add(new ArgOut { Type = typeof(float) });
}
}
public class AmountOfNode : SonsNode
{
public int ItemId { get; set; } // the argument input displayed as an input field
public AmountOfNode()
{
Name = "AmountOf"; // the name of the node which will be displayed in the editor
Description = "Gets the owned amount of the passed item id"; // description about what the node does or how should be used
NodeCategory = NodeCategories.Inventory; // the node category in which to put the node
ArgsIn.Add(new ArgIn { Type = typeof(int), ArgName = nameof(ItemId) }); // input argument displayed as a circle
ArgsOut.Add(new ArgOut { Type = typeof(int) }); // output argument displayed as a circle
}
}
If the input argument isn't of the common types (int, float, bool, string, Vector3, Vector2) the property must still be written and marked with the [XmlIgnore]
attribute like shown in the example below.
public class SetActiveNode : SonsNode
{
[XmlIgnore] // use this attribute when the property type it's not (int, float, bool, string, Vector3, Vector2)
public GameObject GameObject { get; set; } // GameObject type isn't a common type so no input field will be displayed
public bool Value { get; set; } // Boolean type is a common type so a checkbox will be displayed
public SetActiveNode()
{
Name = "SetActive"; // the name of the node which will be displayed in the editor
Description = "Turns the passed GameObject on or off"; // description about what the node does or how should be used
NodeCategory = NodeCategories.Unity; // the node category in which to put the node
ArgsIn.Add(new ArgIn { Type = typeof(GameObject), ArgName = nameof(GameObject) }); // 1° input argument displayed as a circle
ArgsIn.Add(new ArgIn { Type = typeof(bool), ArgName = nameof(Value) }); // 2° input argument displayed as a circle
}
}
To make the node functional we need to implement it's behaviour in RedNodeLoader. To do so we need to create a new class which must have the same exact name, namespace and properties names as the one defined in the editor before. We then need to override the Execute method, which is where the node behaviour will happen, and add inputs and outputs of the node as properties if any.
The RedNodeLoader.GetArgumentsOf(this)
method is used to retrieve both static inputs and passed arguments of a node.
Here is an example implementation for the SetWalkSpeed node we showed before.
public class SetWalkSpeedNode : SonsNode
{
public float Speed { get; set; } // input arguments must be defined as public properties using the same name as chosed in the editor class
public override void Execute()
{
List<object> args = RedNodeLoader.GetArgumentsOf(this); // retrieves the arguments of SetWalkSpeed (only Speed value in this case) and stores them as generic objects in a list in the same order as defined in the editor class
LocalPlayer.FpCharacter.SetWalkSpeed((float)args[0]); // each argument must be explicitly casted to it's proper type and index. Never use the property directly like "Speed" as it won't work if the value is passed
}
}
Nodes having an output argument must have a public property of whatever name we chose and as the same type defined in the editor class. The output property must have the [IsArgOut]
attribute to define it's an output argument.
Here is an example implementation for the GetRunSpeed node we showed before.
public class GetRunSpeedNode : SonsNode
{
[IsArgOut] // output argument must have this attribute
public float Speed { get; set; } // output argument must be defined as a public property. The name can be whatever you want
public override void Execute()
{
Speed = LocalPlayer.FpCharacter.RunSpeed; // getting the desidered value and storing it into the node output argument
}
}
public class AmountOfNode : SonsNode
{
public int ItemId { get; set; } // input argument property
[IsArgOut]
public int OwnedAmount { get; set; } // output argument property
public override void Execute()
{
List<object> args = RedNodeLoader.GetArgumentsOf(this); // getting the value of "ItemId" input argument
OwnedAmount = LocalPlayer.Inventory.AmountOf((int)args[0]); // getting and storing the output argument in the "OwnedAmount" property
}
}
public class SetActiveNode : SonsNode
{
[XmlIgnore] // use this attribute when the property type it's not (int, float, bool, string, Vector3, Vector2)
public GameObject GameObject { get; set; } // 1° input argument property
public bool Value { get; set; } // 2° input argument property
public override void Execute()
{
List<object> args = RedNodeLoader.GetArgumentsOf(this); // getting value of "GameObject" and "Value"
var go = (GameObject)args[0]; // casting and storing the GameObject
go.SetActive((bool)args[1]); // using the casted GameObject and the boolean property
}
}
VECTORS: when working with vectors always use
System.Numerics.Vector
as the property type and not theUnityEngine
one, both in the editor and RedNodeLoader class. In the implementation of the node you will then need to create aUnityEngine.Vector
using the (X,Y,Z) values accordingly.