You are viewing a potentially older version of this package. View all versions.
Largo-POG_Config-1.0.8 icon

POG Config

In-game MOD SETTINGS panel for Pit of Goblin mods. Provides toggles, sliders with numeric input, options lists, and keybind entries. Dependency for other POG mods.

Date uploaded a day ago
Version 1.0.8
Download link Largo-POG_Config-1.0.8.zip
Downloads 5
Dependency string Largo-POG_Config-1.0.8

This mod requires the following mods to function

LavaGang-MelonLoader-0.7.2 icon
LavaGang-MelonLoader

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

Preferred version: 0.7.2

README

POGConfig

In-game config UI framework for Pit of Goblin mods.

POGConfig provides a shared MOD SETTINGS panel that any mod can register entries into. It handles rendering, persistence, scrolling, and input — mod authors only write entry declarations.

Install: Thunderstore Mod Manager / r2modman, or place POGConfig.dll in the game Mods folder. Mods that depend on POGConfig should load after it (MelonLoader respects alphabetical order by default).

Source & Wiki: github.com/LargoScript/POGConfig — full developer Wiki at /Wiki.


Features

  • MODS button injected into both the main menu and pause menu.
  • Scrollable panel — mouse wheel and clickable scrollbar. Scrollbar appears only when content overflows.
  • Four entry types: toggle, slider (with inline numeric text input), options list, keybind.
  • MelonPreferences persistence — per-entry opt-in, auto-saved on change.
  • Hotkey suppressionPOGConfig.PanelOpen lets other mods pause their hotkeys while the panel is open.
  • Marquee animation — label and value text scroll on hover when they overflow their column.
  • Bidirectional slider fill — fill bar grows from a configurable origin point, not just the left edge.
  • Step tick marks — optional evenly-spaced markers on a slider.

For Developers

Click To Expand

Project reference

Add to your .csproj:

<Reference Include="POGConfig">
  <HintPath>..\..\Mods\POGConfig.dll</HintPath>
  <Private>false</Private>
</Reference>

Add using POGMods.Config; to your plugin file.


Registration pattern

Wrap registration in a [MethodImpl(MethodImplOptions.NoInlining)] helper so the IL2Cpp JIT only resolves POGConfig types when the method is actually called. This makes POGConfig an optional dependency — your mod loads cleanly even if POGConfig is absent.

using System.Collections.Generic;
using System.Runtime.CompilerServices;
using POGMods.Config;

