User:Illuminatiswag/Sandbox: Difference between revisions

From Caves of Qud Wiki
Jump to navigation Jump to search
(correct header type)
(preliminary harmony tutorial)
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
=== 203.33 ===
Harmony is etc etc. This tutorial will assume a general familiarity with XML modding and C# scripting,
Released April 22, 2022.
as Harmony should be a last resort used only where those cannot be applied.
* The Joppa Zealot is now much louder.
I'm not including decompiled C# code directly in this tutorial, even in snippets; please look these parts and functions up in
* Improved the travel direction selection for the Joppa Zealot's particle effects.
your own copy so you can follow along.
* Description text for Wilderness Lore: Rivers and Lakes now correctly describes the skill.
 
* Slynth no longer leave behind human corpses.
In this tutorial, we'll be making it possible to bring statues (like those created by a {{f|lithofex}})
* Kaleidoslugs in the Yd Freehold now belong to the Yd Freehold faction.
to life with a {{f|nano-neuro animator}} or {{f|Spray-a-Brain}}. Most walls and furniture can be animated,
* You can no longer cross into [redacted] from outside the [redacted].
but statues are an exception. Stone statues of creatures are dynamically generated at runtime, whereas the
* Made some clarifications in the help description of the Strength attribute.
specifications for animatable furniture are determined by their blueprints, so it's not trivial to
* Containers that occupy multiple tiles will now be moved together, and break if forcefully separated.
fit them into the existing system. We can't do it simply by adding new XML data to specify, or new code as with normal
* You will now be presented with a list of containers to open if several occupy the same tile.
C# scripting. We'll need to use Harmony to patch existing methods.
* Cybernetic implants are now described as such when specified in baetyl demands and similar contexts.
 
* Pathfinding now more studiously avoids giant clams.
== Preliminaries ==
* Fixed a bug that caused odd behavior from armor averaging when some of the body parts involved had 0 AV/DV and others didn't.
 
