EVB-Lithium_fork icon

Lithium fork

Modular balancing framework for Schedule I (IL2CPP): plants, mushrooms, mixing stations (Mk1 & Mk2), lab oven, cauldron, customers, shops, employees. Every feature is optional and JSON-configurable.

Last updated a day ago
Total downloads 27
Total rating 0 
Categories Tools Misc
Dependency string EVB-Lithium_fork-1.0.9
Dependants 0 other packages depend on this package

This mod requires the following mods to function

LavaGang-MelonLoader-0.7.1 icon
LavaGang-MelonLoader

The World's First Universal Mod Loader for Unity Games compatible with both Il2Cpp and Mono

Preferred version: 0.7.1

README

Lithium_fork

Version : 1.0.9.2  •  Target : Schedule I v0.4.5f2 (IL2CPP)  •  Loader : MelonLoader 0.7.1

Lithium_fork is a community-maintained fork of the original Lithium by DerTomDerTwitch — a modular balancing framework for Schedule I. Every module is independent and JSON-configurable under UserData/Lithium/. Enable only what you need. Recent work focused on making the Mk2 Mixing Station timer flow in real time, adding full mushroom (shroom) support to the Plants module, cleaning up the Plants config (separated Plants and Shrooms sections with inline notes), and major performance / log cleanup on the Mk2 UI path.

Not the official Nexus upload — use this fork only if you want the current-game-version build.


Table of contents

  1. Installation
  2. Features at a glance
  3. What's new — v1.0.9.2 highlights
  4. Changelog
  5. Module reference
  6. Building from source
  7. Troubleshooting
  8. Credits & links

Installation

  1. Install MelonLoader 0.7.1 on your Schedule I install.
  2. Launch the game once with MelonLoader so it generates MelonLoader/Il2CppAssemblies/.
  3. Copy Lithium_fork.dll into Schedule I/Mods/.
  4. Launch the game. On first run, default JSON configs are created under UserData/Lithium/ with all modules disabled.
  5. Open the config file(s) for the module(s) you want, set "Enabled": true, tune the values, save, and restart the game.

⚠️ If you are migrating from the original Lithium.dll, remove it from Mods/ first. Only one of the two can be loaded at a time.

The existing UserData/Lithium/ JSON files from the original mod stay compatible (same folder, same module names, same keys with backward-compatible aliases where things changed).


Features at a glance

Module Config file What it does
PropertyPrices PropertyPrices.json Override the purchase price of every property.
Plants (pots) Plants.jsonPlants section Speed up / slow down weed / coca / meth growth. Soil drain rate.
Plants (shrooms) Plants.jsonShrooms section Speed up / slow down mushroom growth. Substrate drain rate.
DryingRacks DryingRacks.json Drying time per rack operation.
MixingStations MixingStation.json Mix duration multiplier (Mk1 and Mk2), input capacity.
LabOven LabOven.json Cook speed multiplier.
ChemistryStation ChemistryStation.json Recipe cook speed multiplier (chem station only).
CauldronCustom CauldronCustom.json Manual / NPC cauldron batch duration.
Employees Employees.json Daily wages, walk speeds, max assignments, action timings (pour / water / sow / harvest).
Customers Customers.json Effect-based contracts, strict handover rejection, effect-bonus payments, dealer suitability.
DynamicsOrders DynamicsOrders.json XP-scaled contract quantity & payment (anchored brackets, smoothed interpolation).
Shops Shops.json Shop & supplier prices (in-person + phone / Dead Drop), stock overrides.
StackSizes StackSizes.json Override item / category stack sizes.
EffectCombos EffectCombos.json Allow / forbid effect combinations.
TrashGrabber TrashGrabber.json Grabber range and capacity.
WateringCans WateringCans.json Capacity & refill behaviour.
Storyline Storyline.json Storyline pacing tweaks.

Every module has its own Enabled flag and defaults to false until you flip it.


What's new — v1.0.9.2 highlights

