This document describes the current mod shape as found in the source tree. It is intended as the baseline for later performance review and refactoring, not as a desired future architecture.
Source/CameraPlus.csprojbuilds the mod assembly for RimWorld 1.6.Source/*.cscontains all runtime code, Harmony patches, settings UI, marker-rule editing UI, data models, and caches.1.1through1.6contain versioned RimWorld assembly outputs. The current C# project writes1.6/Assemblies/CameraPlus.dll.About,LoadFolders.xml,Defs,Languages,Textures,Sounds, andResourcesare the RimWorld mod payload.Resources/{Win64,Linux,MacOS}/effectsare Unity asset bundles loaded at runtime for color picker materials and the bordered marker shader.Originalscontains source art and the Unity project used to generate the effects asset bundle.
CameraPlusMain in Source/Main.cs is the RimWorld Mod entry point.
Startup sequence:
- The constructor reads global mod settings via
GetSettings<CameraPlusSettings>(). - It installs all Harmony patches with the id
net.pardeike.rimworld.mod.camera+. - It installs cross-promotion support through
Brrainz.RimWorld.CrossPromotion. - A later
UIRoot_Entry.Initpostfix inAssets.LoadAssetBundle()loads the platform-specific asset bundle, initializes default marker rules, creates the custom marker folder watcher, and shows the version notice when needed.
The mod has no separate composition root. Most runtime state is static and is reached through CameraPlusMain.Settings, CameraPlusMain.orthographicSize, CameraSettings.settings, and static helper/cache classes.
The project targets net472, which matches RimWorld's managed runtime expectations. Krafs.Rimworld.Ref supplies reference assemblies so the project can build without referencing a local RimWorld installation directly.
The build uses TaskPubliciser to publicise Assembly-CSharp.dll from the Krafs.Rimworld.Ref package and then replaces the original RimWorld reference with the publicised assembly. This is why the code can access internal fields and methods such as CameraDriver.rootSize, cameraDriverInt, gameInt, and renderer internals.
See BUILD_AND_DEPENDENCIES.md for the exact commands and package versions.
CameraPlusSettings in Source/Settings.cs is the global mod settings object. It owns zoom limits, zoom curve shape, dolly/edge scroll tuning, label and marker defaults, shortcuts, and global marker-style defaults.
CameraSettings in Source/CameraSettings.cs is a WorldComponent. It stores the active List<DotConfig> marker rules for the current save. The static CameraSettings.settings pointer is refreshed by a World.FinalizeInit postfix.
SavedViews in Source/SavedViews.cs is a MapComponent. It stores nine RememberedCameraPos entries per map for the modifier + 1..9 load/save view hotkeys.
DotConfig in Source/DotConfig.cs represents one marker rule. A rule contains:
conditions: all must match the pawn.mode: off, vanilla, classic dots, silhouettes, or custom marker image.- colors for normal and selected states.
- map marker, edge marker, mouse reveal, size threshold, relative size, and outline settings.
ConditionTag, BoolTag, and TextTag define the marker-rule predicate system. The concrete predicates in BoolTags.cs and TextTags.cs cover RimWorld pawn type, faction, state, equipment, health, and text/name matching.
Camera behavior is mostly in Source/Main.cs and Source/Tools.cs.
The core zoom mapping is:
- RimWorld still changes
CameraDriver.rootSizein its normal input range. - CameraPlus maps that input through
Tools.LerpRootSize(). CameraDriver.ApplyPositionToGameObjectis transpiled so the Unity camera receives the mapped orthographic size.- The patch also adjusts camera height, clipping planes, field of view, and movement speed settings.
Important camera patches:
CameraDriver.Updaterewrites root-size assignment so zoom-to-mouse can preserve the map position under the cursor.CameraDriver.CurrentZoomremaps RimWorld's zoom enum decisions to the extended zoom range.CameraDriver.CurrentViewRectreplaces uses of raw root size with the mapped size.CameraDriver.CalculateCurInputDollyVectscales edge-scroll input.TimeControls.DoTimeControlsGUIhandles shortcuts.Game.UpdatePlay,TickManager.TogglePaused, andUIRoot_Play.UIRootOnGUIimplement the pause-hold snapback feature.
Marker rendering is split across three layers:
MarkerDecisioncomputes the per-pawn marker decision once per Unity frame.DotToolsdecides whether vanilla pawn drawing, selection brackets, pawn labels, and silhouettes should continue.DotDrawerdraws CameraPlus edge indicators and map markers in aDynamicDrawManager.DrawDynamicThingspostfix.MarkerCachebuilds and recycles per-pawnMaterialinstances for dots, silhouettes, and custom marker textures.
The normal draw flow is:
- RimWorld reaches dynamic drawing for the current map.
DotDrawer.DrawDots(map)enumeratesmap.mapPawns.AllPawnsSpawned.- Each pawn is filtered for fog/invisibility.
MarkerDecisionCachefetches the first matchingDotConfigthroughCaches.dotConfigCacheand computes marker, edge, vanilla-suppression, zoom-threshold, and mouse-reveal decisions.- If no edge marker or in-map marker can be drawn,
DotDrawerskips color and material work for that pawn. DotTools.GetMarkerColors()resolves rule colors, external mod colors, or default pawn colors.MarkerCache.MaterialFor(pawn, dotConfig)creates or refreshes the marker materials.DotDrawerdraws edge markers for off-screen pawns and in-map markers when zoom thresholds apply.
Vanilla rendering suppression is intentional:
- Pawn bodies, vehicle pawns, selection brackets, pawn UI overlays, and RimWorld silhouettes can be skipped when CameraPlus markers are active.
- Pawn and thing labels can be hidden when zoomed out, unless the mouse is close enough to reveal them.
CameraPlusMain.skipCustomRenderingis a public escape hatch other mods can set temporarily to bypass CameraPlus drawing decisions.- Perf builds can additionally patch
PawnRenderer.DynamicDrawPhaseAtto skip vanilla renderer phases for marker-replaced pawns. That experiment is intentionally behind theCAMERAPLUS_PERFcompile gate.
FastUI caches expensive UI coordinate and cell-size reads per frame.
Caches.dotConfigCache caches the first matching rule per pawn for 60 reads, keyed by thingIDNumber.
Caches.shouldShowLabelCache caches label visibility decisions for 60 reads.
Caches.cachedMainColors stores sampled main pawn colors by pawn type and body graphic path.
Caches.cachedCameraDelegates stores reflection-discovered external integration delegates by pawn runtime type.
MarkerDecisionCache stores the computed marker decision by thingIDNumber for the current Unity frame. It exists so the dynamic draw postfix and the vanilla-rendering suppression prefixes can share the same rule lookup and zoom/mouse decision work.
MarkerCache.cache stores Material objects by Pawn. Entries are reused while their marker mode, custom marker name, and outline factor still match the current rule/settings state. It owns MaterialAllocator.Destroy() cleanup when entries are invalidated or the cache is cleared. Custom marker PNG reloads clear this cache so stale custom marker materials are not reused.
CameraPlusSettings.DoWindowContents() draws the main mod settings UI. It exposes zoom limits, zoom curve, movement tuning, marker style defaults, label thresholds, animal behavior, shortcut editor access, and marker-rule editor access.
The marker-rule editor is Dialog_Customization. It is a custom table-like editor for DotConfig rows. It supports:
- condition tag editing.
- mode selection, including custom PNG marker files.
- normal and selected color editing.
- map marker, edge marker, and mouse reveal toggles.
- per-rule zoom threshold, relative size, and outline values.
- row drag/reorder, delete, duplicate, copy, and paste.
- load/save of rule presets.
Related dialogs:
Dialog_AddTaglists available predicate tags.Dialog_TagEditedits text predicates and negation.Dialog_ColorPickerprovides HSV color editing and persistent swatches.Dialog_CustomizationList_LoadandDialog_CustomizationList_Saveload/save XML presets under the CameraPlus config folder.Dialog_ShortcutsandDialog_AskForKeyedit the keyboard shortcuts.Dialog_NewVersionis a first-run-after-version-bump notice.
Static textures in Textures are loaded through RimWorld ContentFinder<Texture2D>.
The platform-specific Resources/*/effects asset bundles provide:
ColorBedmaterial.Huesmaterial.Borderedshader.
Player custom marker PNG files live in GenFilePaths.FolderUnderSaveData("CameraPlus"). A FileSystemWatcher reloads PNG files into Assets.customMarkers.
Player rule preset XML files also live in the same CameraPlus folder. The default rules file is CameraPlusDefaultRules.xml in GenFilePaths.ConfigFolderPath.
Color swatches are stored in CameraPlusColors.txt under GenFilePaths.ConfigFolderPath.
CameraPlus has explicit compatibility paths:
- Harmony dependency is declared in
About/About.xml. - Optional Vehicle Framework support patches
Vehicles.VehicleRenderer:RenderPawnAtby reflection when present. - Optional Save Our Ship 2 support patches background mesh recalculation and material state when present.
- A Dubs Performance Analyzer name-drawing patch is disabled by patching
Analyzer.Fixes.H_DrawNamesFix:Prefix. - External pawn types can expose
CameraPlusSupport.Methods.GetCameraPlusColors(Pawn)andGetCameraPlusMarkers(Pawn)in their own assembly.CameraDelegatesdiscovers these by reflection. - HARMONY_COMPATIBILITY_REVIEW.md records the 2026-05-17 decompiler/GitHub compatibility pass over these patch targets.
- Harmony transpilers depend on RimWorld method IL shape and publicised internals. API updates can compile while still changing runtime semantics.
- Rendering decisions are distributed across
Main.cs,DotTools.cs, andDotDrawer.cs, so a marker change can also change labels, pawn bodies, overlays, and other mods' patches. - Most caches are static and have no central lifecycle reset beyond targeted clear/expiry logic.
DotDrawer.DrawDots()scans every spawned pawn during dynamic drawing.MarkerCacheusesPawnobject keys and per-pawn Unity materials, so cleanup behavior matters for long sessions and large maps.- Settings UI and runtime settings share mutable lists directly; editor interactions take effect immediately.