* Fixed some cases where a lot of watervine or brinestalk would spawn in the same tile.
The first step here isn't to write a patch; it's to figure out where a patch needs to go
* Fixed a bug that prevented the Joppa Zealot's particle text effect from displaying.
* Fixed a bug that caused some liquids not to appear is valid cooking ingredients when they should have.
* Fixed a typo in the names of recipes that use cloning draught as an ingredient.
* Fixed some incorrect grammar in sultan tomb inscriptions.
* Fixed a typo in the Tongue & Cheek recipe effect description.
* Fixed a bug that caused the spreading of Klanq in the vicinity of the Six Day Stilt to only function within the cathedral.
* Fixed a bug that allowed you to loot destroyed containers with the Open action.
* Fixed a bug that caused the AI behavior of some movement and retreat related abilities to malfunction.
* Fixed a bug that caused furniture to rarely spawn stacked.
* Fixed a bug that increased world map lag.
=== 203.32 ===
Released April 15, 2022.
* Owned containers are shown as owned in the trade UI. Items in the owned container that belong to you are labeled to show that.
* The item listing when opening a container now shows which container you are opening.
* NPCs are now a bit less prone to attacking you if you damage objects owned by their faction, especially if the damage seems accidental.
* Abilities granted by items now have their cooldowns tracked separately from the equipped status of the item, and you no longer have to wait before unequipping an item with an ability on cool.
* You no longer automatically reload broken or rusted throwing weapons.
* The reload prompt now displays the current reload keybinding.
* Trolls no longer spawn troll foals on the world map, causing game crashes.
* Living things without brains can now be cloned using cloning draught rather than remaining in budding state indefinitely.
* Clones of animated objects now also indicate that they are animated in their display names.
* Animated doors no longer cause adjacent doors to open and close to match their own state.
* When you use Temporal Fugue in a body that is not your own and then try to talk to the clones, you no longer get the message "You make small talk with yourself" before entering conversation.
* The look display no longer forces capitalization on proper names that are not supposed to be capitalized.
* Fixed a bug that caused some merchant advertisements to not properly reveal the locations of their respective merchants when read.
* Fixed a bug that caused rare exceptions in handling liquid coatings.
* Fixed a bug that made custom visages fail to function if unimplanted and reimplanted.
* Fixed a bug that sometimes caused NPCs to equip items they weren't supposed to, such as Hand-E-Nukes.
* Fixed a bug that sometimes caused mine explosions to be displayed as coming from a *PooledObject*.
* Fixed a bug that sometimes caused the Hydropon to fail to build.
* Fixed a bug that caused zones to be empty if rebuilt after visiting a sky zone above.
* Fixed a bug that caused Svardym to croak in unspoken conversation text. ((e.g. water ritual reputation))
* Fixed a bug that caused hostile creatures to be present near the charred tree of Eskhind.
* Fixed a bug that caused the death message of being transmuted into a gem to not display.
* Fixed a bug that caused you to pray twice at statues created by the gaze of a lithofex.
* Fixed a bug that caused the stairs of the rust wells to be blocked by shale;
* Fixed a bug that caused some nearby items, minimap and message log settings to be improperly stored.
* Fixed a bug that allowed you to complete the quest "Weirdwire Conduit.. Eureka!" after collecting enough wire, whether it was still in your inventory or not.
* Fixed a bug that caused the effects of items such as torches and braziers to display over inventory icons if they lined up with their cell.
* Fixed a bug that allowed you to loot multiple sultan reliquaries if you interacted with them across zone borders.
* Fixed a harmless exception when aborting out of Spray-a-Brain usage from the stage of selecting a target.
* MORE ABOUT THI
=== 203.29 ===
Released April 1, 2022.
* Villages are no longer occasionally colonized by multiple versions of the same legendary creature, which lead to various issues.
* The quest 'Pax Klanq, I Presume?' now completes after you do the important step and talk to Barathrum about it, even if earlier quest steps are still active.
* Events that would create an open pit in [redacted] now open space-time rifts instead.
* Meyehind and Liihart now aid Eskhind if you initiate a fight with her via dialogue.
* 'Do You Really Want to Die' prompts now function correctly in modes with checkpoints.
* Fixed a bug that caused the key mapping option in the system menu to set a checkpoint.
* Fixed a bug that caused the game to crash when an open pit appeared in a pocket dimension that wraps back onto itself in a single screen.
* Fixed a bug that caused some secrets to fail to print to the message log when they were revealed.
* Fixed a bug that caused blank sultan murals to appear in rare circumstances.
* Fixed an exception caused by malformed color codes.
* Fixed a bug that caused manually dismissed proselytes to keep following you.
* Fixed a bug that caused Pax Klanq and other legendary fungi to infect a different limb from the one selected.
* Fixed a bug that caused Pax Klanq to spawn outside his hut.
* Fixed a bug that made it possible for a galgal to run over itself, particularly when possessed by the player.
* Fixed bugs in the interaction of space-time vortices with reality stabilization that caused confusing messages and inappropriate showers of sparks.
* Fixed a bug that made your force bubble not move with you when you jumped. (This probably also will result in jumping interacting with other movement-triggered events that it previously bypassed.)
* Fixed a bug that made hydraulics presses only compress objects that were placed in them, not creatures who entered them.
* Fixed a bug that prevented the comm panel from taking data disks from the player.
* Fixed a typo with a Paralyzing Stinger exclusion.
* [modding]Added anatomies for motes, mirrors, and octohedra.
=== 203.28 ===
Released March 25, 2022.
* You can now use Sunder Mind on creatures who have been fully identified with Sense Psychic.
* Pulsed field magnets now pull just one item from your inventory at a time.
* Electromagnetic shielding now makes artifacts immune to maghammers and pulsed field magnets.
* Holograms can no longer be set on fire.
* Poison-tipped spears and geomagnetic discs are now categorized as thrown weapons.
* Creatures spawned from nests now award 1/4th XP by default.
* Stepping on a junk dollar is now tracked as killing it.
* Robotic psychic hunters are now mentally assailable.
* Urberry is now considered a med.
* Bep now sells an Yd Freehold recoiler.
* Plant walls can now catch on fire, and they vaporize into ash instead of lava.
* Fixed scoring for the Daily Challenge.
* Removed Weekly from high scores screen.
* Added Key Mapping back to the system menu.
* The inventory hotkey spread now takes into account active navigation keybinds.
* The burnished fullerite shield tile no longer has the Templar rhombus.
* Saad Amus can now install cybernetic implants.
* Gave helping hands an item tier.
* Flowers now use fractional weight.
* Caslainard and Polluxus no longer have their item mods appear in their display names.
* There are new UI options to disable text-based and fullscreen screen-warping effects.
* The brinestalk gate in Ezra is now properly named.
* Improved grammar for objects on the worldmap.
* Improved plurality grammar in sultan gospels.
* Fixed a bug that broke move to zone edge.
* Fixed a bug that made turning off automatic rifling through trash disable manually rifling through trash.
* Fixed some issues with the new keybind UI when using the legacy input manager.
* Fixed some non-bonnet relics being described as bonnets.
* Fixed a typo in the gentling cone and mask descriptions.
* FearAura will no longer cause a hard lock if acquired by a player.
* Fixed a bug that prevented Hindriarch Esk from participating in the "Landings Pads" quest.
* Fixed a bug that caused the beguiled effect to persist after a creature had been proselytized.
=== 203.27 ===
Released March 18, 2022.
* Most forms of pathfinding will now automatically dig through walls, if need be, if you have digging capability.
* Autoact interruption by Heightened Hearing, Sense Psychic, and electromagnetic sensors now behaves in a fashion more consistent with other ways that autoact gets interrupted.
* Terrain is no longer shown as "Undamaged" when looked at.
* Added new default bindings to the prerelease input manager:
* alt+dpad left => move to point of interest
* alt+dpad right => auto attack nearby
* alt+dpad up => ascend
* alt+dpad down => descend
* Added a new 'Adventure' keybinding category for legacy keybinds.
* Hook and Drag and latch-on weapons no longer trigger a drag attempt, and potentially prevent movement/attacking, when attempting movement that would not actually perform a drag.
* NPC scavengers will now only attempt to pathfind to trash that is in their line of sight.
* Liquid auto-collection can now always be disabled when it has been enabled, and gives an appropriate message when you attempt to enable it on an empty container for which no appropriate liquid type is obvious.
* Targeting with the prerelease input manager enabled now correctly fires via the fire binding when picking a target when using a binding other than F.
* Fixed an issue preventing the highlight overlay from working with the prerelease input manager.
* Fixed a typo in the "Zoom In" binding.
* Fixed a bug that caused some autoact interruption messages to not be displayed.
* Fixed a bug that caused anchor spikes to prevent the defensive lunge of long blades.
* Fixed a bug that allowed you to take items from sultan reliquaries without triggering the robber chimes by using the "Store an item" menu.
* Fixed a bug that caused Proselytize to become guaranteed once you were sufficiently above your target in levels.
* Fixed a bug that caused wire strands to display both a length and a stack size.
* Fixed a bug that that prevent some alternate move commands from being activated.
=== 203.25 ===
Released March 12, 2022.
* When you are flying, force attack and automatic attack commands now use swoop if it seems productive for them to do so.
* Damage from Sunder Mind, Syphon Vim, poison, bleeding, and various other appropriate sources can no longer be prevented by, and no longer triggers, teleportation-based defensive mechanisms.
* When villages have water as their signature liquid, this no longer overrides non-water preferences for the liquid used in the water ritual.
* Village quests to find the location of objects within a zone, such as dromad traders, will no longer instantly complete upon entering their zone.
* It is no longer possible to start a conversation with any body currently dormant through use of Domination.
* Entering the trade screen with sentient containers by attempting to open them now respects conditions that should prevent that.
* Butchering severed body parts that are butcherable because of the cybernetics they contain now respects the body part being marked important in the same way that it is respected for items that are butcherable for other reasons.
* Most pathfinding is now actively unwilling to path over known yonderbrush locations, rather than merely being very reluctant.
* Unbound keys now pass through to underlying screens when using the prerelase input manager, which means most hotkey lists and unbound key commands will work properly.
* Fixed an issue where old keybindings would still exist even when seemingly overwritten when using the legacy input manager.
* Fixed a bug that made crypt sitters aggressive when they should have been neutral and neutral when they should have been aggressive.
* Fixed a typo when making small talk with a temporal fugue clone.
* Fixed a bug that caused invisible creatures to be highlighted in red when picking a target.
* Fixed a bug that caused a small sphere of negative weight hovering in open air to fail to fall when EMPed or otherwise disabled.
* Fixed a bug that caused going prone to fail to charge any action cost.
* Fixed a bug that made creatures fail to get out of the way of oncoming krakens.
* Fixed a bug that caused the Cudgel Proficiency skill to apply its +2 hit bonus to short blades instead of cudgels.
* [modding] The character sheet now uses the display name of your character's genotype and subtype.
=== 203.23 ===
Released March 4, 2022.
* Wings and functional flight equipment now provide their overland travel bonuses regardless of whether you enable flight (you are presumed to always be flying in overland travel if you are able to).
* Relics with the ability to dismember now decapitate independently of the wielder's Decapitate skill, similarly to serrated weapons, with twice the chance to do so that serrated weapons have.
* Grenades can no longer be detonated on the world map.
* Large boulders now render above doors.
* Fixed an issue with the gamepad ability selection not activating some abilities properly, such as sprint.
* Fixed a snippet of dialog with Indrix that indicated the wrong direction to travel.
* Fixed a bug that caused you to be unable to report Eskhind's death to Neelahind or Hindriarch Keh.
* Fixed a bug that allowed you to loot multiple [redacted] if you managed to move them into the same zone.
* Fixed a bug that caused Trailblazer and Wilderness Lore to have twice the effect on travel speed they were meant to.
* Fixed some cases where lots of furniture would end up stacked in a single cell.
* Fixed a bug that would sometimes cause halls in some areas to be blocked by statues and boulders in a way that would block off access to stairs.
* Fixed a bug that caused the stairs to the liminal way to sometimes be encased in walls.
* Fixed a bug that caused NPCs in villages to spawn in unreachable voids.
* Fixed an issue that caused a variety of rare strange behavior, such as swimming through dirt paths.
* Fixed an issue with double articles in the names of some historic sites generated in some terrains.
* [modding] GameObject.Explode() can now take an optional argument List<GameObject> Hit that will be used as the tracking list for what objects the explosion has affected.
* [modding] The 'placeobjecttest' wish is now more robust when wishing for invalid blueprints.
=== 203.21 ===
Released February 25, 2022.
* We made some improvements to saved games.
*
* Saved games are now less likely to break from patches or mod alterations.
* Most mods can now be disabled in the middle of a saved game.
*
* You can now bask in the open areas of the rust wells.
* You no longer get a popup message when your troll foal followers die.
* Effects that change the result of random mutation rolls, like Eaters' nectar tonics, now also change the results of consuming brain brine.
* Pressing escape during item naming now returns you to the begining of the naming process rather than aborting it.
* The disoriented effect now extends duration on multiple applications rather than stacking.
* Tam is now grammatically recognized as having a proper name again.
* Amnesia no longer triggers from new entries in your general notes or chronology.
* Fixed a bug that caused Amnesia to trigger 100% of the time.
* Broadcast power reception from satellites now works in open areas of the Rust Wells regardless of depth.
* Fixed a bug that made broadcast power reception from satellite slower than it should have been on the world map.
* Fixed a bug that where the extra waterskins you bought to hold excess water from a trade ended up empty.
* Fixed a bug that made nectar injector relics give one mutation point instead of two.
* Fixed a bug that incorrectly allowed magnetized shields and missile weapons to have the jacked item mod added to them.
* Fixed a bug that caused the Yd Freehold to take several minutes to build.
* Fixed a bug that occurred when galgalim ran you down.
* Fixed an issue that caused erratic movement and game crashes when you dominated a flocking creature such as a plastronoid or glowcrow.
* Fixed a bug that caused healing messages in the message log to have improper coloration.
* Fixed an exception when traveling to a new zone with followers in tow.
* [debug] Added the "clearcorrupt" wish to clean up corrupted objects from loading a saved game.
*
*
* Additionally, the following patch notes are from last week's Deep Jungle update.
*
* Items on the ground that were you marked as important now appear as points of interest whes use the 'Move to points of interest' command.
* Reloading weapons and collecting liquids now require you to be able to move your extremities.
* Phylacteries now require you to be able to move your extremities in order to activate them.
* Disabled phylacteries can no longer be activated.
* Geomagnetic discs can no longer acquire the Masterwork item mod (which had no effect on them).
* Magazine-loaded relic missile weapons that fire extra shots now always have at least enough ammo capacity to fire a single set of shots.
* You can no longer sell your knowledge of [redacted] to [redacted].
* The hotkeys for selecting signature meals at ovens are now alphabetic rather than numeric (which caused accidental selection when trying to use the number pad to navigate).
* Stratum identifiers like "surface" and "surface level" no longer appear in the names of random-point and programmable recoilers.
* Attempting to 'open' an animated piece of furniture that is a container now enters the trade screen.
* Tweaked the determination of scanning type to make more things, particularly high-tech furniture, subject to techscanning or bioscanning rather than structural scanning.
* Fixed grammar issues with various boots, gloves, and gauntlets.
* Fixed a bug that caused a number of creatures, including you, to be invulnerable.
* Fixed a bug that made geomagnetic discs fail to target phased creatures while you were phased.
* Fixed a bug that made canvas walls at the Six Day Stilt show up as points of interest via Move to Point of Interest.
* Fixed a bug that caused starting village tinkers and apothecaries to have [human tinker] and [human apothecary] as names.
* Fixed a bug that caused historical sites to generate without a relic or cult leader.
* Fixed a bug that let you sting with a dismembered stinger.
* Fixed a bug that made animated takeable objects still takeable via tab / take all.
* Fixed a bug that caused some quest items not meant to be sold, like Argyve's scratched data disk, to be sellable.
* Fixed a bug that made the wait command error out if you had no activated abilities.
* Fixed a bug that make Berate always unusable when frozen, even if you could use it telepathically.
* Fixed some generative grammar errors in the case of verbs used with pseudo-plural (singular "they") subjects after a pronoun.
* Fixed an appearance of second-person rather than third-person possessive pronouns in mural generation.
* Fixed a bug that made transparent solid objects impossible to look at using the tooltip looker.
* [modding] In a thrown weapon attack, if the Penetrations parameter is modified during the WeaponThrowHit or TakeDamage events, the modified value now appears in messaging. This is cosmetic and does not affect damage calculations.
=== 203.14 - 'beta' branch ===
Released February 11, 2022.
* We made a bunch of changes to starting equipment.
* All castes and callings now start with at least 3-4 witchwood bark. Correspondingly, starting village apothecaries now stock only a token amount witchwood bark.
* Reduced the number of starting torches to a more reasonable amount.
* All starting food -- crusty loaf, food cube, Ekuemekiyyen greens, and salthopper chips -- can now be cooked with. Smoldered mushrooms still preserve into pickled mushrooms.
* Made a bunch of tweaks to true kin starting gear. As a result, they now start with more tonics than they had been in the beta, but not as many as they were capable of starting with on mainline.
* Scholars now have a chance to start with a couple books.
* Pilgrims now start with at least one book and a wineskin.
* Apostles now start with a honeyskin.
* Consuls now start with a wineskin.
* Watervine farmers now start with some random ingredients and a cookbook.
* Arconauts now start with some canned Have-It-All instead of mystery meat.
* Fuming God Child's ash-stained robes now grant +1 AV.
* Greybeards now start with furs and a short bow.
* Horticulturists and Priests of All Suns now start with a short bow.
* Priests of All Moons now start with a compound bow.
* Castes and callings that start with missile weapons now have more ammo.
*
* Witchwood bark now heals a slightly reduced amount.
* Witchwood bark now heals over three turns in descending amounts (12-14 on turn 1, 6-7 on turn 2, 3 on turn 3).
* Witchwood bark is now classified as a med instead of food.
* Gave witchwood bark a new description.
* The world now properly generates all historic sites that, according to gospels and quests, contain relics. This means that occasionally some extra historic sites are now generated.
* Warden Une is now officially true kin and can install cybernetic implants.
* Added iron gates.
* Replaced some doors with gates in zone generation where appropriate.
* Slightly reduced the chest chances in goatfolk haunts.
* Increased the item tier of liquid-cells and nuclear cell.
* Improved the quality of random cave encounters.
* Improved the quality of ruin generation.
* Tweaked some artifact and junk population tables.
* Historic sites no longer have erroneous "unknown"s in their descriptions.
* Fixed some cooking text errors while cooking in the deathlands.
* You can now ask Pax Klanq to puff on you again if you lose your Klanq infection prior to finishing the quest to spread it.
* Broadened the conditions for getting the Red Rock Hazing Ritual achievement.
* Argyve no longer takes wire beyond the 200' requested for "Weirdwire Conduit... Eureka!".
* Fixed a bug that caused the Puff Spores ability to persist permanently if the Klanq infected limb was dismembered.
* Fixed a bug that caused the Grit Gate communications panel to disable trading permanently.
* Fixed a bug that caused Otho not to admit you to Grit Gate if your level was too high when returning from "More Than a Willing Spirit".
* Fixed a bug that caused Neelahind to end the conversation if, during "Kith and Kin", you accused a kendren in prosperous circumstances without having finished "Love and Fear".
* Fixed a bug that caused the dialogue of Bey Lah's residents to not immediately update after "Kith and Kin".
* Fixed a bug that caused the quest "Weirdwire Conduit... Eureka!" to complete at 20' of wire, rather than 200'.
* Fixed a bug that caused Argyve not to take the wire when completing "Weirdwire Conduit... Eureka!".
* Fixed an exception when a liquid fell through a pit.
* Fixed a bug that prevented flying up through the rust wells if the above zones were inactive.
* Fixed a bug that caused a FlyingWhitelistArea object to appear in the nearby objects window.
* Fixed a bug that caused hotkeys to not work in the classic conversation