1. Mk2 Mixing Station timer actually flows in real time

In vanilla, the Mk2's screen counts down minute by minute (e.g. 30 → 29 → 28 …) while a mix runs. With the previous fork, the Mk2 screen froze on the initial duration even though the mix completed — only the Mk1's clock was being driven correctly. This is fixed:

  • OnMinPass advances CurrentMixTime exactly once per in-game minute, debounced by TimeManager.DailyMinSum (no more 2-by-2 jumps from FishNet RPC echoes).
  • Mk2 UI is rewritten from MixingStationCanvas / station-mesh TMP_Text references cached per station and written only when the remaining minutes value actually changes — no more per-frame GetComponentsInChildren + Regex.Replace.
  • Il2CppObjectBase.TryCast<MixingStationMk2>() is now used everywhere for type checks, fixing IL2CPP-inheritance edge cases where is/as returned false on legitimate Mk2 instances.
  • Authority for CurrentMixTime advancement is resolved cleanly across solo / host / client modes.
  • Stack-overflow crash (recursive OnMinPass trampoline) is gone — prefix always return false; and replicates vanilla deterministically.

2. Mushroom (Mushroom Bed / ShroomColony) growth support

PlantGrowth now supports mushrooms in addition to pot-based plants:

  • Subclasses of GrowContainer (e.g. MushroomBed) are discovered at runtime and patched directly on GetTemperatureGrowthMultiplier, GetCurrentGrowthRate, and OnMinPass so shroom-specific modifiers don't get masked by the base patches.
  • A separate Shrooms config section means plants and mushrooms can have different speeds (e.g. mushrooms 2× faster, plants normal).
  • Water / substrate drain rate is configurable independently for plants and shrooms.

3. Plants.json — clearer structure

The config is now self-documenting and split into two named sections:

{
  "Enabled": true,
  "_Note": "Lithium 'Plants' module — configure PLANTS (section 'Plants') and MUSHROOMS (section 'Shrooms') separately.",

  "Plants": {
    "_Note": "POT-BASED PLANTS (Weed/Coca/Meth). GrowthSpeed: 1.0 = normal speed, 2.0 = 2x faster, 0.5 = 2x slower. WaterDrainModifier: soil drying speed — 1.0 = normal, 0.5 = dries 2x slower.",
    "GrowthSpeed": 1.0,
    "WaterDrainModifier": 1.0
  },

  "Shrooms": {
    "_Note": "MUSHROOMS (Mushroom Bed). GrowthSpeed: 1.0 = normal speed, 2.0 = 2x faster, 0.5 = 2x slower. WaterDrainModifier: substrate drying speed — 1.0 = normal, 0.5 = dries 2x slower.",
    "GrowthSpeed": 1.0,
    "WaterDrainModifier": 1.0
  },

  "GrowthModifier": 1.0,
  "WaterDrainModifier": 1.0
}
  • Plants.GrowthSpeed controls weed / coca / meth growth speed.
  • Shrooms.GrowthSpeed controls Mushroom Beds.
  • The root-level GrowthModifier / WaterDrainModifier stay as legacy fallbacks so older JSON files keep working unchanged.
  • Inline _Note fields survive round-trips through the loader and document each section right in the JSON.

4. Performance & logs

  • Mk2 lag from per-frame text scans is gone (cached refs + change-detection debounce).
  • All diagnostic logs for MixingStation / Mk2 / canvas dump / OnMinPass ticks have been removed — the log is quiet during normal play.
  • Harmony warnings about missing methods (Update / LateUpdate / OnDestroy not declared on Mk2) are suppressed by switching the relevant TargetMethod lookups to AccessTools.DeclaredMethod.

5. Build / dev experience

  • Lithium.csproj now fails fast with a clear error if SCHEDULE_PATH (or LithiumScheduleGamePath) is missing, wrong, or points at MelonLoader/ instead of the game root.
  • AssemblyVersion / FileVersion / MelonInfo aligned on 1.0.9.2. The Thunderstore manifest.json uses 1.0.9 because Thunderstore enforces strict 3-segment SemVer.

