You are viewing a potentially older version of this package.
View all versions.
BallSaboteur
Adds a new lock-on sabotage item that turns a target player's ball into a cube until they complete a qualifying stroke.
| Date uploaded | 2 months ago |
| Version | 0.1.2 |
| Download link | Cray-BallSaboteur-0.1.2.zip |
| Downloads | 26 |
| Dependency string | Cray-BallSaboteur-0.1.2 |
This mod requires the following mods to function
BepInEx-BepInExPack
BepInEx pack for Mono Unity games. Preconfigured and ready to use.
Preferred version: 5.4.2305README
BallSaboteur
Adds a new runtime-registered sabotage item to Super Battle Golf.
The item reuses the Orbital Laser pickup and targeting flow for testing, but it does not overwrite the vanilla Orbital Laser item. When used on another player, their ball becomes a cube until they take a sufficiently long stroke and the ball comes fully to rest, at which point it returns to a sphere.
Current prototype behavior:
- Registers a new runtime
ItemTypeand injects matchingItemData. - Reuses Orbital Laser targeting so it locks on to players the same way.
- Randomly converts a share of Orbital Laser spawns into Ball Saboteur for testing.
- Includes an optional
F8debug grant hotkey for fast iteration.
CHANGELOG
Changelog
0.2.1
- Fix the pause menu failing to initialise (and Esc not opening it) when another mod also
injects a custom
ItemTypevia the same ItemCollection extension trick. Both mods'Countpostfixes correctly added 1, but each mod'sGetItemAtIndexprefix only handledindex == items.Length— so the second synthetic slot fell through to vanilla and threwIndexOutOfRangeExceptionfromPauseMenu.Awake. The lookup is now multi-mod-aware: it resolves the synthetic index against the sorted list of all >= 1000 custom ItemTypes inallItemData, so each mod's prefix only claims the slot that maps to its own type.
0.2.0
- Fix the inventory hotkey label showing "Global Strike" while Ball Saboteur is equipped. The
hotkey UI looked up the localized name through
GetEffectivelyEquippedItem, which our remap redirected to Orbital Laser. A targetedHotkeyUi.SetNameprefix now substitutes ourLocalizedNamewhenever the local player's actual equipped slot is the saboteur item. - Fix the lock-on reticle flickering while the saboteur is equipped. The vanilla
OnBUpdateswitch keys on the unmapped slot type and was nulling the lock-on target every frame; our postfix immediately re-set it, producing a per-frame null→target transition. Replaced with aPrefixthat skips the vanilla switch and runs the OL targeting path once per frame. - Fix the saboteur item not disappearing from the inventory after activation. The use flow now
calls
DecrementUseFromSlotAt(mirroring the local override and updating the hotkey icon) followed byRemoveIfOutOfUses, matching the vanilla Orbital Laser activation routine. - Tint the dropped pickup model so it's visually distinct from the vanilla Orbital Laser.
Configurable via
Visuals.TintPickupModelandVisuals.TintPickupColorHex(default magenta).
0.1.3
- Fix a main-thread freeze when loading into the Driving Range (and any host-local lobby). v0.1.2
injected the runtime item into
ItemPool.spawnChances, which causedMatchSetupRules.Updateto iterate a pool containing ourItemType 1001and index pastitemOrderLookup. The pool mutation path (Patch_ItemSpawnerSettings_ResetRuntimeDataandPatch_MatchSetupRules_Update) is removed in favor of a postfix onItemSpawnerSettings.GetRandomItemForthat re-rolls the result into the custom item at a configured chance per pool. - Fix item name showing as "Global Strike" in the pickup prompt and inventory.
ItemData.nameis a privateLocalizedStringthatMemberwiseClonecopies verbatim; the clone now nulls it so the getter lazy-re-resolves againstITEM_1001. - Fix the runtime item appearing with no in-hand visual. Added a postfix on
PlayerInventory.GetEffectivelyEquippedItemthat remaps the customItemTypeback toOrbitalLaserfor consumers that drive the hand-equipment switch, lock-on validation, and aim reticle. The actual use-path still readsGetEffectiveSlotdirectly so sabotage fires. - Fix the item missing from the pause-menu item-probability grid. Restored the targeted
ItemCollection.get_CountandItemCollection.GetItemAtIndexpatches so the grid'sfor (int i = 0; i < AllItems.Count; i++)loop sees the custom slot atitems.Length. - Make the pause-menu percentage reflect the configured spawn chance. Postfixes on
MatchSetupRules.GetWeightandGetItemPoolTotalWeightinject a virtual weightw = p · T / (1 − p)for the custom item soweight / totalWeightdisplays as the configured percent without perturbing vanilla items' true shares after the re-roll. - Fix the dropped
PhysicalItemNRE'ing inCourseManager.ServerSpawnItemand disappearing. The runtime pickup template was stored withSetActive(false), so clones inheritedactiveSelf=falseandPhysicalItem.Awakenever ran, leavingAsEntitynull. The template is now parented under an inactive root GameObject — instances spawned at world root wake up normally. - Fix remote players crashing when they pick up (or receive the
SpawnMessagefor) the dropped item. The runtime clone carried Orbital Laser'sNetworkIdentity.assetIdand wasn't in Mirror's prefab registry. Clear the inherited id via reflection, assign a deterministic id derived fromModGuid + ":pickup:" + CustomItemTypeRaw(FNV-1a), and register it withNetworkClient.RegisterPrefab(prefab, assetId). Re-register from the existingNetworkClient.RegisterMessageHandlerspostfix because Mirror clearsNetworkClient.prefabsinClearSpawners()on shutdown. - Defer
TryRegisterCustomLocalizationEntryfromItemCollection.OnEnableto the firstLocalizedString.GetLocalizedStringcall. TouchingLocalizationSettings.StringDatabaseduringOnEnableblocks on async localization init and deadlocks the main thread at game load. - Broaden the
LocalizedString.GetLocalizedStringfallback rewrite to match any result containingITEM_1001, not just the exact"Data/ITEM_1001"form, since Unity's missing-entry fallback varies by configuration.
0.1.2
- Fix the match-setup and item-probability UI paths so the runtime-added Ball Saboteur item no longer indexes past vanilla item lookup arrays. This removes the launch/lobby blocker while preserving the custom spawn-pool injection and sabotage behavior.
- Add the missing
UnityEngine.UIproject reference required by the guarded match-setup refresh path.
0.1.1
- Fix client disconnect when the server broadcasted ball-state messages: Mirror clears message
handlers on shutdown, so re-register via a
NetworkClient.RegisterMessageHandlerspostfix and useReplaceHandlerto remain idempotent across reconnects. - Add
Debug.EnableSelfSabotageHotkey(F9) for single-player verification: while hosting locally, pressing F9 sabotages the local player's own ball, bypassing the self-target guard.
0.1.0
- Initial Ball Saboteur prototype.