Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - JT

Help / Re: Change pawn guilty state
October 24, 2021, 10:38:54 AM
guiltyTicksLeft is the only field in Pawn_GuiltTracker, and it's private; not sure where you found lastGuiltyTick, since I can't even find it in ILSpy. =)

What you can do is call pawn.guilt.Notify_Guilty(int) with your own guilt duration, as it simply provides a direct setter for that value.  For instance, this.targetPawn.guilt.Notify_Guilty() would make the pawn guilty for a game day, and Notify_Guilty(12*GenDate.TicksPerHour) would make them guilty for 12 game hours.
Help / Re: [Solved] Trying to change hair color midgame
October 24, 2021, 10:35:03 AM
Nope, that actually is the best/only solution.  RimWorld 1.3+ caches the pawn graphics, unless you're using a mod like Graphics Settings, HD Pawn Renderer, etc.
Yep, it's definitely a library mismatch, but RimWorld doesn't even load System.Drawing as far as I can tell.  You'll have to target RimWorld's own API or Unity-based drawing functions instead.  Take a look at CompPowerPlantWind for an example of what's involved in the Unity API for loading textures and drawing.  Dubwise's mods are also pretty good reference, such as his ceiling fan in Dub's Bad Hygiene, as he pulls directly from RimWorld's own drawing functions (keep in mind they're closed source, though, so it's only fair dealing if you learn from them without any copying or modification).

GraphicsDatabase.Get<T>() yields a Verse.Graphic that can then be drawn in various ways with GenDraw or calling Draw on the Graphic itself.
Help / Re: Missing head texture
October 24, 2021, 10:20:41 AM
You have a lot of red errors for AdvancedShieldBelts, so that seems like the most probable culprit, especially because by its own admission it modifies a lot of pawn rendering stuff: S16's Extension(s16.core): AdvancedShieldBelts(1.0.0), BodyTypeExtend(1.0.0), HediffApparel(1.0.0), S16(1.0.0), S16(1.0.0), OniShedding(1.0.0 [no FileVersionInfo])

So whatever S16's extensions is, I suspect it's causing your trouble, not your facial animation stuff.
Mods / Re: Need help troubleshooting
October 24, 2021, 09:53:15 AM
The trick to debugging spamlogs is to look for the earliest Harmony patch you can find in the "stack" of calls that are being made.

Root level exception in OnGUI(): System.NullReferenceException: Object reference not set to an instance of an object
  at Verse.ListerBuildings+<AllBuildingsColonistOfDef>d__14.MoveNext () [0x00038] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.IdeoBuildingPresenceDemand+<AllBuildings>d__22.MoveNext () [0x00087] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.RitualObligationTargetWorker_Altar+<GetTargetsWorker>d__3.MoveNext () [0x000a8] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at System.Linq.Enumerable.Contains[TSource] (System.Collections.Generic.IEnumerable`1[T] source, TSource value, System.Collections.Generic.IEqualityComparer`1[T] comparer) [0x00036] in <351e49e2a5bf4fd6beabb458ce2255f3>:0
  at System.Linq.Enumerable.Contains[TSource] (System.Collections.Generic.IEnumerable`1[T] source, TSource value) [0x0000a] in <351e49e2a5bf4fd6beabb458ce2255f3>:0
  at RimWorld.RitualObligationTargetWorker_Altar.CanUseTargetWorker (Verse.TargetInfo target, RimWorld.RitualObligation obligation, RimWorld.Ideo ideo) [0x0003a] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.RitualObligationTargetWorker_AnyGatherSpotOrAltar.CanUseTargetInternal (Verse.TargetInfo target, RimWorld.RitualObligation obligation) [0x00082] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.RitualObligationTargetFilter.CanUseTarget (Verse.TargetInfo target, RimWorld.RitualObligation obligation) [0x0001f] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.Precept_Ritual.ShouldShowGizmo (Verse.TargetInfo target) [0x00076] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at Verse.Thing+<GetFloatMenuOptions>d__147.MoveNext () [0x0042f] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at Verse.ThingWithComps+<GetFloatMenuOptions>d__40.MoveNext () [0x00078] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at (wrapper dynamic-method) RimWorld.FloatMenuMakerMap.RimWorld.FloatMenuMakerMap.AddHumanlikeOrders_Patch11(UnityEngine.Vector3,Verse.Pawn,System.Collections.Generic.List`1<Verse.FloatMenuOption>)
  at RimWorld.FloatMenuMakerMap.ChoicesAtFor (UnityEngine.Vector3 clickPos, Verse.Pawn pawn, System.Boolean suppressAutoTakeableGoto) [0x000a5] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.FloatMenuMakerMap.TryMakeFloatMenu (Verse.Pawn pawn) [0x000a1] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at (wrapper dynamic-method) RimWorld.Selector.RimWorld.Selector.HandleMapClicks_Patch1(RimWorld.Selector)
  at RimWorld.Selector.SelectorOnGUI () [0x00000] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.MapInterface.HandleLowPriorityInput () [0x0000f] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at RimWorld.UIRoot_Play.UIRootOnGUI () [0x000dd] in <b64badbf3c3d41018b3ca5d3e8c77771>:0
  at (wrapper dynamic-method) Verse.Root.Verse.Root.OnGUI_Patch1(Verse.Root)
(Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 39)

PRE: [800]LWM.DeepStorage.Patch_AddHumanlikeOrders.Prefix, BioReactor.BioReactorPatches.Prefix_AddHumanlikeOrders post: CompTurret.HarmonyInstance.FloatMenuMakerMap_AddHumanlikeOrders_CompTurret_Patch.Postfix, MVCF.Harmony.Brawlers.AddHumanlikeOrders_Postfix, HeavyWeapons.Patch_FloatMenuMakerMap+AddHumanlikeOrders_Fix.Postfix, VFECore.Patch_FloatMenuMakerMap+AddHumanlikeOrders_Fix.Postfix, JecsTools._HumanlikeOrdersUtility.AddHumanlikeOrders_PostFix, OgsCompSlotLoadable.HarmonyCompSlotLoadable.AddHumanlikeOrders_PostFix, rjw.RMB_Menu.SexFloatMenuOption, [0 ]CallTradeShips.Patch_FloatMenuMakerMap_AddHumanlikeOrders.Postfix, [0 ]LWM.DeepStorage.Patch_AddHumanlikeOrders.Postfix TRANS: LWM.DeepStorage.Patch_AddHumanlikeOrders.Transpiler

The actual failure is occurring in the 11th patch in the list, there, which is Deep Storage.  However, as Deep Storage works for me, that means that the culprit is a mod that is interfering with it.

One or more of CallTradeShips, JecsTools, RimJobWorld, OgsCompSlotLoadable, Vanilla Expanded Core, HeavyWeapons, or BioReactor is causing compatibility problems with [LWM]DeepStorage.  OgsCompSlotLoadable and JecsTools are the more notorious culprits (the former because I've never heard of it, the latter because it was hastily updated for 1.3, so double-check to make sure you're running the latest version), although RJW is an inefficient mess of code that could also be responsible.

I do run Deep Storage without any issues along with JecsTools, VFE Core, and BioReactor.

That all said, what's failing is a call to check for ideology buildings around a target building, so your next debugging step is to search for any mods that might affect that process.  CallTradeShips seems most likely to me.

Next, if you have ruled out all of the above (the problem doesn't disappear by uninstalling any of the mods that conflicts with Deep Storage), you'll have to set your sights on the next Harmony patch line:

  at (wrapper dynamic-method) RimWorld.Selector.RimWorld.Selector.HandleMapClicks_Patch1(RimWorld.Selector)

and figure out what edits that:

Selector.HandleMapClicks: PRE: LWM.DeepStorage.Patch_HandleMapClicks.Prefix

...which is also LWM Deep Storage.  It's also possible you're running an incorrect version of Deep Storage, so double-check that, too.

Since OnGUI is the root level interface with the game, and it's HugsLib that patches that one, it's extremely unlikely to be the cause of the problem.

I couldn't find any missing defs or cross-referencing errors, so it looks like it's not an indirect problem caused by a mod that's removing a building that the game expects to be there, so that's not a problem, at least.

You've got a few other weird things going on, such as many of your mods are listed as copies rather than as originals -- are you subscribed to items at the same time as you have them installed loosely in your mod folder, or do you have multiple copies in the same folder?  That's probably not a good idea. =)
Mods / Re: MOD Request: Prison Chain
October 24, 2021, 09:36:40 AM
If you mean like a tent, Camping Stuff has multi-tile portable tents (instantly built out of actual wall tiles once it's installed) that would work for that purpose.

There's also the mod "Imprisonment on the Go" which allows taking prisoners without having a bed assigned, which might suit your purposes even better -- right-click and "Imprison".  (It's kinda ironic that the "capture" command brings them to prison and the "imprison" command captures them on the spot without bringing them to a prison cell, come to think of it.)  Along with "Prison Labor", you could then capture any prisoner on a map and set them to "Force to work", and they'll stay put or even perform minor hauling tasks until you reform your caravan, without a cell, with your warden pawns periodically moving to watch them and keep them under guard.