Latest revision as of 05:18, 26 September 2023

Harmony is etc etc. This tutorial will assume a general familiarity with XML modding and C# scripting, as Harmony should be a last resort used only where those cannot be applied. I'm not including decompiled C# code directly in this tutorial, even in snippets; please look these parts and functions up in your own copy so you can follow along.

In this tutorial, we'll be making it possible to bring statues (like those created by a lithofex) to life with a nano-neuro animator or Spray-a-Brain. Most walls and furniture can be animated, but statues are an exception. Stone statues of creatures are dynamically generated at runtime, whereas the specifications for animatable furniture are determined by their blueprints, so it's not trivial to fit them into the existing system. We can't do it simply by adding new XML data to specify, or new code as with normal C# scripting. We'll need to use Harmony to patch existing methods.

Preliminaries

The first step here isn't to write a patch; it's to figure out where a patch needs to go. First, we look at the ObjectBlueprints file (Items.xml) for the nano-neuro animator to see how it animates an object. We see it has the AnimateObject part, which we can look up later in the decompiled game code. Next, we look at the blueprints (in Furniture.xml) for an object that can already be animated, the iron maiden. These are the relevant lines:

   <tag Name="AnimatedSkills" Value="Persuasion_Intimidate" />
   <tag Name="BodyType" Value="IronMaiden" />
   <tag Name="Animatable" />

