Hello everyone! So far, mods have not be officially supported, but that will be changing!!
Let us know what issues you’re running into while modding, and we’ll look into what we can do to make it easier!
Hello everyone! So far, mods have not be officially supported, but that will be changing!!
Let us know what issues you’re running into while modding, and we’ll look into what we can do to make it easier!
Since 0.9 it seems like IL2CPP is no longer used, and we have mono (very nice, readable code!).
Although assembly stripping seems to be enabled can this be disabled? It adds almost no benefit and its just extremely tedious for modders (we have to get the unstripped assemblies from the same unity version and include them in the managed folder of the game for mods to work properly.
(This guide is now also extremely out of date, since the switch to mono)
Yeah, that make more easy to map
I completely agree about assembly stripping.
It would also be great to have proper support for creating custom in-game interfaces. Ideally with the same visual style as the base game, but flexible enough to design them however we want. This would make it much easier to build immersive and consistent mod experiences.
That makes perfect sense. Has anyone had experience from other games doing a great job on this? On top of my head, I’m thinking either direct methods (command line style), or we could release a “prefab library”?
Direct methods is probably the simplest for both constructing and finding all the mod support resources back somewhere in one API. (There are already some existing ways to inspect game assets within the unity editor although it is a bit more complicated to setup)
Hello, I think the fast approach is to create a prefab library for UI elements, and simplify (direct hook) for calling a method.
This is from my old code which uses reflection to call functions in your IL2CPP backend, since the new game backend are mono, that make call pretty straight forward :
try
{
// Try to get the placement system
var placementSystem = GameObject.FindObjectOfType<Il2Cpp.PlacementSystem>();
if (placementSystem != null)
{
MelonLogger.Msg("Found PlacementSystem, attempting to place item directly");
// Use reflection to call methods on the placement system
var startPlacingMethod = placementSystem.GetType().GetMethod(
"StartPlacing",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance
);
if (startPlacingMethod != null)
{
startPlacingMethod.Invoke(placementSystem, new object[] { singleItem });
MelonLogger.Msg("Successfully started placing item directly");
}
}
}
catch (Exception ex)
{
MelonLogger.Error($"Error with direct placement: {ex.Message}");
}
And this are “how the modder struggle with using original UI” :
private void CreateSandboxSectionButton()
{
try
{
var canvases = GameObject.Find("Canvases");
if (canvases == null) { MelonLogger.Warning("Canvases not found"); return; }
var designerUI = canvases.transform.Find("InteriorDesignerUI");
if (designerUI == null) { MelonLogger.Warning("InteriorDesignerUI not found"); return; }
var topbar = designerUI.Find("Topbar");
if (topbar == null) { MelonLogger.Warning("Topbar not found"); return; }
if (topbar.Find("Section (4)") != null)
{
MelonLogger.Msg("Section (4) already exists");
return;
}
var template = topbar.Find("Section (1)");
if (template == null)
{
MelonLogger.Warning("Template Section (1) not found, cannot copy sprites");
return;
}
var tplOutlineImg = template.Find("Outline").GetComponent<UnityEngine.UI.Image>();
var tplBgImg = template.Find("Background").GetComponent<UnityEngine.UI.Image>();
var tplSelBgImg = template.Find("Background/SelectedBackground")?.GetComponent<UnityEngine.UI.Image>();
var tplIconImg = template.Find("Background/Icon")?.GetComponent<UnityEngine.UI.Image>();
var section4 = new GameObject("Section (4)", Il2CppType.Of<RectTransform>());
section4.transform.SetParent(topbar, false);
section4.transform.SetSiblingIndex(0);
var outlineGO = new GameObject("Outline", Il2CppType.Of<RectTransform>(), Il2CppType.Of<UnityEngine.UI.Image>());
outlineGO.transform.SetParent(section4.transform, false);
var outlineImg = outlineGO.GetComponent<UnityEngine.UI.Image>();
outlineImg.sprite = tplOutlineImg.sprite;
outlineImg.color = new Color(1f, 1f, 1f, 0.2f);
var oRT = outlineGO.GetComponent<RectTransform>();
oRT.anchorMin = Vector2.zero;
oRT.anchorMax = Vector2.one;
oRT.anchoredPosition = new Vector2(-18f, 0f);
oRT.sizeDelta = new Vector2(28f, 28f);
var bgGO = new GameObject("Background", Il2CppType.Of<RectTransform>(), Il2CppType.Of<UnityEngine.UI.Image>());
bgGO.transform.SetParent(section4.transform, false);
var bgImg = bgGO.GetComponent<UnityEngine.UI.Image>();
bgImg.sprite = tplBgImg.sprite;
bgImg.color = new Color(0.2353f, 0.2706f, 0.3137f, 0.9412f);
var bRT = bgGO.GetComponent<RectTransform>();
bRT.anchorMin = Vector2.zero;
bRT.anchorMax = Vector2.one;
bRT.anchoredPosition = new Vector2(-18f, 0f);
bRT.sizeDelta = new Vector2(23f, 23f);
var sandboxGO = new GameObject("Sandbox", Il2CppType.Of<RectTransform>(), Il2CppType.Of<UnityEngine.UI.Button>());
sandboxGO.transform.SetParent(bgGO.transform, false);
var sRT = sandboxGO.GetComponent<RectTransform>();
sRT.anchorMin = Vector2.zero;
sRT.anchorMax = Vector2.one;
sRT.anchoredPosition = Vector2.zero;
sRT.sizeDelta = Vector2.zero;
var selBG = new GameObject("SelectedBackground", Il2CppType.Of<RectTransform>(), Il2CppType.Of<UnityEngine.UI.Image>());
selBG.SetActive(false);
selectedBgGO = selBG;
selBG.transform.SetParent(sandboxGO.transform, false);
var selImg = selBG.GetComponent<UnityEngine.UI.Image>();
if (tplSelBgImg != null)
selImg.sprite = tplSelBgImg.sprite;
selImg.color = new Color(0.1524f, 0.496f, 0.734f, 0.4522f);
var sbRT = selBG.GetComponent<RectTransform>();
sbRT.anchorMin = Vector2.zero;
sbRT.anchorMax = Vector2.one;
sbRT.anchoredPosition = Vector2.zero;
sbRT.sizeDelta = new Vector2(-23f, -23f);
selBG.SetActive(false);
var iconGO = new GameObject("Icon", Il2CppType.Of<RectTransform>(), Il2CppType.Of<UnityEngine.UI.Image>());
iconGO.transform.SetParent(sandboxGO.transform, false);
var iconImg = iconGO.GetComponent<UnityEngine.UI.Image>();
if (tplIconImg != null)
iconImg.sprite = tplIconImg.sprite;
iconImg.color = Color.white;
var iRT = iconGO.GetComponent<RectTransform>();
iRT.sizeDelta = new Vector2(44, 44);
iRT.anchoredPosition = Vector2.zero;
var btn = sandboxGO.GetComponent<UnityEngine.UI.Button>();
btn.onClick.AddListener(new Action(() =>
{
selBG.SetActive(true);
ToggleSandboxMode();
}));
}
catch (Exception ex)
{
MelonLogger.Error($"Error creating Section (4): {ex.Message}");
}
}
We can definitely make that experience better/smoother ![]()
Thanks! Anything else we should consider? Auto-loading savegames when in dev-mode or something? Easy reloading (if possible)?
It would be great if you add both direct access to certain methods and a prefab library. That combination would give a lot more flexibility when creating or testing mods. The idea with Hot Reload and automatic save loading in dev mode also sounds amazing, it would really speed up mod development and make testing much more convenient.
I was wondering if you could support an easy way to add new items into the game as well,
Seems like something that a lot of mods would target, (new furniture, new products)
And a way to add them to stores like ika bohag for display and purchase.
I’m currently messing around with trying to add a simple board into the game but doesn’t seem straightforward since most of these building interiors are setup by hand I guess..
All furniture and appliance stores are set up using the blueprint creator. There’s no automatic process to add them to a store!
Employees and products are then added to each store type asset!
Are you able to access and edit those store types?