If you mean something like a cage, that you can force a prisoner into like a cryptosleep casket, that's certainly doable as a mod.

If you mean something like a caravan packing spot or caravan roping spot, that's quite a bit harder, but still possible with a lot of detouring/code patching.  Best way I could think of is to add new nodes to a pawn's thinktree if they're tethered, so that they use a special waiting or wandering task that keeps them within range of the chain position, and can only scan for and eat food nearby.  Mod compatibility would be a massive pain, though.

Mind you, I'm not volunteering, I'm just listing alternatives. =)
Mods / Re: Mod Request: Clean Thrumbos
October 24, 2021, 09:29:28 AM

<?xml version="1.0" encoding="utf-8" ?>

<Operation Class="PatchOperationConditional">
<match Class="PatchOperationReplace">
<value><FilthRate>1.0</FilthRate></value> <!-- Filth rate 1.0 is the same as humans and dogs -->
<nomatch Class="PatchOperationAdd">


All you gotta do now is learn how to stick that into an XML file in a mod folder and publish the mod, and you're good to go.  Tweak the filth rate as desired, but dogs are 1.0. =)

I, Jeremy T. Gibson, hereby release the above into the public domain, subject to the Ludeon EULA.  No credit is necessary.
If you're stuck, what you want is the pawn's holdingOwner.

Digging around with ILSpy is probably a better bet than relying on the save data, since not all runtime data is saved in a format that is identical to the guts of the script engine, the same way that a photograph doesn't tell you anything about the camera that made it.

Reservations are for jobs, not carrying, and definitely can't be relied on (at least, not all the time -- some jobs, like tending patients, do reserve the carried medicine all the way through).
Help / Re: Anesthetic hediff?
October 24, 2021, 09:09:11 AM
Yep, anaesthetic is applied via code; the only XML involvement is the definition of the hediff itself, and a hard-coded "<anesthetize>true</anesthetize>" flag on any given medical bill's RecipeDef.

Fortunately, what you actually want is already possible in the game -- do a search for "Recipe_AddHediff" in the game's Data folder to find the correct recipe example (Sterilize), then follow the example: switch your workerClass to "Recipe_AddsHediff" and configure the addsHediff appropriately, and be sure to set anesthetize=false so you don't double-dip the patient with both an induced coma and anesthetic that could in all probability kill them.  (Honestly, the "volunteering" necessary to test surgical mods makes me pretty damned uncomfortable at times. ;-))