We also check the blueprint in the same file for "Random Stone Statue", which is what we want to animate. It's missing all the tags we see in the iron maiden, but has the part "RandomStatue", which we'll look up in the decompiled game code. We could just add them to the blueprint via normal XML modding, and this would make the statue animatable. But we run into the question of what BodyType to assign to our random statue. We don't know whether it'll be a statue of a salthopper, a saw-hander, or Saad Amus the Sky-Bear. There's no single correct anatomy to assign. So we'll have to do it on the fly, at runtime, which means using C# rather than XML.

Next, we check the decompiled game code, starting with the AnimateObject part. The important pieces here are the CanAnimate method, which checks if a GameObject frankenObject can be animated, and the Animate method, which takes frankenObject and a few less-relevant parameters and brings frankenObject to life. CanAnimate checks if frankenObject has either the blueprint tag or the string property "Animatable" (more on this later), while Animate goes through a long process of assigning frankenObject all the parts it needs to become a real live creature, including assigning the anatomy based on the blueprint tag BodyType (or Humanoid by default).

We also check the decompiled code for RandomStatue. Here, the important bit is the method SetCreature, which modifies the object's properties (tile, description, etc.) to correspond with the creature it depicts. This is where we need to set up the information which AnimateObject.Animate will use to turn this statue into an appropriate creature. Since we need to modify an existing method, we'll need to use Harmony.

