Skip to content

Changelog

QinShenYu edited this page Jun 10, 2026 · 11 revisions

0.7.1 — 2026-06-10

New Wearable Templates

Six new ItemTemplate values fill in the wearable-slot gaps that had no matching enum entry in 0.7.0:

  • BikeHelmet — light helmet, armor=1, hat slot
  • RiotHelmet — heavy helmet, armor=2, hat slot
  • Scarf — neck slot
  • ArmWarmers — arms slot
  • FannyPack — torsofront slot
  • MaterialPouch — thighback slot

CustomItemBuilder — Convenience Factory Expansion

The wearable factory line-up is significantly expanded so the common case no longer needs the three-arg Create(...) plus the .Wearable(...) chain. New static factories on CustomItemBuilder:

  • Head: Helmet / BikeHelmet / RiotHelmet / DustMask / Goggles / Balaclava / Scarf
  • Torso: Hoodie / Shirt / TorsoArmor / BellyArmor / Belt / Bandolier / FannyPack
  • Limbs: Gloves / TacticalGloves / ArmWarmers / LimbWraps / KneePads / Sneakers / Boots / MaterialPouch / LegPouch

Each factory is exactly equivalent to Create(id, owner, ItemTemplate.X) — they exist purely for readability at the call site.

Breaking Changes

None. Uhhh maybe.


0.7.0 — 2026-06-06

CustomItemRegistry — Builder API & Spawnable Custom Items

The 0.2.0-era limitation is gone. Custom items registered through ScavLib are now fully spawnable at runtime — Utils.Create(id, ...) and GameUtil.SpawnItem(id, ...) resolve custom IDs to a cloned template Prefab instead of failing at the Resources.Load call.

The new CustomItemBuilder is the recommended entry point. It is a chainable builder that constructs the ItemInfo, picks a clone template, registers the ID, and (optionally) registers display-name and description locale keys in a single fluent call:

CustomItemBuilder.Create("mymod_salve")
    .FromTemplate(ItemTemplate.Bandage)
    .Category("medical")
    .Weight(0.3f).Value(15)
    .Tags("medicine", "dressing")
    .DisplayName("Herbal Salve")
    .Description("A pungent paste that soothes burns and shallow cuts.")
    .UsableOnLimb((limb, item) =>
    {
        limb.skinHealAmount += 15f;
        limb.pain           -= 20f;
        item.condition      -= 0.25f;
    })
    .Register();
  • FromTemplate(ItemTemplate) — picks which vanilla Prefab to clone as the visual / physical base. The ItemTemplate enum covers the common bases (Bandage, Pills, Bottle, Generic, etc.). Falls back to a sensible default if omitted.
  • Resource resolution — ScavLib applies a Priority.Low Harmony Prefix to both Utils.Create(string, Vector2, float) and Utils.Create(string, Transform). Registered custom IDs are intercepted and instantiated from the cloned template; unknown IDs are passed through to the game's normal Resources.Load path, so other mods that patch Utils.Create keep working.
  • Owner Ledger — every registration records the owning mod name. The scavlib check command now lists custom items alongside commands, and conflicts are reported with both owners.

The legacy CustomItemRegistry.RegisterItem(id, info) and RegisterSimpleItem(...) entry points are retained and now marked [Obsolete] with a one-line message pointing to CustomItemBuilder. Existing 0.6.x mods that only registered metadata continue to compile and run; they simply do not get a spawnable Prefab unless re-declared through the Builder.

See the CustomItemRegistry page for the full Builder reference, ItemTemplate enum, and migration notes.


RecipeBuilder — Custom Crafting Recipes

A new ScavLib.crafting namespace exposes RecipeBuilder, a chainable API for adding entries to the game's Recipes.recipes list. Recipes plug into the existing crafting UI and INT-skill checks — no extra wiring required.