Alternatively, if you're interested in going the extra fully-metricated 1.6 kilometres to make a script mod, you'd probably just bypass the anesthetize flag entirely.  Instead you'd write a new bill; this gives you the advantage that you can avoid "double dipping" the patient if they're already anaesthetised, as that would also kill them even if the bill itself doesn't apply any.  Since I have a few surgical bill doers in my own code, here's a chopped version that took me no time at all, and should give you the base template to follow (but this is only the reel and line, not the whole fish: you'll still have to learn enough to finish it on your own =)):

public class SurgicalBill_InduceComa : Recipe_Surgery
public override void ApplyOnPawn(Pawn pawn, BodyPartRecord part, Pawn billDoer, List<Thing> ingredients, Bill bill)
Hediff anaesthetic = => diff.def == HediffDefOf.Anesthetic);
if(anaesthetic != null) {; //if already under the effects of anesthetic, remove it so we don't kill the unlucky blighter

//Add Hediff here... hint: AddHediff(hediffDef) is available in any pawn's Pawn_HealthTracker, and looks quite similar to the removal above
//Report violation?  Record the Tale in the colony's history?  All certainly possible...

public override bool IsViolationOnPawn(Pawn pawn, BodyPartRecord part, Faction billDoerFaction)
if(pawn.guilt.IsGuilty) return false;
if( return false;
if( < 0.25) return false;
if(HealthUtility.TicksUntilDeathDueToBloodLoss(pawn) < GenDate.TicksPerHour*8) return false;
foreach(Hediff hediff in {
if(hediff.CurStage.lifeThreatening && !hediff.FullyImmune()) return false;
return true;
After patching this with a bugfix Harmony patch on my end (which fixes the issue!), I have discovered that the problem also exists for RitualBehaviorWorker_Speech.

Once again, I'll assert that this is definitely a vanilla bug. ;-)

When running an ideology with all roles removed, the right-click float "verb" menu will fail to open.


Not applicable.

Cause and solution:

The culprit is the RimWorld function:

// H:\RimWorld13\RimWorldWin64_Data\Managed\Assembly-CSharp.dll
// Assembly-CSharp, Version=1.3.7922.19870, Culture=neutral, PublicKeyToken=null
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: v4.0.30319
// Hash algorithm: SHA1
// RimWorld.RitualBehaviorWorker_Conversion
public override string CanStartRitualNow(TargetInfo target, Precept_Ritual ritual, Pawn selectedPawn = null, Dictionary<string, Pawn> forcedForRole = null)
Precept_Role precept_Role = ritual.ideo.RolesListForReading.First((Precept_Role r) => r.def == PreceptDefOf.IdeoRole_Moralist);
if (precept_Role.ChosenPawnSingle() == null)
return "CantStartRitualRoleNotAssigned".Translate(precept_Role.LabelCap);
bool flag = false;
foreach (Pawn item in target.Map.mapPawns.FreeColonistsAndPrisonersSpawned)
if (ValidateConvertee(item, precept_Role.ChosenPawnSingle(), throwMessages: false))
flag = true;
if (!flag)
return "CantStartRitualNoConvertee".Translate(precept_Role.ChosenPawnSingle();
return base.CanStartRitualNow(target, ritual, selectedPawn, forcedForRole);

If the error doesn't immediately leap out at you, the problem is that the routine is scanning for the first spiritual leader role of the pawn's ideology.  Upon finding that role, it then verifies whether that role is assigned to any colonist.

However, if you have removed every role from your ideology, the IEnumerable<>.First() function will throw an exception, because it cannot operate against an empty set.

The function should be changed to use FirstOrDefault() instead, and the following line should double-check to be sure that precept_Role is non-null.

This is a vanilla bug, but is exposed by mods, because Achtung! includes a prompt that reports when an exception is encountered while attempting to display the float menu.

jcstryker first discovered this issue:

A lot of the weather stuff is indeed hard-coded to use Unity assets instead of external files -- take a look at your favourite IL decompiler at Weather or Snow for instance, and you'll see that the particles are all loaded as Unity materials rather than loaded as Texture2Ds from mod data (which would enable easy modding).  That said, it wouldn't be completely unapproachable to make a mod with an assembly that simply edits the Material contained in the target reference class, since even though they are private accessible, you could always use Harmony to replace them with a runtime-generated Material of your choice.  For example, the vanilla RimWorld snow gentle weather is:

public class WeatherOverlay_SnowGentle : SkyOverlay
private static readonly Material SnowGentleOverlayWorld = MatLoader.LoadMat("Weather/SnowOverlayWorld");

public WeatherOverlay_SnowGentle()
worldOverlayMat = SnowGentleOverlayWorld;
worldOverlayPanSpeed1 = 0.002f;
worldPanDir1 = new Vector2(-0.25f, -1f);
worldOverlayPanSpeed2 = 0.003f;
worldPanDir2 = new Vector2(-0.24f, -1f);

You could Harmony-postfix the WeatherOverlay_SnowGentle constructor (see "Target method annotations" on the Github page for how to target a constructor) by overriding the worldOverlayMat with a newly generated Material of your own.  Since Unity is really really big on its WYSIWYG editor and assorted nonsense, a simple operation like loading a PNG from the disk at runtime, converting it to a texture, and popping it into SetTexture() as byte data, is absurdly difficult compared to most games -- but still should be doable.
Releases / Re: [R1.0] Signs and Memorials (v1.2.3)
March 07, 2020, 07:35:07 PM
Howdy y'all!

I went ahead and forked this, fixed this, and ported this, because it was causing save corruption on the 1.0 version and the fix contributed by the author last September missed the underlying cause of the error.  (Anyone who knows me knows that I will sooner allow a project to fester and die than to roll back scope.  It's the reason why I have left behind a litany of vapourware...)

Long story short, I grew up in a-- I mean, a fixed 1.0 version and a 1.1 port can be downloaded directly from

1.1 has a few memorials now.  I haven't ported the features of this mod onto vanilla memorials.  Yes, it's less relevant in 1.1 -- but then, I only ported for the benefit of 1.0 anyway, and a port-up to 1.1 was mostly an afterthought. ;-)  The upgrade has also been offered to the original author's repository as a pull request, since I have little or no intention of maintaining this.  Anyone else can pick up the ball anytime they like. =)
Releases / Re: [1.0] Simple Slavery
July 22, 2019, 07:07:15 PM

If that pull request goes through, it should be possible to support Prison Labor motivation mechanics out of the box!

Simple Slavery's own support is almost as simple.  You'd have to build against PrisonLabor.dll, though, but it is a soft requirement.  The alternative would be to use Reflection, but Reflection is inefficient and obnoxious.

// Freedom!!!
public void TryToEscape()
isMovingToEscape = false; // We've moved into place to escape already
hoursSinceLastEscapeAttempt = 0; // Reset time tracker
SaveMemory (); // Save our work priorities and willpower to the external hediff
Messages.Message ("MessageSlaveEscaping".Translate(pawn.Name.ToStringShort), pawn, MessageTypeDefOf.ThreatBig); //Z- NameStringShort -> Name.ToStringShort
//Z- Added Letter to escaping slaves event
string text = "LetterIncidentSlaveEscaping".Translate(pawn.Name.ToString());
Find.LetterStack.ReceiveLetter("LetterLabelSlaveEscaping".Translate(), text, LetterDefOf.NegativeEvent, null);
pawn.SetFaction (actualFaction); // Revert to real faction
pawn.guest.SetGuestStatus (slaverFaction, true);
pawn.guest.Released = false; // Ensure the slave is not set to released mode
pawn.guest.interactionMode = PrisonerInteractionModeDefOf.NoInteraction; // Ensure the interaction mode is not "release"

//Prison Labor support
Need escape = pawn.needs.TryGetNeed(DefDatabase<NeedDef>.GetNamed("PrisonLabor_Motivation"));
if(escape != null) {
PrisonLabor.Need_Motivation need = (PrisonLabor.Need_Motivation)escape;
need.Inspired = false;
need.CurLevel = 0f;
need.CanEscape = true;
need.ReadyToRun = true;
Quote from: NoImageAvailable on July 17, 2019, 10:11:43 PM
Quote from: Crossbowman on July 17, 2019, 08:29:38 PM
With regards to ammunition damage, it's true; there is a certain strangeness to how all cartridge types behave the same way when used in different weapons.

CE supports ammo types providing weapon-specific projectile stats. It's not used in the base mod as vanilla/CE Guns don't have any weapons where the difference would be significant. If some third-party mod does and isn't using this functionality then that's on whoever patched it.

Is there some other way to do this than defining an entirely new AmmoSet (and series of BulletCEs), though?  It's simple and easy to define a series of new AmmoSets for an entirely new ammo type added by the mod (I've done that myself for my WIP CE compatibility patch for Metro Armory), but duplicating a core AmmoSet feels particularly wrong.

Giving the weapon a stat multiplier would be more cross-compatible than creating patch soup where a mod must duplicate a vanilla AmmoSet for itself, and then not be able to use any ammo types that are patched into the vanilla AmmoSet by other mods other than by having to check for those mods and patch itself, and/or demand those mods to patch for its benefit.

A simple DamageFactor and MuzzleVelocityFactor as part of the weapon's stats would work wonders: the DamageFactor would increase (or decrease) the damage of each projectile fired by that weapon, and the MuzzleVelocityFactor would increase (or decrease) the speed.

(In case you're wondering, I already have two as-yet-unreleased use cases where I've bumped into this barrier:
1) Adding new low-tech variants of hand-swaged Soviet bullets that can be made by tribals, which are patched into vanilla 5.45x39mm and 7.62x54mm lists.  This invalidates any duplicates of the vanilla AmmoSet unless they accommodate this patch too.
2) Adding various AmmoSets to tweak the draw strengths of the bows I use in my mod, such that arrows, crossbow bolts, streamlined arrows, and great arrows all inflict different amounts of damage depending on the bow type.  Any arrow added to the vanilla AmmoSet is not duplicated into the new AmmoSets.)