Changelog

1.0.9.2 — Mk2 timer, Shrooms, Plants/Shrooms split, perf + log cleanup

  • Fix: Mk2 Mixing Station UI timer now counts down minute by minute (was stuck on the initial value).
  • Fix: Mk2 OnMinPass debounce via DailyMinSumCurrentMixTime advances once per in-game minute (no more 2-by-2 jumps).
  • Fix: Stack overflow on MixingStation::OnMinPass (Il2CppInterop recursion).
  • Fix: IL2CPP type-checks use Il2CppObjectBase.TryCast<MixingStationMk2>() instead of is/as (Mk2 instances are now correctly identified everywhere).
  • Fix: MixingStationCanvas rewrites the "X mins remaining" string after vanilla each frame — vanilla can no longer re-assert the old value.
  • Add: Mushroom growth support — GrowContainer subclasses (incl. MushroomBed) discovered at runtime; per-shroom Harmony patches for GetTemperatureGrowthMultiplier, GetCurrentGrowthRate, OnMinPass.
  • Add: New Plants.json schema — explicit Plants (pots) and Shrooms (mushroom beds) sections, each with GrowthSpeed + WaterDrainModifier and an inline _Note. Legacy root keys still respected as fallbacks.
  • Perf: Mk2 UI rewrite uses per-station TMP_Text cache + change-detection debounce; no more per-frame GetComponentsInChildren / Regex.Replace.
  • Chore: Diagnostic logging removed from MixingStationPatchLogic, MixingStationMk2Registry, MixingStationCapacityPatch, MixingStationCanvasUpdatePatch.
  • Chore: Harmony "method not found" warnings silenced via AccessTools.DeclaredMethod.
  • Chore: All in-code notes and JSON _Note strings translated to English.
  • Build: Strict, friendly errors when SCHEDULE_PATH is missing / wrong.

1.1.1 — pre-fork baseline (carryover from earlier fork builds)

  • Stable Mk1 mixing-station patches, employee re-application after FishNet init, fork-specific Customers / DynamicsOrders / Shops behaviour.

(Earlier history follows the original Lithium changelog on Nexus.)


Module reference

Plants — UserData/Lithium/Plants.json

Key Type Default Effect
Enabled bool false Master toggle for the module.
Plants.GrowthSpeed float 1.0 Multiplier on growth tick rate for pot-based plants (weed / coca / meth). 2.0 = 2× faster, 0.5 = 2× slower.
Plants.WaterDrainModifier float 1.0 Multiplier on soil drying rate. 0.5 = dries 2× slower.
Shrooms.GrowthSpeed float 1.0 Multiplier on growth tick rate for Mushroom Beds.
Shrooms.WaterDrainModifier float 1.0 Multiplier on substrate drying rate.
GrowthModifier (legacy root) float 1.0 Fallback used when a section omits GrowthSpeed.
WaterDrainModifier (legacy root) float 1.0 Fallback used when a section omits WaterDrainModifier.

Effective values used at runtime:

  • Pots: Plants.GrowthSpeed ?? GrowthModifier, Plants.WaterDrainModifier ?? WaterDrainModifier
  • Shrooms: Shrooms.GrowthSpeed ?? Shrooms.GrowthModifier ?? GrowthModifier, Shrooms.WaterDrainModifier ?? WaterDrainModifier

MixingStation — UserData/Lithium/MixingStation.json

  • MixSpeed (float, default 1.0) — scales GetMixTimeForCurrentOperation with Floor(result / MixSpeed). 2.0 ≈ half the required minutes, 0.5 ≈ double. Same semantics as LabOven.json's Speed.
  • InputCapacity — sets MaxMixQuantity on every Mixing Station instance via a Start postfix.
  • Mk1 & Mk2 both supportedOnMinPass is patched on the base MixingStation and on MixingStationMk2 when the game overrides (not merely inherits) the method.
  • Mk2 UI is driven from MixingStationCanvas + station-mesh TMP refs cached per instance; the displayed X mins remaining is rewritten after vanilla each frame so it always tracks CurrentMixTime.
  • Legacy: if your file still has MixStepsPerSecond and MixSpeed is left at 1.0, that integer is used as the multiplier once.