RecipeBuilder.Create("mymod_salve_recipe")
    .Category(RecipeCategory.Medicine)
    .RequireINT(3)
    .Ingredient("bandage", minCondition: 0.5f)
    .Ingredient("alcohol", amount: 50f, isLiquid: true)
    .IngredientByQuality(new CraftingQuality("herb", 1f))
    .Result("mymod_salve", amount: 1, resultCondition: 1f)
    .Register();
  • Ingredient(specificId, ...) matches a concrete item ID; the underlying RecipeItem.specific flag is set internally so callers never touch it.
  • IngredientByQuality(quality) matches any item carrying the listed CraftingQuality (the same mechanism vanilla uses for "any blade", "any disinfectant", etc.).
  • Result(...) / ResultLiquid(...) chooses the produced item and amount. DontDrainResultLiquid() exposes RecipeResult.dontDrainResultLiquid for recipes whose product is itself a WaterContainerItem that should arrive pre-filled.
  • Failure behavior is unchanged from vanilla — INT shortfall of 1 may reduce result quality to a 0.2 ~ 0.9 multiplier; a shortfall of 3 triggers the gore penalty on both legs. Keep RequireINT close to your target player level.

Liquid results follow RecipeResult.SpawnResult exactly: ScavLib will first try to top off a single-liquid container the player already carries, and otherwise spawn a temporary craftingbottle that despawns after 300 s.

See the RecipeBuilder page for the full API.


CustomLiquidBuilder — Custom Liquids

ScavLib.liquid.CustomLiquidBuilder registers new LiquidType entries into Liquids.Registry and exposes the two consumption hooks the game ships with:

CustomLiquidBuilder.Create("mymod_tonic")
    .DisplayName("Bitter Tonic")
    .Color(new Color(0.35f, 0.55f, 0.20f))
    .ValuePerLiter(8f)
    .Quality("medicine", 0.4f)
    .OnDrink((ml, body) =>
    {
        body.painShock -= 10f * (ml / 100f);
        body.sicknessAmount = Mathf.Max(0f, body.sicknessAmount - ml * 0.05f);
    })
    .HealthUsable()
    .OnHealthUse((ml, limb) =>
    {
        limb.skinHealAmount += 5f * (ml / 50f);
    })
    .Register();

Delegate signatures match the game exactly: OnDrink(float ml, Body body) and OnHealthUse(float ml, Limb limb). The builder honours every vanilla LiquidType field — healthUsable, injectable, injectionSickness, localeFromItem, and the qualities list — and supports Quality(...) calls for crafting recognition.

See the CustomLiquidBuilder page for the full API.


LocaleRegistry — i18n for Custom Content

ScavLib.locale.LocaleRegistry lets mods supply translations for their custom IDs without shipping a forked Lang/EN.json. Entries are layered on top of the game's Locale.GetItem / GetBuilding / GetMoodle / GetOther lookups via a Harmony Postfix, so the vanilla fallback chain is preserved: current language → EN → bare id.

LocaleRegistry.AddItem("mymod_salve",  "EN", "Herbal Salve");
LocaleRegistry.AddItem("mymod_salve",  "ZH", "草药膏");
LocaleRegistry.AddOther("mymod_salve_desc",
    "EN", "A pungent paste that soothes burns and shallow cuts.");

Bulk loading from JSON / dictionaries is supported via AddItems(lang, dict) and friends. CustomItemBuilder.DisplayName(...) / Description(...) and CustomLiquidBuilder.DisplayName(...) are thin wrappers over this system — calling them is equivalent to manually adding the EN entry. Language switches via Locale.ChangeLanguage(...) reload the scene, and LocaleRegistry's table survives the reload.

See the LocaleRegistry page for the full API and the mod-lang JSON loader.


Other Improvements

  • scavlib check now reports custom items, recipes, and liquids alongside Harmony patch status and commands, with owner-mod attribution and conflict detection.
  • All four registries (items, recipes, liquids, locale) sit behind the same Owner Ledger pattern introduced for commands in 0.4.2, so cross-mod conflicts surface in a single diagnostic.

Breaking Changes

None. I don't think adding back existing features is a destructive update.


0.6.1 — 2026-06-04

uGUI Control Set Expansion

The UguiBuilder control set is significantly expanded. All additions are purely additive — existing AddLabel / AddButton / AddToggle / AddSlider / AddProgressBar / AddScrollView / AddSeparator / AddSpace signatures are unchanged.

New input controls:

  • AddInputField(initialText, placeholder, onValueChanged, onEndEdit) — a single-line text field returning TMP_InputField. onValueChanged fires on every keystroke; onEndEdit fires on focus loss / Enter.
  • AddNumberField(initial, step, min, max, isInteger, onChange) — a numeric stepper laid out as [ - ][ field ][ + ], returning the new UguiNumberField. Reads through Value; programmatic writes via SetValue(value, notify); Step(direction) applies one increment. Values are clamped to min/max, and isInteger rounds to whole numbers.