Now we begin the actual modding. Begin by creating a mod folder with the usual basic files in it. Next, add a C# file. The name doesn't really matter, but we'll call it AnimateStatue.cs. Put a namespace declaration in it and import Harmony, like so:

   using HarmonyLib;
   namespace AnimateStatue.HarmonyPatches {
       //TODO
   }

Next, we need to add a Harmony patch to RandomStatue.SetCreature to assign the proper BodyType. This is a simple modification, and it doesn't impact any of the other lines in the method, so we don't have to worry about the order in which it happens. This makes it a perfect candidate for a Harmony postfix patch.

Postfixes

A postfix patch modifies a function by adding some code which will always be run after the original function finishes. This code can have access to the parameters used to call the function, the value returned by the function, and (in the case of non-static methods) the instance whose method is being called. Even if another mod adds a postfix patch to the same function as us, we're guaranteed that our postfix code will be run. We're not guaranteed that it'll run *correctly*, but this patch is simple and discrete enough that it'd be hard for another mod to break it accidentally.

We start our patch with a declaration of what function we're modifying, and some more imports we'll be using:

   //...earlier stuff
   using XRL.World;
   using XRL.World.Parts;
   namespace AnimateStatue.HarmonyPatches {
       [HarmonyPatch(typeof(RandomStatue), nameof(RandomStatue.SetCreature))]
       class RandomStatuePatch {
           //TODO
       }
   }

