RimWorld

RimWorld

Dispenser Extended
Isajii Feb 6, 2024 @ 5:30am
NPDTiers - The Nutrient Paste Expansion Mod compatibility
Hello. Would it be possible to add support for the NPDTiers - The Nutrient Paste Expansion Mod? After installing Dispenser Extended, the ability to choose any of the new nutrient paste types from the gizmo is missing, only simple Nutrient Paste can be chosen. Interestingly though, the new nutrient paste types show up in the mod configuration page!

https://steamcommunity.com/sharedfiles/filedetails/?id=2043895447

My Anima tree worshiping tribe of spacefaring, mad scientist, brain-extracting monstergirls will thank you.

Sorry for opening a Discussion topic, but I play the GoG version of Rimworld and am unable to post on the mod's main page.
< >
Showing 1-4 of 4 comments
zeus  [developer] Feb 6, 2024 @ 7:54am 
i will look into it, the compatibility may be added tomorrow (when I wake up).

Edit: patched.
Last edited by zeus; Feb 6, 2024 @ 7:57pm
Isajii Feb 7, 2024 @ 8:42am 
It worked! Wonderful, thank you so much! :D
I was able to update this for 1.6 but I don't know how to share the .dll file so im just going do step by step. download DnSpy. Open the Dispenser.dll -> In the left side there should be a Dispenser.dll in purple -> Open that tab -> Scroll down until you see Startup -> right click edit class -> Paste the following code -> Then compile -> Then File -> Save Module:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using RimWorld;
using Verse;