New selection controls:

  • AddImage(sprite, pixelSize, maxSize) and the AddImage(spriteName, ...) overload — display a sprite sized from its texture (with a pixel multiplier and a max edge length), returning Image. Non-interactive by default.
  • AddDropdown(options, initialIndex, onChange) — a self-contained dropdown returning UguiDropdown. The body is a compact button (current selection + ▼); options expand on a global overlay and collapse on outside click. Read Index / SelectedText; set programmatically with SetIndex.
  • AddToggleGroup(options, initialIndex, style, onChange) — a mutually exclusive selector returning UguiToggleGroup. style chooses between UguiToggleGroupStyle.Checkbox (one labelled checkbox per row) and Buttons (a single row of equal-width buttons). Read SelectedIndex; set with SetSelected.

Layout Containers

  • BeginHorizontal(rowHeight) — returns a UguiHorizontalRow for placing controls side by side. Supports fixed-width and text-fitted AddSmallButton, plus AddFlexibleSmallButton for equal-width fill. The row implements IDisposable, so a using block settles it automatically.
  • BeginTabs() — returns a UguiTabView. Add pages with AddTab(name, buildPage); switch with Select(index) and read the current page via ActiveIndex. Also IDisposable — a using block defaults to the first page and completes initial layout on exit.
using (var tabs = b.BeginTabs())
{
    tabs.AddTab("General", page => page.AddToggle("God Mode", false));
    tabs.AddTab("Advanced", page => page.AddSlider("Rate", 0f, 10f, 1f));
}

Tooltips & Overlay Layer

A shared overlay layer now backs both tooltips and dropdowns. It renders on a single persistent canvas, always above open windows, and survives scene transitions.

  • WithTooltip(name, desc) — a chainable extension on any control return value (or GameObject) that attaches a hover tooltip.
b.AddButton("Heal All", () => PlayerUtil.HealAll())
 .WithTooltip("Heal All", "Restores every limb and clears status effects.");

ScavLib's tooltips are deliberately isolated from the game's native tooltip system, so a control with a ScavLib tooltip no longer shows a duplicate game-native tooltip alongside it.


Window & Theme Additions

  • Close button — windows can now show an X close button in the top-right of the title bar. It is enabled by default and is suppressed when the title bar is hidden. New theme metrics control its size and right-edge padding.
  • Click-blocking — a visible window with BlockGameInput = true now reliably absorbs clicks so they do not pass through to world-space actions (item pickup, etc.); clicks on empty space still pass through.
  • Theme metricsUguiTheme.Metrics gains the layout values backing the new controls (small-button sizing, tab header height, input-field height and padding, stepper-button width, dropdown arrow width / row height / max visible rows, tooltip width / padding / cursor offset, toggle-group button height, default image max size, and scrollbar width).

Breaking Changes

None.


0.6.0 — 2026-06-01

uGUI Window Framework

A full uGUI-based UI framework now lives under ScavLib.gui.ugui, intended as the primary player-facing UI layer. The existing IMGUI windows are retained as a debug-only facility (see the rename below). Public API:

  • UguiWindowBase — inherit and implement Build(UguiBuilder) to define a window. Supports a hotkey ToggleKey, per-window Theme, runtime-toggleable dragging (DefaultDraggable / SetDraggable), and click-blocking (DefaultBlockGameInput / BlockGameInput). Register via UguiWindowBase.Register(window).
  • UguiBuilder — control builder: AddLabel, AddButton, AddToggle, AddSlider, AddProgressBar, AddScrollView, AddSeparator, AddSpace.
  • UguiTheme — immutable, chainable theme object. Derive variants with WithRowSpacing, WithRowHeight, WithHeaderHeight, WithWindowSize, WithPanelColor, WithContentPadding, WithTitleAlignment, WithHiddenTitle, WithTitlePadding, etc. The default theme matches the game's native look.
  • UguiPixelBar — pixel-snapped fill bar used by AddProgressBar.

uGUI windows render through a single persistent overlay canvas, so they are available in both the main menu and in-game and survive scene transitions.


Breaking Changes

The IMGUI menu framework was moved into its own sub-namespace and renamed. This is a source-breaking change — IMGUI mods written against 0.5.x will not compile until updated.