This states that our patch, RandomStatuePatch, will modify the class RandomStatue's method SetCreature. We can then fill in the class definition with our actual postfix function:

   //...earlier stuff
   class RandomStatuePatch {
       [HarmonyPostfix]
       static void Postfix(RandomStatue __instance, GameObject creatureObject) {
           //TODO
       }
   }

The HarmonyPostfix line tells Harmony that we're defining a postfix. The parameters of the Postfix function declare what information we want access to. Here, we're using the special variable __instance to get access to the RandomStatue part whose SetCreature method we're calling, like the keyword "this" if we were writing a method rather than patching one. We're also taking `creatureObject`, the sole parameter of the setCreature method. The name `creatureObject` is not arbitrary - if we're taking parameters from the original function, we have to name them to match. If the developers renamed SetCreature's parameter from `creatureObject` to `beingObject`, we would have to change the parameter here to `beingObject`.

Now that we understand the class and function signatures for our patch, let's add the substance of it:

   //...earlier stuff
       static void Postfix(RandomStatue __instance, GameObject creatureObject) {
           __instance.ParentObject.SetStringProperty("Animatable", "Yes"); //this may be unnecessary
           __instance.ParentObject.SetStringProperty("BodyType", creatureObject.Body.Anatomy);
       }

Here, __instance is the RandomStatue part. __instance.ParentObject is therefore the actual Random Stone Statue game object we're dealing with, as that object has the RandomStatue part. We're setting the statue's Animatable string property to "Yes", and setting the BodyType string property to the anatomy of the creatureObject. (The former is technically unnecessary as we could instead put Animatable in a blueprint tag, but we're doing the patch here anyway. Feel free to delete it and instead do some XML if you prefer.). Since BodyType is set according to the creature the statue depicts, we should be able to animate it with the correct body type. Load up the game, enable the mod, spawn in some random statue (or lithofexes), and try it for yourself! Use the swap wish to check the creature's anatomy.

