ULTRAKILL
Install with App

Details

Latest version
1.0.0
Last Updated
First Uploaded
Downloads
108
Likes
1
Size
206KB
Dependants

A universal BepInEx plugin which at runtime can replace or modify pretty much anything in the game (textures, audio, meshes, colours, component fields, subtitles etc) all driven by JSON rules and with absolutely no need for recompiling.

Config layout

  • BepInEx/config/ColorizedInk.json - rules, hotkeys, enforce interval
  • BepInEx/config/SceneSettings.json - per-scene render settings (generated on first launch)
  • BepInEx/config/ColorizedInk/Textures/ — PNG/JPG textures referenced by rules
  • BepInEx/config/ColorizedInk/Audio/ — audio clips referenced by rules
  • BepInEx/config/ColorizedInk/Meshes/ - .obj / .bundle / .ab geometry files for mesh swaps

Both of the JSON files come back as commented templates when you run without them. The keys that start with an underscore or are called 'Instructions' are docs, the parser just ignores them so don't worry about that at all.

Root | Exact name of a scene root object. Array = multiple exact roots. RootContains | Substring on scene-root names ("a|b" or array = OR) RootNotContains | Exclusion substring(s) Path | Exact child path under the root (e.g. "Props/Walls/Cube"). Array = multiple paths (cross-product with Root). PathContains | Substring on node names; "A//B" = deep search nodes containing B under nodes containing A PathNotContains | Exclusion substring(s) on node names Name[n] | Cube[2] = the 2nd same-named sibling (0-based) works on Root and any Path segment Scene / SceneContains | Gate the rule to a scene (name, GUID, or build index) Enabled | false keeps the rule in the file without applying it MaterialIndex | Pick which material on a renderer to write to AddComponent | true creates ComponentType on objects lacking it, even mod-spawned temp objects made with new GameObject()

Writes — general components

ComponentType will accept any component name you like (Renderer, AudioSource, ParticleSystem, Light, Image, Text, modded components etc) and will resolve it via reflection across all loaded assemblies.

PropertyName supports deep member paths (a.b.c), indexing and collection edits:

  • material.color, _MainTex, _EmissiveTex, clip, startColor, common fields and shader slots.
  • enabled - toggle any behaviour on/off (x.y.enabled reaches into the nested members) The mesh field on MeshFilter or SkinnedMeshRenderer lets you swap geometry from Meshes/. The system parses .obj files at runtime. For real Unity Meshes, use .bundle files.
  • lines[2], items[*], list[+] append, list[-]/[-2] remove, whole-collection replace via a JSON array Value
  • Value: [a, b, c] on a non-collection member = random pick per apply; on a collection member = adds every entry

Writes — SubtitledAudioSource (subtitles)

Add, edit, or create subtitle tracks on voicelines:

{
  "PathContains": "SubtitleSource",
  "ComponentType": "SubtitledAudioSource",
  "PropertyName": "subtitles.lines[0].subtitle",
  "Value": "Rewritten line"
}
  • subtitles.lines[n].subtitle or .time, edit a single line.
  • subtitles.lines[+] - insert a line chronologically (by its time) so that it fires in the middle of playback at the correct point. A single object or an array of lines.
  • subtitles.lines[-] / [-n] deletes a line
  • subtitles.lines = JSON array - replace the entire track
  • subtitles = { "lines": [...] } - build a track on a component which previously had no subtitles
  • AddComponent: true + subtitles.lines[+] - put subtitles on an object which previously hadn't had a SubtitledAudioSource (mod-spawned temp audio included); Awake() self-wires the AudioSource

Scene settings

Per scene RenderSettings overrides for fog (fog colour, fog density, fog mode), ambient intensity/lighting, trilight colours, skybox (either .bundle skybox materials or skyboxImage panorama textures from Textures/ with tint, exposure, rotation and halo):

{
  "SceneContains": "cybergrind",
  "fog": true, "fogColor": "#8040FF", "fogDensity": 0.05,
  "ambientIntensity": 0.3
}

## Logging

LogOutput.log shows 'Rule applied: ...' for each working rule and a 'Rule found nothing: ...' warning when a path resolves to nothing. Dead paths are pretty obvious in there.
The log will also show the scene name when the scene is loaded into it so you can readily use the Scene value.
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.