0.5.x (old) 0.6.0 (new)
ScavLib.gui namespace ScavLib.gui.imgui namespace
MenuWindow ImguiWindow
MenuBuilder ImguiMenuBuilder
MenuManager ImguiMenuManager

Migration: change using ScavLib.gui; to using ScavLib.gui.imgui;, then rename MenuWindowImguiWindow, MenuBuilderImguiMenuBuilder, and MenuManagerImguiMenuManager. Member names and method signatures are unchanged, so no other edits are required.


0.5.0 — 2026-05-29

PlayerUtil — Partial-Class Split & Full Body Coverage

PlayerUtil is now a single public static partial class split across multiple source files for maintainability. The public API is unchanged by the split; the files are PlayerUtil.cs (inventory), .Vitals, .States, .Drugs, .Appearance, .Sleep, .LastStand, .Recovery, .RawWrites, .Thresholds, and .Compat.

Coverage was completed to expose nearly every readable/writable Body field:

  • Drug componentsHasPainkillers / HasAntidepressants / HasSleepingPills, with matching RemovePainkillers / RemoveAntidepressants / RemoveSleepingPills. Plus GetOpiateHappiness, GetAntidepressantHappiness, and GetCaffeinated.
  • Last StandTriedRollingLastStand, SuccessfullyRolledLastStand, GetLastStandTime, GetAntibioticImmunityTime (and the HasTriedLastStand / HasSuccessfullyRolledLastStand aliases in .States).
  • SleepGetBadSleepAmount, GetGoodSleepTime.
  • AppearanceIsDisfigured, IsEyeGone, IsBothEyesGone, IsMindWiped, GetHorrifiedLevel, GetClawHealth, GetWeightOffset.
  • Extended state queriesIsBreathing, IsInWater, HasScubaGear, IsStanding, IsCrouching, IsOnHardStimulants, UsedNeuralBooster, IsUsingSleepingBag, IsBothHandsUnusable, IsAboveMedicalCutoff, CanTakeNap, AllowUseItem, IsFibrillationRising, IsBrainDying.
  • Computed read-only propertiesGetBloodVolumePercentage, GetTempDiffFromNormal, GetDesensitizedMult, GetCorpsesSeen, GetBloodPressureReadout, GetRespiratoryRateReadout, and several per-second movement / clotting / immunity multipliers.
  • Raw writes — the Set*Raw family was completed across every extended vital and boolean flag, each with Mathf.Clamp protection.

HealAll() now also clears active Painkillers / Antidepressants / SleepingPills components and resets badSleepAmount, opiateHappiness, and antidepressantHappiness, mirroring Body.TryLastStand() cleanup.


Compatibility Aliases

The partial-class split renamed several boolean accessors from Get* to Is* / Has*. The old names are preserved as thin aliases in PlayerUtil.Compat.cs so mods written against 0.4.x compile unchanged: GetDisfigured, GetEyeGone, GetBothEyesGone, GetSleeping, GetFibrillationForced, GetHasPulmonaryEmbolism. Prefer the canonical Is* / Has* names in new code.


ModSession & ModRegistry

Internal storage migrated to a ModSession-keyed dictionary. Each registration now retains its EventBus adapter handle so the planned 0.5.1 enable/disable flow can unregister cleanly.

  • New public ModRegistry.GetSession(string name) returns the runtime ModSession (exposing Info, Lifecycle, and IsEnabled).
  • ModSession.IsEnabled is always true in 0.5.0; toggling arrives in 0.5.1.

VersionedDependency

ModInfo dependencies are now backed by VersionedDependency, which supports optional MinVersion / MaxVersion bounds with loose version parsing (pre-release / build suffixes stripped, bare integers padded). Checks are advisory only — a mismatch logs a warning but does not block loading. The legacy string-array ModInfo constructor and the Dependencies name shim remain for backward compatibility.


EventBus.GetHandlerCount

New EventBus.GetHandlerCount(Type) and generic GetHandlerCount<T>() report the number of handlers subscribed to an exact event type. Handlers subscribed to a base type are not counted (inheritance is resolved at Post() time). Useful for skipping expensive payload preparation when nobody listens.


CommandRegistry Query API

New CommandRegistry.GetOwner(string name) and GetAllRegistered() expose the owner ledger for tooling. The scavlib check diagnostic is now built on top of them, reporting Harmony patch status, command owners, and conflicts.