Unfortunately, as you'll have discovered if you tried it out, all statues are still humanoid. We're going to need more patching.

The problem here is the split between blueprint tags and object properties. These are two very similar ways of assigning random bits of data to objects. Tags and properties have a name and a value. Some code will check only whether a name is present, while other code will check the value for a given name and do something with it. The difference is where they're defined. Blueprint tags are set directly on object blueprints, like <tag Name="Animatable"/> or <tag Name="BodyType" Value="IronMaiden"/>. Object properties, on the other hand, are set at runtime, such as by our calls to SetStringProperty. In many cases, the two are effectively interchangeable, as game objects will often use methods like GetPropertyOrTag(name), which checks for a property with that name and, if there's none, checks the blueprint for a tag with that name. For instance, the AnimateObject.CanAnimate method checks if the object to be animated has Animatable as either a tag or a property.


The key issue is that the AnimateObject.Animate method sets the anatomy of the newly animated object by a call to GetTag("BodyType"), which exclusively checks tags, not properties. But we can't set BodyType on the blueprint, so we have to set it to check properties as well. This might be possible to do with another postfix, simply overriding the anatomy. In fact, that would probably be the safest route. Try doing it yourself as an exercise.

There is, however, a certain elegance to the idea of just tweaking this one method call instead of adding a whole new one. An odd sort of elegance, to be fair, since we'll have to drag ourselves down into the mud to actually achieve it. But if we really do want to just modify this one function call, in the middle of the method, without touching anything else or duplicating the call, we'll have found ourselves a use case for a Harmony transpiler.

Transpilers

Compared to standard C# scripting, Harmony patches are powerful but difficult and fragile, and should be used only as a last resort. Transpilers have the same relationship to the rest of Harmony as Harmony itself does to that standard scripting. Rather than hooking on to a function at the beginning or end to work with its arguments and values, transpilers directly manipulate the sequence of code instructions which make up the function. Here be dragons.