LabOven — UserData/Lithium/LabOven.json

  • Speed scales OvenCookOperation.GetCookDuration with Max(1, Floor(duration / Speed)) (clamped to ≥ 0.01). IsReady postfix keeps CookProgress >= GetCookDuration() aligned with the scaled duration.

ChemistryStation — UserData/Lithium/ChemistryStation.json

  • Speed — required cook minutes = Max(1, Floor(StationRecipe.CookTime_Mins / Speed)). The chemistry station uses ChemistryCookOperation.IsComplete + ChemistryStation.UpdateClock; postfixes align CurrentTime and refresh the alarm (DisplayCurrentTime = false).
  • Breaking: older configs used BonusStepsPerTick — use Speed instead.

CauldronCustom — UserData/Lithium/CauldronCustom.json

  • ManualMinutesPerBatch / NpcMinutesPerBatch — when > 0, replaces vanilla remaining batch time with that many minutes. When 0, falls back to ManualCookSpeed / NpcCookSpeed with the same semantics as LabOven.Speed.
  • DebugLogOnce — log first SendCookOperation once per session.
  • LogEverySendCookOperation — verbose; logs every batch with duplicate-skip reasoning. Does not require Enabled.
  • Patches Cauldron.SendCookOperation only (not StartCookOperation) and de-duplicates RPC echoes via NetworkObject.ObjectId.
  • Compatibility: do not run the Nexus Custom Cauldron Timers mod simultaneously with CauldronCustom.Enabled: true.

Employees — UserData/Lithium/Employees.json

  • Botanists: MaxAssignedPots, WalkSpeed, DailyWage, SoilPourTime, WaterPourTime, AdditivePourTime, SeedSowTime, HarvestTime — applied per hire via Harmony postfixes on HarvestPotBehaviour.StartAction and any other *PotBehaviour.StartAction exposed at runtime; overwrites the behaviour's timeRemaining / actionDuration after each action.
  • Cleaners: DailyWage, WalkSpeed, MaxBins — re-applied on NetworkInitialize___Early, again on NetworkInitialize__Late, then over the next ~45 frames so FishNet init doesn't overwrite them.
  • Chemists (MaxStations) & Packagers (MaxStations, MaxRoutes, PackagingSpeedMultiplier) — same re-application pass.

Customers — UserData/Lithium/Customers.json

  • Enabled must be true for any fork Customers behaviour.
  • Contracts.RejectHandoverWithoutPreferredEffects (default true) — block handover when delivered effects don't match the customer's preferred ones. Does not require Contracts.Enabled.
  • Contracts.RequireXPForStrictHandover (default false) — gate strict handover on Contracts.XPRequired. Leave false to always enforce strict matching.
  • Contracts.Enabled (default true) — rewrites TryGenerateContract to use listed products + preferred effects instead of vanilla random pick.
  • EffectBonus.Enabled — fork bonus payments on effect/quality match.
  • Notifications use Contracts.MessageTemplates (player) / Contracts.DealerTemplates (dealer), gated by SendNotification* and NotificationCooldownInMinutes.
  • ProcessHandover and ProcessHandoverServerSide both guarded so deliveries skipping the normal path still respect strict handover.

DynamicsOrders — UserData/Lithium/DynamicsOrders.json

  • Re-rolls contract payment & quantity based on LevelManager.TotalXP after CustomerContractRewriter runs.
  • Global.XpPaymentBrackets — sorted by MinXp, each defines OrderQuantity{Min,Max} and Payment{Min,Max}. Smooth interpolation between brackets so tier crossings don't snap.
  • Global.EnableOrderQuantityVariance (default true) — when false, quantity sticks near the top of the band (stricter progression).
  • Rules — optional per-NPC overrides keyed by Customer.NPC.ID. Sparse entries supported. On first run, missing/empty Rules is auto-populated with 66 customers at Enabled: false.
  • Counter-offer UI re-syncs price from Customer.OfferedContractInfo so the form matches the message after Dynamics re-rolls.