namespace Dispenser
{
// Token: 0x02000008 RID: 8
[StaticConstructorOnStartup]
public static class Startup
{
// Token: 0x0600001E RID: 30 RVA: 0x00002F78 File Offset: 0x00001178
static Startup()
{
try
{
Harmony harmony = new Harmony("localghost.dispenser");
Type typeFromHandle = typeof(Building_NutrientPasteDispenser);
MethodBase[] array = new MethodBase[]
{
AccessTools.Method(typeFromHandle, "TryDispenseFood", null, null),
AccessTools.Method(typeFromHandle, "DispenseFood", null, null)
};
bool flag = false;
foreach (MethodBase methodBase in array)
{
if (!(methodBase == null))
{
flag = true;
MethodInfo methodInfo = methodBase as MethodInfo;
string name = (methodInfo != null && methodInfo.ReturnType == typeof(Thing)) ? "PrefixThing" : "PrefixBoolOut";
HarmonyMethod prefix = new HarmonyMethod(AccessTools.Method(typeof(Startup.HarmonyPatches), name, null, null))
{
priority = 800
};
harmony.Patch(methodBase, prefix, null, null, null);
Log.Message("[Dispenser Extended] Patched (prefix) " + methodBase);
}
}
if (!flag)
{
Log.Error("[Dispenser Extended] No dispense method found.");
}
}
catch (Exception arg)
{
Log.Error("[Dispenser Extended] Startup failed: " + arg);
}
}

// Token: 0x02000009 RID: 9
public static class HarmonyPatches
{
// Token: 0x0600001F RID: 31 RVA: 0x000030A0 File Offset: 0x000012A0
private static ThingDef ResolveFromString(string s)
{
if (s.NullOrEmpty())
{
return null;
}
ThingDef namedSilentFail = DefDatabase<ThingDef>.GetNamedSilentFail(s);
if (((namedSilentFail != null) ? namedSilentFail.ingestible : null) != null)
{
return namedSilentFail;
}
string sl = s.Trim().ToLowerInvariant();
return DefDatabase<ThingDef>.AllDefsListForReading.FirstOrDefault((ThingDef x) => ((x != null) ? x.ingestible : null) != null && !x.label.NullOrEmpty() && x.label.Trim().ToLowerInvariant() == sl) ?? DefDatabase<ThingDef>.AllDefsListForReading.FirstOrDefault((ThingDef x) => ((x != null) ? x.ingestible : null) != null && !x.label.NullOrEmpty() && x.label.ToLowerInvariant().Contains(sl));
}

// Token: 0x06000020 RID: 32 RVA: 0x00003118 File Offset: 0x00001318
private static ThingDef GetChosen(Building_NutrientPasteDispenser inst)
{
if (inst == null)
{
return null;
}
List<ThingComp> allComps = inst.AllComps;
if (allComps == null)
{
return null;
}
foreach (ThingComp thingComp in allComps)
{
Type type = thingComp.GetType();
MethodInfo methodInfo;
if ((methodInfo = type.GetMethod("ResolveSelected", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) == null)
{
methodInfo = (type.GetMethod("GetSelected", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetMethod("CurrentDef", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
}
MethodInfo methodInfo2 = methodInfo;
if (methodInfo2 != null && typeof(ThingDef).IsAssignableFrom(methodInfo2.ReturnType))
{
try
{
ThingDef thingDef = (ThingDef)methodInfo2.Invoke(thingComp, null);
if (((thingDef != null) ? thingDef.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via method => " + thingDef.defName);
return thingDef;
}
}
catch
{
}
}
string[] tdHints = new string[]
{
"selected",
"chosen",
"output",
"meal",
"food",
"product"
};
FieldInfo fieldInfo = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(ThingDef) && tdHints.Any((string h) => f.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (fieldInfo != null)
{
try
{
ThingDef thingDef2 = fieldInfo.GetValue(thingComp) as ThingDef;
if (((thingDef2 != null) ? thingDef2.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via field " + fieldInfo.Name + " => " + thingDef2.defName);
return thingDef2;
}
}
catch
{
}
}
PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo p) => p.PropertyType == typeof(ThingDef) && p.CanRead && tdHints.Any((string h) => p.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (propertyInfo != null)
{
try
{
ThingDef thingDef3 = propertyInfo.GetValue(thingComp, null) as ThingDef;
if (((thingDef3 != null) ? thingDef3.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via prop " + propertyInfo.Name + " => " + thingDef3.defName);
return thingDef3;
}
}
catch
{
}
}
string text = null;
string[] sHints = new string[]
{
"selectedDefNameOrLabel",
"selectedDefName",
"selectedLabel",
"selected",
"chosen",
"output",
"meal",
"food",
"product"
};
FieldInfo fieldInfo2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(string) && sHints.Any((string h) => f.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (fieldInfo2 != null)
{
try
{
text = (fieldInfo2.GetValue(thingComp) as string);
}
catch
{
}
}
if (text == null)
{
PropertyInfo propertyInfo2 = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo p) => p.PropertyType == typeof(string) && p.CanRead && sHints.Any((string h) => p.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (propertyInfo2 != null)
{
try
{
text = (propertyInfo2.GetValue(thingComp, null) as string);
}
catch
{
}
}
}
if (!text.NullOrEmpty())
{
ThingDef thingDef4 = Startup.HarmonyPatches.ResolveFromString(text);
if (((thingDef4 != null) ? thingDef4.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via string '" + text + "' => " + thingDef4.defName);
return thingDef4;
}
Log.Message("[Dispenser Extended] string '" + text + "' did not resolve.");
}
}
Log.Message("[Dispenser Extended] no per-building choice found -> paste");
return null;
}

// Token: 0x06000021 RID: 33 RVA: 0x00003504 File Offset: 0x00001704
public static bool PrefixThing(ref Thing __result, Building_NutrientPasteDispenser __instance)
{
ThingDef chosen = Startup.HarmonyPatches.GetChosen(__instance);
if (chosen == null)
{
return true;
}
__result = ThingMaker.MakeThing(chosen, null);
__result.stackCount = 1;
Log.Message("[Dispenser Extended] OUTPUT => " + chosen.defName);
return false;
}

// Token: 0x06000022 RID: 34 RVA: 0x00003544 File Offset: 0x00001744
public static bool PrefixBoolOut(ref bool __result, ref Thing food, Building_NutrientPasteDispenser __instance)
{
ThingDef chosen = Startup.HarmonyPatches.GetChosen(__instance);
if (chosen == null)
{
return true;
}
food = ThingMaker.MakeThing(chosen, null);
food.stackCount = 1;
__result = true;
Log.Message("[Dispenser Extended] OUTPUT => " + chosen.defName);
return false;
}
}
}
}
Originally posted by Najica:
I was able to update this for 1.6 but I don't know how to share the .dll file so im just going do step by step. download DnSpy. Open the Dispenser.dll -> In the left side there should be a Dispenser.dll in purple -> Open that tab -> Scroll down until you see Startup -> right click edit class -> Paste the following code -> Then compile -> Then File -> Save Module: (Please note I did my best to update this mod, Im by no means a Rimworld modder)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using RimWorld;
using Verse;

namespace Dispenser
{
// Token: 0x02000008 RID: 8
[StaticConstructorOnStartup]
public static class Startup
{
// Token: 0x0600001E RID: 30 RVA: 0x00002F78 File Offset: 0x00001178
static Startup()
{
try
{
Harmony harmony = new Harmony("localghost.dispenser");
Type typeFromHandle = typeof(Building_NutrientPasteDispenser);
MethodBase[] array = new MethodBase[]
{
AccessTools.Method(typeFromHandle, "TryDispenseFood", null, null),
AccessTools.Method(typeFromHandle, "DispenseFood", null, null)
};
bool flag = false;
foreach (MethodBase methodBase in array)
{
if (!(methodBase == null))
{
flag = true;
MethodInfo methodInfo = methodBase as MethodInfo;
string name = (methodInfo != null && methodInfo.ReturnType == typeof(Thing)) ? "PrefixThing" : "PrefixBoolOut";
HarmonyMethod prefix = new HarmonyMethod(AccessTools.Method(typeof(Startup.HarmonyPatches), name, null, null))
{
priority = 800
};
harmony.Patch(methodBase, prefix, null, null, null);
Log.Message("[Dispenser Extended] Patched (prefix) " + methodBase);
}
}
if (!flag)
{
Log.Error("[Dispenser Extended] No dispense method found.");
}
}
catch (Exception arg)
{
Log.Error("[Dispenser Extended] Startup failed: " + arg);
}
}

// Token: 0x02000009 RID: 9
public static class HarmonyPatches
{
// Token: 0x0600001F RID: 31 RVA: 0x000030A0 File Offset: 0x000012A0
private static ThingDef ResolveFromString(string s)
{
if (s.NullOrEmpty())
{
return null;
}
ThingDef namedSilentFail = DefDatabase<ThingDef>.GetNamedSilentFail(s);
if (((namedSilentFail != null) ? namedSilentFail.ingestible : null) != null)
{
return namedSilentFail;
}
string sl = s.Trim().ToLowerInvariant();
return DefDatabase<ThingDef>.AllDefsListForReading.FirstOrDefault((ThingDef x) => ((x != null) ? x.ingestible : null) != null && !x.label.NullOrEmpty() && x.label.Trim().ToLowerInvariant() == sl) ?? DefDatabase<ThingDef>.AllDefsListForReading.FirstOrDefault((ThingDef x) => ((x != null) ? x.ingestible : null) != null && !x.label.NullOrEmpty() && x.label.ToLowerInvariant().Contains(sl));
}

// Token: 0x06000020 RID: 32 RVA: 0x00003118 File Offset: 0x00001318
private static ThingDef GetChosen(Building_NutrientPasteDispenser inst)
{
if (inst == null)
{
return null;
}
List<ThingComp> allComps = inst.AllComps;
if (allComps == null)
{
return null;
}
foreach (ThingComp thingComp in allComps)
{
Type type = thingComp.GetType();
MethodInfo methodInfo;
if ((methodInfo = type.GetMethod("ResolveSelected", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) == null)
{
methodInfo = (type.GetMethod("GetSelected", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetMethod("CurrentDef", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
}
MethodInfo methodInfo2 = methodInfo;
if (methodInfo2 != null && typeof(ThingDef).IsAssignableFrom(methodInfo2.ReturnType))
{
try
{
ThingDef thingDef = (ThingDef)methodInfo2.Invoke(thingComp, null);
if (((thingDef != null) ? thingDef.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via method => " + thingDef.defName);
return thingDef;
}
}
catch
{
}
}
string[] tdHints = new string[]
{
"selected",
"chosen",
"output",
"meal",
"food",
"product"
};
FieldInfo fieldInfo = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(ThingDef) && tdHints.Any((string h) => f.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (fieldInfo != null)
{
try
{
ThingDef thingDef2 = fieldInfo.GetValue(thingComp) as ThingDef;
if (((thingDef2 != null) ? thingDef2.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via field " + fieldInfo.Name + " => " + thingDef2.defName);
return thingDef2;
}
}
catch
{
}
}
PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo p) => p.PropertyType == typeof(ThingDef) && p.CanRead && tdHints.Any((string h) => p.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (propertyInfo != null)
{
try
{
ThingDef thingDef3 = propertyInfo.GetValue(thingComp, null) as ThingDef;
if (((thingDef3 != null) ? thingDef3.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via prop " + propertyInfo.Name + " => " + thingDef3.defName);
return thingDef3;
}
}
catch
{
}
}
string text = null;
string[] sHints = new string[]
{
"selectedDefNameOrLabel",
"selectedDefName",
"selectedLabel",
"selected",
"chosen",
"output",
"meal",
"food",
"product"
};
FieldInfo fieldInfo2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((FieldInfo f) => f.FieldType == typeof(string) && sHints.Any((string h) => f.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (fieldInfo2 != null)
{
try
{
text = (fieldInfo2.GetValue(thingComp) as string);
}
catch
{
}
}
if (text == null)
{
PropertyInfo propertyInfo2 = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((PropertyInfo p) => p.PropertyType == typeof(string) && p.CanRead && sHints.Any((string h) => p.Name.IndexOf(h, StringComparison.OrdinalIgnoreCase) >= 0));
if (propertyInfo2 != null)
{
try
{
text = (propertyInfo2.GetValue(thingComp, null) as string);
}
catch
{
}
}
}
if (!text.NullOrEmpty())
{
ThingDef thingDef4 = Startup.HarmonyPatches.ResolveFromString(text);
if (((thingDef4 != null) ? thingDef4.ingestible : null) != null)
{
Log.Message("[Dispenser Extended] chosen via string '" + text + "' => " + thingDef4.defName);
return thingDef4;
}
Log.Message("[Dispenser Extended] string '" + text + "' did not resolve.");
}
}
Log.Message("[Dispenser Extended] no per-building choice found -> paste");
return null;
}

// Token: 0x06000021 RID: 33 RVA: 0x00003504 File Offset: 0x00001704
public static bool PrefixThing(ref Thing __result, Building_NutrientPasteDispenser __instance)
{
ThingDef chosen = Startup.HarmonyPatches.GetChosen(__instance);
if (chosen == null)
{
return true;
}
__result = ThingMaker.MakeThing(chosen, null);
__result.stackCount = 1;
Log.Message("[Dispenser Extended] OUTPUT => " + chosen.defName);
return false;
}

// Token: 0x06000022 RID: 34 RVA: 0x00003544 File Offset: 0x00001744
public static bool PrefixBoolOut(ref bool __result, ref Thing food, Building_NutrientPasteDispenser __instance)
{
ThingDef chosen = Startup.HarmonyPatches.GetChosen(__instance);
if (chosen == null)
{
return true;
}
food = ThingMaker.MakeThing(chosen, null);
food.stackCount = 1;
__result = true;
Log.Message("[Dispenser Extended] OUTPUT => " + chosen.defName);
return false;
}
}
}
}
< >
Showing 1-4 of 4 comments
Per page: 1530 50