These code instructions are not in C#. Instead, they're in the Common Intermediate Language (CIL or IL), a bytecode instruction set developed by Microsoft as a compilation target for C# (and other high-level languages). Each instruction in IL is a single, small operation on a stack-based virtual machine. For instance, calling a function with three parameters, a single line of C#, requires four instructions in IL: three instructions to put the parameters on the stack, plus an instruction to actually call the function.

A detailed introduction to IL is beyond the scope of this tutorial, but the decompiler ILSpy has an option to decompile the code in "IL with C#" format, which will include the IL instructions annotated with their corresponding C# lines. This, in my opinion, is the most useful format for understanding the IL. In this case, we can easily find the C# line with the GetTag call, which will look roughly like this:


IL_0126: callvirt instance string XRL.World.GameObject::GetTag(string, string)

We want to change this to call GetTagOrStringProperty. We'll need to add some more imports, and a new patch class for AnimateObject.Animate:

   //...earlier stuff
   using System.Reflection;
   using System.Reflection.Emit;
   using System.Collections.Generic;
   namespace AnimateStatue.HarmonyPatches {
       //...earlier stuff
       [HarmonyPatch(typeof(AnimateObject), nameof(AnimateObject.Animate))]
       class AnimateObjectPatch {
           //TODO
       }
   }

The Reflection imports will allow us to work with instructions more directly. Our transpiler will need to be annotated as such, and must be a function from a sequence of code instructions to a sequence of code instructions. Since we're working so abstractly here, we can't use normal collections, so we need to import System.Collections.Generic for the IEnumerable sequence type. Here's what the function signature will look like:

   //...earlier stuff
   class AnimateObjectPatch {
       [HarmonyTranspiler]
       static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
           //TODO
       }
   }

We want this to find the code instruction that calls GetTag and swap it to call GetTagOrStringProperty. There are a couple calls to GetTag in this function, but luckily the one we care about is the first one, so we can just modify the first call to GetTag that we find. We yield rather than simply returning, because we're returning an IEnumerable sequence which will automatically collect the values we return:

       static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
           var found = false;
           foreach (var instruction in instructions)
           {
               if (!found && false) //TODO should check if the instruction calls GetTag
               {
                   yield return null; //TODO should be the code instruction but for GetTagOrStringProperty instead
                   found = true;
               }
               else {
                   yield return instruction;
               }
           }
       }

Now we need to be able to examine the instructions. For a full rundown on how to do this, see [Harmony documentation here]. Essentially, we get the information of the GameObject methods GetTag and GetPropertyOrTag (using System.Reflection), which we can then use to check and construct CodeInstructions (a class defined by Harmony to make it easier to work with IL in C#). CodeInstructions come with a handy Calls method, which takes a MethodInfo and checks whether the instruction is a call to the method with that info. This solves our first TODO. For the second TODO, we create a new CodeInstruction with the operation code Callvirt (from System.Reflection.Emit) which calls the method specified by another MethodInfo, in this case the MethodInfo m_getTagOrProp which refers to GameObject.GetPropertyOrTag.

   //...earlier stuff
   class AnimateObjectPatch {
       static MethodInfo m_getTagOrProp = typeof(GameObject).GetMethod("GetPropertyOrTag");
       static MethodInfo m_getTag = typeof(GameObject).GetMethod("GetTag");
       [HarmonyTranspiler]
       static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
           //...earlier stuff
               if (!found && instruction.Calls(m_getTag))
               {
                   yield return new CodeInstruction(OpCodes.Callvirt, m_getTagOrProp);
                   found = true;
               }
           //...earlier stuff
       }
   }

In summary, our transpiler patch takes in the IL instructions for AnimateObject.Animate and passes them through unchanged until it finds an instruction that's a call to GetTag. It replaces that instruction with a call to GetPropertyOrTag, then passes all the remaining IL instructions through unchanged. We can clearly see how fiddly and fragile this is. If the call to GetTag is replaced with a call to something else, it will replace the next call to GetTag instead. If a new GetTag call is added before the BodyType one, it will replace that one instead and leave BodyType to exclusively pull from blueprint tags.

Even so, this is almost a best-case scenario for a transpiler. We don't have to worry about branching or labels, we only have to replace a single instruction, and the replacement does essentially the same thing but more permissive, so it's unlikely to break too badly. If you take a crack at transpiling on your own, try to keep your patches likewise as minimal as possible - nothing good awaits in the deep swamps of assembly metaprogramming.