public override void OnInitializeMelon()
{
    try { TryRegisterConfig(); } catch { }
    MelonLogger.Msg("My Mod loaded.");
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void TryRegisterConfig()
{
    POGConfig.Register("My Mod", new List<ConfigEntry>
    {
        new ToggleEntry("Enable Feature", () => _enabled, v => _enabled = v),
        new SliderEntry("Speed",          () => _speed,   v => _speed   = v, 0f, 10f),
        new KeyEntry("Hotkey",            () => _key,     v => _key     = v),
    });
}

Register also creates a MelonPreferences category named after your mod (spaces → underscores). Entries with a prefKey load and save automatically within that category.


POGConfig.PanelOpen

public static bool PanelOpen { get; }

true while the MOD SETTINGS panel is visible. Check it before processing any in-game hotkey so keypresses inside the panel don't trigger game actions:

void Update()
{
    if (!POGConfig.PanelOpen && Input.GetKeyDown(_toggleKey))
        Toggle();
}

Entry types

ToggleEntry

Renders as a 40×24 yellow checkbox on the right side of the row.

new ToggleEntry(string label, Func<bool> get, Action<bool> set)
new ToggleEntry(string label, Func<bool> get, Action<bool> set, string prefKey)
  • The toggle calls set immediately when the user clicks it.
  • OnUpdate polls get() every frame and silently syncs the visual state if the value changed externally.
  • prefKey auto-saves to MelonPreferences on every change.
new ToggleEntry("God Mode", () => _god, v => _god = v, "GodMode")

SliderEntry

Row layout: 26 % label | 26 % value input | 48 % slider

Label and value columns clip with RectMask2D and animate a marquee on hover when the text overflows.

Full constructor (all parameters after max are optional):

new SliderEntry(
    string label,
    Func<float> get,
    Action<float> set,
    float min,
    float max,
    Func<float, string> fmt = null,   // display formatter; default: v => v.ToString("F1")
    string prefKey          = null,   // MelonPreferences key; null = no persistence
    float  originValue      = 0f,     // where the yellow fill bar anchors from
    bool   showFill         = true,   // false hides the fill bar entirely
    bool   wholeNumbers     = false,  // true snaps the handle to integers
    int    stepPointsCount  = 0)      // ≥2 draws evenly-spaced tick dots along the track

Value input field — click the yellow number box to type a value directly; Enter confirms, Escape cancels, clicking outside also confirms. The formatter suffix (e.g. "s", "%") is stripped on parse. A regex extracts the first numeric token, so "43s" and "43 seconds" both parse as 43. Enter confirms, Escape cancels. Values are clamped to [min, max].

Bidirectional fill — the yellow bar spans from originValue to the current value. If the current value is below origin it grows left; above origin it grows right. Set showFill: false to hide it entirely (recommended for wholeNumbers step sliders).

Step tick marksstepPointsCount draws that many 4×4 px dots evenly from left to right edge of the track. They are purely visual; combine with wholeNumbers: true to make the handle snap between them.

Column widths — by default the row is split 35 % label / 15 % value box / 50 % slider. Override per-entry with object initializer syntax:

new SliderEntry("Speed", () => spd, v => spd = v, 0f, 10f)
    { LabelFraction = 0.45f, ValueFraction = 0.10f }

The slider takes whatever fraction remains after label + value + a 2 % gap.

Examples:

// Simple, 0–100 %, no persistence
new SliderEntry("Volume", () => vol, v => vol = v,
    0f, 1f, v => $"{v * 100:F0}%")

// Bidirectional fill from 0, persisted
new SliderEntry("Temperature", () => temp, v => temp = v,
    -50f, 50f, v => $"{v:F1}°C",
    "Temp", originValue: 0f)

// Integer steps 0–8 with 9 tick dots, no fill, persisted
new SliderEntry("Points", () => (float)pts, v => pts = (int)v,
    0f, 8f, v => $"{v:F0}",
    "Points", originValue: 0f, showFill: false, wholeNumbers: true, stepPointsCount: 9)

// Numeric suffix stripped on parse ("43s" → 43)
new SliderEntry("Backup Interval", () => sec, v => sec = v,
    5f, 180f, v => $"{v:F0}s", "BackupSec", originValue: 5f)

OptionsSliderEntry

A slider that snaps to integer indices and shows a string label for each position. No fill bar. Suitable for named modes (difficulty, quality preset, etc.).

new OptionsSliderEntry(
    string   label,
    Func<int> get,
    Action<int> set,
    string[] options,
    string   prefKey = null)
  • options is the full list of display strings, index 0 = leftmost.
  • The slider's wholeNumbers is forced true and maxValue = options.Length - 1.
  • OnUpdate polls get() and syncs the visual state if the index changed externally.
  • prefKey saves/loads the integer index.
new OptionsSliderEntry(
    "Difficulty",
    () => _difficulty,
    v  => _difficulty = v,
    new[] { "Easy", "Normal", "Hard" },
    "Difficulty")

KeyEntry

Row layout: 42 % label | 28 % current key name | 90 px "Change" button

new KeyEntry(string label, Func<KeyCode> get, Action<KeyCode> set)
new KeyEntry(string label, Func<KeyCode> get, Action<KeyCode> set, string prefKey)
  • Clicking Change enters listen mode. The next Input.GetKeyDown press is bound. Press Escape to cancel without changing the binding.
  • Clicking Clear removes the binding — sets the key to KeyCode.None, displayed as — none —. Useful for making a hotkey optional.
  • Only one KeyEntry can be in listen mode at a time (KeyEntry.AnyWaiting flag).
  • prefKey saves/loads the key name as a string via Enum.Parse<KeyCode>. KeyCode.None is stored as "None" and loaded correctly.

AllowMouseButtons — by default Mouse0Mouse6 are excluded from listen mode (prevents accidentally binding a mouse click). Opt in per-entry:

new KeyEntry("Attack", () => _key, v => _key = v) { AllowMouseButtons = true }

Persistence details

MelonPreferences categories are created as modName.Replace(" ", "_") automatically inside Register. You do not manage the category yourself. Each entry with a non-null prefKey:

Entry type Stored as Loaded via
ToggleEntry bool direct assignment
SliderEntry float clamped to [min, max], rounded if wholeNumbers
OptionsSliderEntry int clamped to [0, options.Length - 1]
KeyEntry string (key name) Enum.TryParse<KeyCode>

Saving happens on every value change (inside the wrapped set callback). The preferences file is written via MelonPreferences.Save().


Runtime behavior notes

  • The ConfigBehaviour MonoBehaviour runs on a DontDestroyOnLoad runner object with HideFlags.HideAndDontSave. The UGUI Canvas lives on a separate root DontDestroyOnLoad object — these must not share the same parent to avoid the canvas being hidden by HideAndDontSave.
  • BuildStaticUI is called from Start and retried from Update on failure. _uiReady = false is set on exception, allowing recovery the next frame.
  • Content rows are built lazily in EnsureContent the first time the panel is opened. If POGConfig.RegistryVersion has changed since the last build (a mod registered after the first open), content is destroyed and rebuilt.
  • Click detection uses RectTransformUtility.RectangleContainsScreenPoint(rt, point, null) — the null camera argument is required for ScreenSpaceOverlay canvas, because GetWorldCorners returns canvas-centered world coordinates while Input.mousePosition is bottom-left screen pixels.
  • Sliders and toggles use Unity's EventSystem pipeline (drag, click). POGConfig creates a POG_EventSystem with StandaloneInputModule if no EventSystem exists in the scene.
  • The scrollbar is manually implemented (no ScrollRect). ScrollContent.anchoredPosition.y = _scrollOffset drives the position; RectMask2D on the viewport clips overflow. The thumb height is VIEWPORT_H² / totalContentH, clamped to a 30 px minimum.
  • NetworkMenu.TogglePauseMenu is Harmony-patched: if the panel is open when the game tries to close the pause menu, the patch closes the panel instead and returns false to suppress the original call.

Extending with a custom entry type

Subclass ConfigEntry and override the internal methods:

public class MyEntry : ConfigEntry
{
    public MyEntry(string label) : base(label) { }

    // Build UGUI children inside the provided row GameObject.
    // Row is 40 px tall, full viewport width.
    // Use Clicks.Register(btnRt, callback) for clickable buttons.
    internal override void BuildRowInto(GameObject row, TMP_FontAsset font) { ... }

    // Called every frame while the panel is open.
    internal override void OnUpdate() { ... }

    // Called once during Register() to wire up MelonPreferences.
    internal override void BindPrefs(MelonPreferences_Category cat) { ... }
}

CHANGELOG

Changelog

1.0.9

Native config window. The MOD SETTINGS panel is now a real injected NetworkMenuState that the game switches to via NetworkMenu.SetState, the same mechanism used for its own Settings/Credits/etc. screens. The game hides the underlying menu on its own and treats the panel as the current screen instead of an overlay drawn on top of it, so Esc/cancel behaves like a native screen transition rather than two separate systems racing each other. Falls back automatically to the previous plain-overlay behavior if the injected state can't be registered.

Keybind rework. KeyEntry supports up to 3 universal slots per action — any slot accepts a keyboard key or a mouse button (KeyCode.Mouse0Mouse6), no fixed keyboard/mouse split. One slot by default; a mod opts into more via the new constructor KeyEntry(label, get(slot), set(slot, value), prefKey, slotCount). Slots sit right-aligned in a fixed 3-column grid, each with an inline clear button. The old single-key constructors still work exactly as before (source- and binary-compatible with 1.0.x mods). Slot 1 persists under the same prefKey as before, slots 2/3 under prefKey_2/prefKey_3. Also fixed: starting a rebind by clicking a slot could immediately capture that same mouse click as the bound button.

Vanilla Binds tab. A new "VANILLA BINDS" entry at the top of the sidebar shows the game's own controls — every user-assignable action with its keyboard (up to 2) and mouse (1) binds, read live from Rewired each time the tab opens. Binds are editable: click a slot, press a key (keyboard columns) or a mouse button (mouse column), and the change is written straight into the vanilla input system via the same Rewired API the game's Controls screen uses, then saved and verified by re-reading — the row always ends up showing the real state, and a warning is logged if an assignment did not stick. Keyboard/mouse column typing matches how the game itself stores binds, so nothing can be assigned here that the vanilla screen couldn't display.

Restore Defaults. Every mod page now has a RESTORE DEFAULTS button at the bottom that resets all of that mod's settings to the values the mod registered with (captured before saved preferences were applied), and persists the reset.

Pause menu & UI fixes.

  • The pause-menu MODS button is now a real NetworkMenuButton injected at the top of the pause button column — native yellow selector, hover, click sound and gamepad navigation, and the column layout spaces it automatically (same approach as the main menu since 1.0.7). The old floating overlay button remains only as a fallback if the injection target is not found.
  • Fixed: pressing Esc to close the MOD SETTINGS panel in the pause menu no longer also closes the pause menu itself with the same key press.
  • Toggle checkboxes are now square (22×22) instead of rectangular, and the yellow checkmark is drawn as a centred square so it no longer looks unevenly inset at fractional UI scales.

1.0.8

  • Fixed: hovering the MODS button in the main menu now shows the selector and yellow highlight exactly like the native buttons. The cloned button is now instantiated directly under the Buttons Panel so it resolves its internal NetworkMenu reference; the old manual hover workaround was removed.
  • Fixed: pressing Esc to cancel a search edit, a slider value edit, or a key-binding capture no longer also closes the whole panel with the same key press.
  • Added: Esc now cancels key-binding capture in KeyEntry (previously only the Cancel button worked).
  • Fixed: the sidebar mod list snaps back to the top when it is rebuilt (e.g. while typing a search query after scrolling down).

1.0.7

  • The MODS button in the main menu is now a real NetworkMenuButton injected into the in-game Buttons Panel (between START and SETTINGS) instead of a floating overlay. It matches the game's button style, sound, and selection visuals.
  • The pause-menu MODS button keeps the floating-overlay approach for now.
  • Vertical scrollbar fixed: the thumb is now draggable on both the mod list (sidebar) and the settings list (content), and clicking on the empty track centres the thumb on the cursor and starts a drag from that point. Mouse wheel now scrolls only the section the cursor is over.
  • Switching between mods in the sidebar resets the content scroll position back to the top.
  • Mouse and keyboard input on the underlying main/pause menu is suppressed while the config panel is open (Enter no longer triggers main-menu buttons by accident).
  • Pressing Esc now closes the config panel.

1.0.6

  • Added a full developer Wiki for POGConfig with entry-by-entry docs, architecture notes, runtime behavior details, persistence guidance, and troubleshooting pages.
  • Included CHANGELOG.md in the package build to ensure release notes are visible on Thunderstore.

1.0.3

  • SliderEntry now exposes LabelFraction and ValueFraction properties so mod authors can override the default column widths (35 % / 15 % / 50 %) via object initializer syntax.
  • KeyEntry now shows a Clear button to remove the keybind (KeyCode.None). Unbound keys display as — none —.

1.0.1

  • Added OptionsSliderEntry for discrete string option lists.
  • Added KeyEntry.AllowMouseButtons opt-in property to allow mouse button binding.
  • Added POGConfig.PanelOpen public property; other mods can suppress hotkeys while the panel is open.
  • Added vertical scrollbar; scroll wheel supported.
  • Added marquee text animation for long labels and value strings (hover to scroll).

1.0.0

  • Initial release: in-game settings UI framework for Pit of Goblin mods.
  • ToggleEntry, SliderEntry, KeyEntry.
  • MelonPreferences persistence via prefKey.