Per-Patch Failure Isolation (ScavLibPlugin)

ScavLibPlugin now applies each Harmony patch class individually inside its own try/catch, recording success/failure in PatchStatus / PatchErrors. A single failing patch disables only its dependent events — the rest of ScavLib keeps running, and scavlib check shows exactly which patch failed and why.


Breaking Changes

None. All additions are purely additive, and the renamed accessors retain their old names as aliases.


0.4.2 — 2026-05-27

Hierarchical Subcommands

BaseCommand now supports an optional SubCommands dictionary for structured command routing. The new ExecuteSubCommand() helper handles dispatch, help output, and unknown-subcommand errors automatically — no more manual if/else or switch blocks.

Subcommand keys are automatically merged into ArgAutofill[0] at registration time, so first-level subcommand names get Tab completion for free.

Limitation:​ The game console only consults the top-level command's autofill table. Nested subcommand names (second level and beyond) cannot be Tab-completed — expose them via your help output instead.

SubCommands is rejected at registration time if ArgDescription[0] starts with bool or position, because the game's Command constructor will already auto-inject candidates for those types and the autofill keys would collide.


Command Owner Ledger & TryRegister

CommandRegistry.TryRegister(cmd, ownerModName, out error) is the new preferred registration API. It returns explicit success/failure info and records which mod owns each command in an internal ledger. The legacy Register(BaseCommand) overload is unchanged and records null as owner.


CommandRegistry.Unregister

CommandRegistry.Unregister(string name) removes a previously-ScavLib-registered command from the game's console. Only commands present in the owner ledger can be removed — game-native commands are protected automatically.


scavlib check

New diagnostic subcommand. Run scavlib check in the developer console to see:

  • Status of every Harmony patch ScavLib applies.
  • All commands ScavLib has injected, paired with their owner mod name.
  • A conflict summary if any names collide.

Per-Patch Failure Isolation

ScavLib's Harmony patches are now applied one class at a time inside individual try/catch blocks. A single failing patch no longer takes down the entire plugin or its dependents — only the events tied to the failed patch go quiet, and scavlib check shows exactly which one failed.


GameUtil.Log Multi-Line Support

GameUtil.Log now splits on \n and emits one console call per line. The game's log command tokenizes input on spaces and previously lost everything after an embedded newline. CRLF line endings are handled correctly; empty lines are skipped.


Command Name Validation

BaseCommand.ValidateName is invoked at registration time and rejects:

  • null or empty names
  • names containing spaces
  • names that collide with the 47 built-in game commands

A soft warning (not a rejection) is emitted for names without an underscore prefix, encouraging the <modname>_<commandname> convention.


Breaking Changes

None.


0.4.1 — 2026-05-26

Fixes & Improvements

  • EventBus deduplicationEventBus.Register() now silently unregisters any existing handlers from the same listener before re-registering. Calling Register() more than once on the same object no longer causes callbacks to fire multiple times.

  • LayerLoadedPatch performance — The private instantiatingWorld field is now resolved via reflection once and cached, eliminating a Traverse allocation on every WorldGeneration.Update() frame.

  • WorldUnloadingPatch refactor — Patch classes have been promoted from nested to top-level, improving Harmony compatibility and avoiding potential class-resolution issues on some runtimes.

  • ModRegistry O(1) lookupTryFind() and IsRegistered() now use a name-keyed dictionary index instead of a linear scan.

  • ScavLibCommand line output — Each line of scavlib status output is now issued as a separate console call, fixing truncated display in the in-game console.

Breaking Changes

None.


0.4.0 — 2026-05-25

Lifecycle System

v0.4.0 introduces a first-class lifecycle system for mods. By passing an IModLifecycle implementation to ModRegistry.Register(), your mod's callbacks are automatically wired into the EventBus — no manual EventBus.Register() call required.

ModRegistry.Register(new ModInfo("MyMod", "1.0.0", "desc"), new MyLifecycle());

The IModLifecycle interface defines six callbacks:

Callback When it fires
OnEnabled() Immediately after successful registration
OnDisabled() Reserved for future use
OnWorldLoaded(WorldLoadedEvent) World fully initialized, Body safe to access
OnLayerLoaded(LayerLoadedEvent) Every layer (biome) finishes generating
OnWorldUnloading(WorldUnloadingEvent) Before layer transition or save-and-exit
OnWorldDestroyed(WorldDestroyedEvent) WorldGeneration.OnDestroy() completes