Shops — UserData/Lithium/Shops.json

  • Overrides re-applied each time a shop opens (SetIsOpen) so lazy-loaded UIs (e.g. Dark Market) get the right prices.
  • Limited stock (ItemOverrides.Stock) preserved across the same session (partial stock stays partial, real sold-out stays 0) with a ShopCode|itemId session cache cleared on returning to the main menu.
  • ArmsDealerShopCode armsdealer. ShroomsSupplierShopCode shrooms_shop. Dark Market → generic ShopCode shop on DarkMarketInterface GameObject.
  • In-person dealer prices use WeedSupplier / CokeSupplier / MethSupplier / ShroomsSupplier blocks. Phone / Dead Drop prices use NPC supplier blocks: Albert, Shirley, Salvador, Fungal (mushroom supplier, e.g. Fungal Phil) via PriceOverrides.
  • PhoneMirrorListingPriceFields (default false) — only flip to true if phone UI still shows wrong totals after overrides.

Building from source

  1. Set the game path before building (PowerShell):
    $env:SCHEDULE_PATH = "D:\SteamLibrary\steamapps\common\Schedule I"
    
    …or pass -p:LithiumScheduleGamePath="…" directly to dotnet build.
  2. Launch the game once with MelonLoader so MelonLoader\Il2CppAssemblies\ exists.
  3. From the repo root:
    dotnet build .\Lithium.csproj -c Release
    
  4. The DLL lands in bin\Release\net6.0\Lithium_fork.dll. Copy it into Schedule I\Mods\.

The .csproj will fail with an explicit error if SCHEDULE_PATH is missing, points at MelonLoader/ instead of the game root, or Il2CppAssemblies/Assembly-CSharp.dll isn't there.


Troubleshooting

  • MelonLoader log line on startup: Lithium_fork v1.0.9.2 confirms the right build is loaded. If you see an older version, you have a stale Lithium.dll or Lithium_fork.dll in Mods/.
  • Mk2 timer still frozen? Make sure MixingStation.json has Enabled: true. Look in MelonLoader.log for any MixingStation patch failure — that usually means a game update renamed a method (open an issue with the game version).
  • Shrooms grow at vanilla speed? Confirm Plants.json has Enabled: true and a Shrooms section with GrowthSpeed != 1.0. The log should show [Lithium_fork][Plants] Shroom patches applied: … and Shroom config in use: GrowthSpeed=….
  • Phone / Dead Drop prices wrong after overrides? Try setting PhoneMirrorListingPriceFields: true in Shops.json. If that still doesn't fix it, capture MelonLoader.log at the moment of Place Order and report with the game version.
  • Customers.json ignored? Check Enabled: true at the module root, and that Contracts.Enabled: true if you want contract-product rewriting. The startup log prints [Lithium_fork][Customers] Config: … with the effective flags.
  • JSON encoding looks broken in PowerShell: the files are UTF-8 (the loader uses Newtonsoft, which is UTF-8-aware). The garbled console output is just the terminal codepage — the mod reads the file correctly.

Credits & links

Role Who
Original Lithium DerTomDer / DerTomDerTwitch, YukiSora, and contributors per the Nexus page.
Lithium_fork Community fork maintained for current Schedule I IL2CPP builds. Not an official Nexus upload of the original file.
Link Description
Lithium on Nexus Original mod page.
Original Lithium docs Module concepts & sample configs.
Schedule I modding docs IL2CPP / MelonLoader reference.

Respect the original author's permissions on Nexus if you redistribute. Lithium_fork is a separate maintenance effort; credit DerTomDerTwitch and the original project when sharing.