Namespace GrindFest
Classes
- AlchemyTests
Alchemy system tests — brewing and recipe validation. Liquid transfer tests (pouring, filling) are in LiquidTests. Each public IEnumerator method is a separate test case.
- AnvilItemContainerTests
Anvil item container migration tests. These tests use ONLY bot API (DropInto, InteractWith) to place items and craft. They verify observable outcomes (items produced, items consumed) — NOT internal storage.
Purpose: safety net for migrating CraftingStationBehaviour.Items → InventoryContainerBehaviour. All tests should pass BEFORE and AFTER the migration.
- AutomaticHero
Base class for all bots
- AutomaticIdentifyToolBehaviour
Tool that automatically identifies unidentified items when mousing over them. Attach to any item that should have auto-identify capability when equipped.
- BlacksmithingTests
Blacksmithing system tests — anvil repair, socketing, forge smelting/salvage. Each public IEnumerator method is a separate test case.
- BookTests
Tests for the book system — verifies OneJS rendering pipeline puts correct content on the 3D book via shared RenderTextures.
Uses TEST_COLOR_BOOK which has solid-color pages using the leaf model: Cover: Red (1,0,0) — separate, via RenderCover content[0]: Green (0,1,0) — OpenFront right (leaf 1 front) content[1]: Blue (0,0,1) — OpenMiddle left (leaf 1 back) content[2]: Yellow (1,1,0) — OpenMiddle right (leaf 2 front) content[3]: Magenta (1,0,1) — after turn 1 left (leaf 2 back) content[4]: Cyan (0,1,1) — after turn 1 right (leaf 3 front) content[5]: Orange (1,0.5,0) — after turn 2 left (leaf 3 back) content[6]: White (1,1,1) — after turn 2 right (leaf 4 front)
- BrewHealthPotionTest
Test: Hero brews a health potion by interacting with a cauldron. Setup: Cauldron with water + bone, hero has ladle + Alchemy skill. Action: Hero InteractsWith cauldron (same as player bot script). Verify: Cauldron contains Health liquid.
- BurnableBehaviour
Unified fire/fuel component. Handles burning, fuel consumption, fire VFX, fire spread, damage, light flicker, crafting station attachment, and tooltip. Replaces the old FuelSystem, FuelBehaviour, and CampfireBehaviour.
- ButtonMasherAchievement
Button Masher - Manually use skills by clicking. Bots can't help you here.
- CloseableUIStack
Manages a stack of closeable UI elements so ESC closes the most recently opened item.
- CombatTests
Combat tests — attacking, killing, damage dealing.
- ConsumableTests
Consumable tests — drinking potions, eating food.
- CoreTests
Core framework tests — isolation, CleanSlate, runner behavior.
- DevTest
Base class for in-game developer tests. Tests run as coroutines in the live game — no NUnit, no scene reload. Editor-only (uses Addressables, Cheats patterns).
Each public method returning IEnumerator is discovered as a test. Group related tests in one class, each method is a separate test case.
- DevTestRunner
In-game developer test runner. Discovers all DevTest subclasses and runs their public IEnumerator methods as test cases in the live game. No NUnit, no scene reload.
Run all tests: Ctrl+Shift+T or
DevTestRunner.Instance.RunAllTests()
Run filtered:DevTestRunner.Instance.RunAllTests("PourWater")(substring match on ClassName.MethodName)
- DirectionMarker
Editor-only gizmo to visualize a transform's direction. Useful for projectile attach points, spawn points, aim directions, etc.
- DropItemTests
Simple drop tests to verify DropInto works correctly with different item types. Isolates the drop mechanism from crafting recipes.
- DungeonCrawlerAchievement
Dungeon Crawler - Enter 1,000 dungeons. The abyss files a restraining order.
- ExplorerCartographerAchievement
Explorer's Cartographer - The world writes itself. You just walk through it.
- ExternalAchievementService
Platform-agnostic service for unlocking external achievements (Steam, Kongregate, etc). Supports both progress updates (stat-based) and direct unlocks.
How it works with Steam:
- Each achievement defines a Steam Stat name (e.g. "stat_kills") and an achievement ID.
- Progress calls update the stat value. Steam shows progress in the overlay.
- When the stat reaches the threshold configured in the Steamworks dashboard, Steam auto-unlocks the achievement. We also call Trigger() as a safety net.
- Fireball
Casts a fireball that explodes on impact, dealing 1.2x weapon damage as fire damage to all enemies in a 2m radius
- FishStoryAchievement
The Fisher King - By the waters of Leman I sat down and wept.
- GlobetrotterAchievement
Globetrotter - Your bot has wanderlust.
- GoldAchievement
Gold Goes Brrr - Lifetime gold earned. Gold forever.
- InvokeOnReloadAttribute
Marks a static method to be automatically called when your bot script is recompiled. This is useful for destroying instantiated classes of your types that will stop being valid (mono behaviours are destroyed automatically) or storing state before your bot code is reloaded.
- ItemExperienceAffix
Adds experience bonus when item is equipped. Follows same pattern as ItemStatAffix for consistency.
- ItemPickupDropTests
Simple tests for core item pick-up and drop mechanics. Tests the building blocks that DropInto relies on.
- KillAchievement
Mass Extinction - Kill monsters. Lots of monsters. ALL the monsters.
- LifeDrainSpell
Channels to continuously drain life from target enemy and converts it to mana or health based on the spell variant
- LiquidTests
Liquid transfer tests — filling and pouring between containers (buckets, cauldrons, rivers). Each public IEnumerator method is a separate test case.
- Logger
Captures Unity log messages to Logs/alchemy_log.txt. Errors/exceptions always captured. Regular Debug.Log captured when from bot/alchemy code. Only active in Unity Editor.
- LumberjackingTests
Lumberjacking tests — chopping trees, gathering wood. Each public IEnumerator method is a separate test case.
- MagpieAchievement
Magpie - If it's on the floor, it's yours.
- MainRoadBehaviour
Marks a Road spline as part of the main road network. At Start(), reads spline knots and registers them as waypoints in MainPathBehaviour.Waypoints so GoToArea() navigation works.
Add this component to Road SplineMarker GOs (via +MainRoadBehaviour in ink). The road spline endpoints must align with neighboring area road splines for the waypoint chain to connect properly.
- MaxLevelAchievement
Number Go Up - Reach higher and higher levels. Brain happy.
- ModDefinition
Mod manifest deserialized from mod.json. Required in every mod folder for Workshop publishing and mod browser display.
- ModManager
Central mod manager. Handles discovery, deployment, and hot-reload of all mod types.
Currently supports:
- Ink Story mods (.ink files → runtime compilation → world regeneration)
- UI mods (JavaScript/TypeScript → OneJS ScriptEngine eval)
Future:
- Prefab mods (Addressable prefab overrides)
- Script mods (C# bot scripts)
Folder structure: persistentDataPath/Mods/GrindFest/Story/ — base story mod (smart-copied from StreamingAssets) persistentDataPath/Mods/[ModName]/Story/ — additional story mods persistentDataPath/Mods/[ModName]/UI/dist/index.js — UI mod entry point (eval'd after app.js) persistentDataPath/Mods/[ModName]/Prefabs/ — .prefab DSL files defining custom prefabs (resolved by InkWorldGenerator) persistentDataPath/Mods/[ModName]/Scripts/ — (future) script mods
In Editor, can optionally use Assets/_GrindFest/Story/ directly (toggle via UseEditorStoryFiles).
- ModScriptCompiler
Compiles C# scripts from mods into in-memory assemblies, with security scanning.
Security model:
- Parse all .cs files into Roslyn syntax trees.
- Walk the syntax trees looking for dangerous API usage (not string matching — avoids false positives from comments and string literals).
- If dangerous APIs found and mod is not trusted → block compilation.
- Trust is hash-based: if scripts change, trust is revoked.
- First-party mods (author "GrindFest Team") are always trusted.
- MovementTests
Movement tests — verifying hero can walk, navigate, and reach destinations.
- MusicalInstrumentBehaviour
Provides MIDI instrument functionality for playable musical items.
- MyPreciousAchievement
My Precious - Hold 1 million gold at once. Dragon cosplay.
- NamespaceRewriter
A syntax rewriter that transforms all references to GrindFest namespace to GrindFest.Isolated namespace.
- NativeFileWatcher
Drop-in replacement for FileSystemWatcher that avoids Mono's
DefaultWatcherpolling (which allocates ~300 KB/frame of GC).On Windows: Uses
ReadDirectoryChangesW(event-based, zero-GC steady state). On other platforms: Falls back to periodic polling with controlled interval.Events fire on a background thread, same as FileSystemWatcher.
- ObservableList<T>
A minimal observable wrapper around List<T>. Fires Changed on every mutation (Add, Remove, Clear, indexer set). Zero-allocation event — uses
Actionwith no args.Designed for OneJS bridge: JS subscribes via
onejs.subscribe(list, 'Changed', cb).Unity-serializable: the inner List is serialized by Unity.
- OneJSLiveReload
Replaces OneJS Runner's polling-based live reload with NativeFileWatcher. Attach to the same GameObject as Runner (ScriptEngine). Disables Runner.liveReload on Start and uses OS-native file change notifications instead.
- OverworldMonsterSpawner
Checks the player movement and after few steps spawns a monsterrs
- PageContentBridge
Bridge between BookController and OneJS for rendering book page content. Creates 4 RenderTexture-targeted UIDocuments matching EndlessBook's material slots:
- Left: BookPageLeft (left page in OpenMiddle)
- Right: BookPageRight (right page in OpenMiddle)
- Front: BookPageFront (front of turning page / right side in OpenFront / cover when closed)
- Back: BookPageBack (back of turning page)
- PartyAnimalAchievement
Strength in Numbers - Build your army.
- PersistedAreaBehaviour
Stores all items in this area in save data, and restores them on load. Checks if player has modified/entered area, so it doesn't save unnecessary data.
- PlaytestInfo
Text-based game state for AI playtesting. Call via MCP unity_execute: return GrindFest.PlaytestInfo.GetState();
- PortalViewBehaviour
Renders a see-through portal effect on a Moon Gate. Creates a secondary camera at the destination, renders to a RenderTexture, and projects it onto a wide fan/cone mesh extending from the gate opening toward the hero. The fan direction tracks the hero's position for parallax.
- PourWaterTest
Test: Hero pours water from bucket into cauldron using DropInto (bot API). Setup: Spawn cauldron + bucket with water, give bucket to hero. Action: BotHero.DropInto(bucket, cauldron) — same as player bot script. Verify: Cauldron contains water, total conserved.
- PrestigeAddictAchievement
Prestige Addict - Purchase 100 prestige upgrades. The meta-game IS the game.
- PrestigeNode
Bot-facing snapshot of a single Grimoire (Prestige Tree) node. Returned by GetPrestigeNodes(); bots use this to decide what to inscribe via InscribePrestigeNode(string).
Field semantics:
- Id is the stable identifier (use this when calling
InscribePrestigeNode); Name is the human-facing label and may be re-themed in future updates. - Kind is one of:
"Sigil","Bridge","Aux","Continuation"(and a couple of specials). - Parents is the prerequisite list. The Grimoire only allows inscribing a node when at least one parent is already inscribed (the gateway node has no parents).
- IsInscribed reflects whether this node is currently inscribed on the local party.
- GoldCost / SoulCost / GemCost
/ AchievementPointCost are the per-currency costs to
inscribe (unspecified currencies report
0). Real-money nodes (Patron) report0across the board — they're paid via Steam, not via in-game wallets, and bots cannot purchase them.
- Id is the stable identifier (use this when calling
- PrestigeSystem
Single source of truth for the Grimoire (prestige tree). Holds the static bonus fields the rest of the game reads (LevelCap, HeroSlot, etc.), AND the JS bridge that the OneJS / Preact UI calls into to query state, register the node registry, and inscribe nodes.
History: this used to be split across
PrestigeSystem(fields + dormant legacy shop stubs) andPrestigeTreeBridge(registry + purchases + bot API projection). The split was an artifact of the migration off the old gold shop \u2014 they're now a single class so callers don't have to remember which side owns what.JS callers reach this class via Puerts as
CS.GrindFest.PrestigeSystem.{GetState, TryInscribe, RegisterNodes}.
- PumpkinApocalypseAchievement
Pumpkin Apocalypse - Smash 666 pumpkins. The Great Pumpkin weeps.
- PumpkinLeaderboardHUD
Real-time leaderboard HUD display for pumpkin smashing. Shows: Top N entries, and Your Position (if not in top N). Supports both IMGUI (legacy) and TextMeshPro (zero-allocation) rendering. Requires Steam integration - automatically disabled when DISABLESTEAMWORKS is defined.
- RangedAttack
Ranged attack skill for bows and crossbows. Two modes through skill system:
- If no arrow nocked: plays nock animation (allows movement), spawns arrow
- If arrow nocked: shoots the arrow
- ReagentRecipe
Alchemy recipe that matches reagents by their Category field. Carries conversion parameters for gradual liquid transformation while stirring.
- ShortScale
Parse a numeric value into a short scale string representation.
- SkipTestAttribute
Skip a test class or individual test method during DevTestRunner discovery. Skipped tests are logged but not executed.
- StackExtensions
Extension methods for Stack to support Remove operation
- StashTests
Tests for stash / chest container interactions. Verifies items can be placed in, retrieved from, and properly tracked in container-based storage (InventoryController + InventoryContainerBehaviour).
- StatJunkieAchievement
Stat Junkie - Allocate 10,000 stat points. Min-maxing taken to its logical extreme.
- SteamWorkshopService
Steam Workshop integration for GrindFest mods. Handles publishing, querying, subscribing, and installing Workshop items.
Uses Facepunch.Steamworks UGC API.
Well-known content folders included when publishing: UI/ (full source + dist/), Scripts/, Story/, Assets/, mod.json, preview.*
Excluded directories: node_modules, .git, .vs, .vscode, .idea, obj, bin
- SteamWorkshopService.WorkshopMeta
Auto-generated metadata stored alongside the mod. Keeps mod.json clean (author-edited only).
- TalkativeBotAchievement
Hello World - Your bot said 10,000 things. Does it ever shut up?
- TestCharacterReactive
Test component to verify OneJS EventfulProperty works with game properties NOTE: [EventfulProperty] must be on FIELDS, not properties! Source generator creates public properties automatically (Strength, Dexterity, etc.)
- TimePlayedAchievement
Time is Just a Number - Total play time. Your bot doesn't need sleep.
- TorchBehaviour
Handles torch-specific behavior when equipped/unequipped. Uses BurnableBehaviour for fire VFX management.
- TotalPartyKillAchievement
Total Party Kill - Wipe your entire party 100 times. Teamwork makes the dream die.
- TouchedByDeathAchievement
Touched by Death - Die again. And again. And again.
- VBCompiler
Visual Basic compiler that inherits from the base Compiler class. Does not support top-level scripts or isolated execution.
- VSCodeManager
Manages VSCode extraction from StreamingAssets zip file with version checking.
The bundled zip is the official VS Code archive downloaded from Microsoft: https://update.code.visualstudio.com/latest/win32-x64-archive/stable No customizations are applied — it's the vanilla VS Code zip. To update it, use the Unity editor menu: GrindFest > VS Code Bundle > Download Latest.
Platform support:
- Windows: VSCode-win32-x64.zip → extracts → Code.exe (fully working)
- Linux: VSCode-linux-x64.tar.gz → needs tar extraction → ./code (TODO: implement)
The extracted folder lives next to the zip in builds (StreamingAssets/). deploy_grindfest.bat cleans up the extracted folder before Steam upload so only the zip ships with the game.
- WorldGeneratorTests
Tests for InkWorldGenerator + ContentParser. TODO: Re-implement — previous tests had issues with inactive renderers and missing MapMarker components.
Structs
- ActionResult
Result of a bot action like PickUp, Equip, DropInto, Attack, or InteractWith.
Three core states:
- Failed — The action could not be performed (item is null, out of range, etc.)
- InProgress — The hero is working on it (walking to target, animating, etc.)
- Done — The action completed this frame
Implicitly converts to
bool:trueif InProgress or Done,falseif Failed. This makes it easy to use in priority loops:if (PickUp(item)) return; // hero is busy picking up — don't start another actionFor precise checks, compare with
==:var result = PickUp(item); if (result == ActionResult.Done) Say("Got it!"); if (result == ActionResult.InProgress) Say("Walking there...");
- ModScriptCompiler.CompileResult
Result of compiling a single mod's scripts.
- ModScriptCompiler.SecurityFinding
Represents a single security finding from the syntax tree scan.
- ShortScaleData
Allows for the value to be separated from the symbol; both can be styled separately.
Interfaces
- ICloseableUI
Interface for UI elements that can be closed with ESC
Enums
- ModContentFlags
Flags indicating which well-known content folders a mod contains.