ModLifecycleBase provides default empty implementations of all six callbacks. Inherit from it and override only what you need.


New Events

Three new pre-defined game events are now built into ScavLib and triggered automatically via Harmony:

LayerLoadedEvent

Fired every time a layer (biome) finishes generating and is playable, including the very first layer of a session.

Property Type Description
BiomeDepth int Depth of the layer that just loaded. 0 = first layer.
IsFirstLoad bool true only on the first layer of the session.

WorldUnloadingEvent

Fired before WorldGeneration.Clear() runs, giving you a last chance to read player and world state safely.

Property Type Description
CurrentBiomeDepth int The depth being torn down.
NextBiomeDepth int The depth to be generated next, or -1 on save-and-exit.
IsExitToMenu bool Convenience: true if NextBiomeDepth < 0.

WorldDestroyedEvent

Fired after WorldGeneration.OnDestroy() completes. Use for global cleanup that must survive layer transitions.

Property Type Description
LastBiomeDepth int Last known biome depth before destruction. May be -1.
WasSaveAndExit bool true if triggered by save-and-exit.

ModInfo — Dependency Declaration

ModInfo now accepts an optional dependencies parameter listing the names of mods this mod depends on. Missing dependencies are logged as warnings at registration time (advisory only — load order is still enforced by BepInEx [BepInDependency]).

new ModInfo("MyMod", "1.0.0", "desc", "Author", "ScavLib", "OtherMod")

scavlib status now displays declared dependencies after the mod entry:

ScavLib Activated.
Version: 0.4.0
Registered Mods: [
  MyMod v1.0.0 by Author [F] Deps: [ScavLib, OtherMod]
]

The [F] tag indicates the mod was registered with an IModLifecycle object.


Breaking Changes

None.


0.3.0 — 2026-05-24

PlayerUtil — Full Body Coverage

PlayerUtil has been expanded to cover virtually every writable field on the player Body component, up from the ~15 methods in 0.2.3. The new additions are organized into named sections within the source file:

  • Circulation / BloodBloodViscosity BloodVesselSize FibrillationProgress HeartRatePressureOffset FibrillationForced HasPulmonaryEmbolism StrokeAmount
  • AdrenalineAdrenaline CurAdrenaline
  • Infection / Toxicology / SepsisSepticShock SicknessAmount VenomTotal VenomCurrent
  • BleedingInternalBleeding Hemothorax
  • Neurology / PsychologyShock PainShock TraumaAmount RadiationSickness HorrifiedLevel FocusedLevel BrainGrowSickness
  • Happiness / WeightHappinessBase WeightOffset
  • External ConditionsWetness Dirtyness SnowAmount HearingLoss
  • ClawsClawHealth ClawRegrowTime
  • ImmunityImmunity AntibioticImmunityTime
  • StimulantsCaffeinated
  • AppearanceDisfigured EyeGone BothEyesGone
  • SleepSleeping BadSleepAmount
  • Read-only derived propertiesAveragePain TotalBleedSpeed IsBreathing IsInWater HasScubaGear IsStanding IsCrouching IsOnHardStimulants UsedNeuralBooster

Each writable field provides both a Get read and a SetRaw direct-field write with Mathf.Clamp protection. The two-path convention (recommended writes vs. Raw writes) introduced in 0.2.0 is preserved throughout.


PlayerUtil.Thresholds — Expanded Constants

PlayerUtil.Thresholds has been expanded from ~50 to 60+ named constants, now covering every threshold used by the game's moodle system:

Category Constants added
Blood pressure CRITICAL_LOW LOW_1~3 HIGH_1~3 CRITICAL_HIGH
Bleeding speed CRITICAL HEAVY MEDIUM
Internal bleeding CRITICAL
Hemothorax PRESENT HEAVY
Stroke CRITICAL
Happiness Full 8-tier scale (MISERABLEHAPPY_4)
Weight offset Full 8-tier scale (UNDERWEIGHT_4OVERWEIGHT_4)

All values were extracted directly from MoodleManager.AddAllMoodles() and match the thresholds used by the game's own UI.


LimbUtil — Aggregate Queries

