


A BepInEx plugin for R.E.P.O. that renders an in-world dot trail along the NavMesh path to the current extraction point — or back to the truck once all extractions are complete. It mirrors the same target-selection logic the in-map "backtrack" dots use, projected into world space so you can follow it without opening the map.
Hotkeys (configurable, all default to F-keys):
On plugin Awake(), it:
BepInEx/config/isaia.repopathfinder.cfg) defines all hotkeys, dot visuals, animation tunables, and the path-recompute threshold. A [Internal] ConfigVersion key migrates stale defaults from prior versions in place — values that exactly match a previous default get overwritten with the current default; user-customized values are left alone.DontDestroyOnLoad GameObject. Adds two MonoBehaviours to it: PathfinderManager (path / animation / rendering) and PathfinderCustomizeUI (the F6 IMGUI overlay).InputBlocker is gated on a single static BlockInput flag flipped by the customize UI; it is inert when the menu is closed. While the menu is open, the following are short-circuited so cursor movement doesn't drive the camera and clicks don't fire weapons:
UnityEngine.Input.GetAxis, GetAxisRaw, GetMouseButton, GetMouseButtonDown, GetMouseButtonUp — return 0f / false.UnityEngine.InputSystem.InputAction.ReadValue<T>() for Vector2, float, and Vector3 — return default(T). Bound dynamically via reflection, so the plugin still loads if the new InputSystem assembly is absent.Input.GetKey / GetKeyDown / GetKeyUp are deliberately not patched, so F4 / F5 / F6 keep working.The path is cached in world space, not recomputed every frame:
RoundDirector.allExtractionPointsCompleted (read by reflection) → LevelGenerator.LevelPathTruck.transform.position; otherwise RoundDirector.extractionPointCurrent.transform.position. Truck mode: always LevelPathTruck. Identity is the ExtractionPoint reference or the literal string "truck".OffPathRecomputeThresholdMeters (default 3.0) from the cached polyline (closest-point-on-line-segment over each cached segment), orNavMesh.CalculatePath(playerNavmeshPos, targetPos, AllAreas, path) is called once. The corners and prefix arc-distances are stored; the displayed dots reference these stable world-space points for as long as the cache lives. playerNavmeshPos is PlayerAvatar.LastNavmeshPosition (read by reflection — internal field).playerArc scalar; that's the trail's "back end". Walking forward changes only playerArc; the cached corners are untouched, so dot world positions stay stable regardless of how the player moves.MaxDots (default 30) sphere primitives, colliders stripped, sharing one material with per-dot color set via MaterialPropertyBlock._animOffset ∈ [0, spacing) advances by FlowSpeed m/s. When it crosses spacing the circular pool pointer _frontPool decrements, which makes every sphere's slot index increment by 1 simultaneously. Spacing stays perfectly even regardless of how availableLength = totalLength - playerArc changes.MaxDots-1 to 0 per cycle has its smoothing flag cleared in the same step, so it snaps to its new position at the player end of the trail instead of being lerped across the room.CachedPointAtDistance(playerArc + slot*spacing + animOffset), plus GroundOffset on Y. The result is exponentially smoothed toward the displayed position with rate PositionSmoothRate (default 25 1/s) for the rare cases where the path is re-cached.min(SmoothStep(0..fadeZone, d), SmoothStep(0..fadeZone, endpoint-d)) where d = slot*spacing + animOffset, endpoint = min(availableLength, MaxDots*spacing), fadeZone = spacing * FadeZoneFraction. Hides the wrap and the path's own ends behind alpha-0 transitions.Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Standard"). _ZWrite 0 and SrcAlpha/OneMinusSrcAlpha blend are set if the chosen shader exposes those properties; render queue is 3500.PathfinderCustomizeUI (default F6) is a draggable GUILayout.Window:
#RRGGBBAA text field with Apply, preview swatch over a white underlay so transparency is obvious, eight color presets.DotScale, FlowSpeedMetersPerSec, SpacingMeters.DefaultValue. Close dismisses the panel.PathfinderManager subscribes to DotColorHex.SettingChanged so the cached _baseColor refreshes the moment a slider moves; size / flow / spacing are read from config every frame and update for free.Cursor.lockState and Cursor.visible are saved, then forced to None / true so the cursor is interactable. On close: the saved values are restored. InputBlocker.BlockInput is flipped in lockstep with visibility.Thunderstore Mod Manager: click Install. Manual: drop REPOPathfinder.dll into <profile>/BepInEx/plugins/REPOPathfinder/. Requires BepInEx-BepInExPack.
PlayerController.instance.playerAvatarScript, RoundDirector.instance, and LevelGenerator.Instance to compute its own path and renders its own GameObjects. No RPCs, no Photon traffic. Each player can install independently; nothing about the plugin requires the host or other clients to have it.UnityEngine.Input (legacy) and UnityEngine.InputSystem.InputAction.ReadValue<T>() (new). REPO's own classes are read via reflection (for internal fields) but are never patched.NavMesh.CalculatePath for itself; it does not bake or alter the game's NavMesh.Input.* axis / mouse-button reads and on InputAction.ReadValue<Vector2/float/Vector3>() (bound via reflection); GetKey* deliberately untouched so hotkeys still work.activeCount == MaxDots, the wrapping dot's slot transitioned MaxDots-1 → 0 while staying inside the visible branch, so the smoothing lerp animated it across the entire trail length. The frontPool decrement now also clears _wasVisible for the pool index that just wrapped, forcing a snap.PathfinderManager subscribes to DotColorHex.SettingChanged and refreshes its cached _baseColor immediately, so dragging color sliders updates world dots on the same frame.PathfinderTargetMode ConfigEntry. Cache invalidated on toggle so the trail re-orients on the next frame.OffPathRecomputeThresholdMeters from the cached polyline. Walking forward / backward along the path no longer translates dot world positions at all — the dots are nailed to absolute world coordinates; only their visibility shifts as the player walks past them.RecalcIntervalSeconds is now deprecated and ignored.PositionSmoothRate, default 25). _wasVisible flags reset on every path recompute and on hidden→visible transitions so wraps still snap rather than draw a curved interpolation across geometry.d %= trailExtent wrap with a shared _animOffset plus circular pool pointer _frontPool. Spacing now stays perfectly even regardless of how availableLength changes; previously a moving modulo divisor could shake dots off the spacing grid.DotColorHex / DotScale defaults from 0.1 / 0.2 are overwritten with the new defaults; user-customized values are preserved._animOffset advances every frame and the trail recycles continuously instead of populating-then-disappearing in waves.MaterialPropertyBlock for alpha fade.PlayerAvatar.LastNavmeshPosition to RoundDirector.extractionPointCurrent (or LevelGenerator.LevelPathTruck when all extractions are complete), primitive sphere pool with Sprites/Default material at render queue 3500.