LimbUtil gains several new aggregate query methods:

  • HasBrokenBone()true if any non-dismembered limb is broken
  • HasDislocation()true if any non-dismembered limb is dislocated
  • HasInfection()true if any non-dismembered limb has an active infection
  • HasDismemberment()true if any limb has been dismembered
  • GetMaxInfection() — highest infection amount across all limbs (0 ~ 100)
  • GetAveragePain() — mirrors Body.averagePain
  • GetTotalBleedSpeed() — total bleed speed across all limbs

Bug Fixes

  • PlayerUtil.HealAll() — Now also resets shock, adrenaline, curAdrenaline, painShock, traumaAmount, wetness, dirtyness, hearingLoss, hasPulmonaryEmbolism, venomCurrent, venomTotal, internalBleeding, hemothorax, and clawHealth — fields that were silently left dirty in 0.2.x.

Breaking Changes

None.


0.2.3

Bug Fixes

  • PlayerUtil — Fixed wrong namespace (ScavLib_API.utilScavLib.util) and wrong access modifier (internal classpublic static class). The class was previously invisible to all external mods.
  • ItemPickupPatch — Removed unused bool force parameter from the Postfix signature to eliminate unnecessary Harmony reflection binding.
  • CommandRegistryPatch — Added [HarmonyPriority(Priority.High)] to guarantee commands are registered before WorldLoadedEvent fires.

Added

  • PlayerUtil — 9 new methods:
    • GiveItem(string id) — Spawn item at player feet and auto-pickup
    • TakeItem(string id) — Drop first matching item from inventory
    • HasItem(string id) — Check surface inventory by ID
    • GetAllItems() — Get all slot inventory items
    • IsAlive() / IsConscious() / IsDying() — State queries
    • GetHunger() / GetThirst() — Vital reads

0.2.2

Bug Fixes

  • MenuManager — Fixed menu windows not rendering. Migrated MenuManager from a standalone DontDestroyOnLoad GameObject to a MonoBehaviour renderer attached to PlayerCamera. Removed the now-unused MenuWindowStyle enum.

0.2.1

Bug Fixes

  • PlayerUtil — Fixed Body.PickUpItem call signature: parameter type corrected from Item it to Item item.

0.2.0 — 2026-05-23

New Modules

  • PlayerUtil — Vital sign reads, state queries, recommended writes, raw writes, inventory shortcuts, and PlayerUtil.Thresholds (~50 named constants from the game's moodle system).
  • SkillUtil — Typed SkillType enum, level/XP reads, XP writes, raw level set, global XpMultiplier.
  • ItemUtil — World-space item scanning, safe condition modification, favourite toggling, safe destruction, global registry queries.
  • CustomItemRegistry — Register custom ItemInfo definitions into Item.GlobalItems with automatic injection timing.

Event System

  • Added pre-defined game events: WorldLoadedEvent, ItemPickedUpEvent, ItemDroppedEvent — triggered automatically via Harmony, no manual patching required.
  • EventBus.Post<T> now dispatches along the full inheritance chain.
  • Exception isolation improved: handler errors now include event type context in the log message.

Improvements

  • GameUtil — Added IsWorldLoaded, GetWorld(), SpawnItemAt(id, Transform), Notify().
  • ModRegistry — Added TryFind(name, out ModInfo) and IsRegistered(name). Duplicate registration now logs a warning instead of silently succeeding.

Bug Fixes

  • MenuWindow — Fixed auto-height: Height = 0 now uses true IMGUI content-adaptive sizing instead of a hardcoded 400px fallback.
  • MenuManager — Removed erroneous using UnityEngine.Windows reference.
  • CommandRegistry — Removed dead Init() body; method is now a documented extension point called explicitly from ScavLibPlugin.Awake().

Breaking Changes

  • EventBus.Post<T> now dispatches to base-type subscribers. Only affects code subscribing to BusEvent directly, which was not meaningful in 0.1.0.
  • MenuWindow with Height = 0 now resizes to content instead of 400px. Add public override float Height => 400f to restore the previous behavior.

0.1.0 — Initial Release

  • MenuWindow / MenuBuilder — IMGUI menu framework.
  • ConfigManager — BepInEx config wrapper.
  • EventBus / [Subscribe] / BusEvent — Attribute-based event bus.
  • CommandRegistry / BaseCommand — Console command registration.
  • GameUtil — Basic player state and item spawning helpers.
  • ModInfo / ModRegistry — Mod metadata registration.

Clone this wiki locally