ShrinkingZone
A closing perimeter that shrinks over time, damaging players caught outside it based on their max health. Sound effects are available but OFF by default following reports of motion sickness.
By Skler
| Date uploaded | 2 weeks ago |
| Version | 1.0.4 |
| Download link | Skler-ShrinkingZone-1.0.4.zip |
| Downloads | 299 |
| Dependency string | Skler-ShrinkingZone-1.0.4 |
This mod requires the following mods to function
willis81808-UnboundLib
This is a helpful utility for ROUNDS modders aimed at simplifying common tasks.
Preferred version: 3.2.14README
ShrinkingZone — ROUNDS Mod
A closing "safe zone" for ROUNDS: a circular perimeter that shrinks over time, damaging any player caught outside it (battle-royale ring style).
Files
ShrinkingZonePlugin.cs— BepInEx plugin entry point, hooks round start/end.ZoneController.cs— spawns the ring visual, shrinks it, and damages players outside it.ShrinkingZoneMod.csproj— build project; references the 4 game DLLs needed to compile.manifest.json— Thunderstore package manifest.
Requirements
- BepInEx (5.4.x) installed for ROUNDS
- UnboundLib — round lifecycle hooks
- (Optional) ModdingUtils — extra player/health helpers if you want cleaner damage calls
- Visual Studio / Rider / VS Code with the .NET SDK
- References to
Assembly-CSharp.dll,UnityEngine.dll,BepInEx.dll, andUnboundLib.dllfrom your ROUNDS install (ROUNDS/BepInEx/plugins/andROUNDS/ROUNDS_Data/Managed/)
Verified against Assembly-CSharp.dll / UnboundLib.dll via dnSpy
Every field/method/attribute name below has been confirmed against the real game and library assemblies — no more guessing left in this mod. Kept here as a reference in case you update BepInEx/UnboundLib/the game later and need to re-check anything that might have changed.
How to enumerate active players✅ Confirmed:PlayerManager.instance.playersis apublic List<Player> players. Code already matches this exactly.How to apply damage, and how to read max health✅ Confirmed:Player.datais aCharacterDatawith plain public floatshealthandmaxHealth.- Damage is dealt through a
DamagableEvent(subclass of abstractDamagable) sitting on the player's GameObject — accessed viaplayer.GetComponent<DamagableEvent>()since no cached field was found. - Real signature:
TakeDamage(Vector2 damage, Vector2 damagePosition, GameObject damagingWeapon = null, Player damagingPlayer = null, bool lethal = true, bool ignoreBlock = false). Notedamageis a Vector2 (magnitude = amount, direction = knockback), not a float —ZoneController.ApplyZoneDamagenow builds this vector pointing back toward the zone center.
Round start/end hook names✅ Confirmed:GameModeHooksis apublic static classholding plainconst stringidentifiers —HookRoundStart = "RoundStart"andHookRoundEnd = "RoundEnd"— exactly matching whatShrinkingZonePlugin.csalready uses.
All three items above are confirmed — see the "Multiplayer / networking" section below for the remaining (now also resolved) networking-specific checks.
Tuning
All the knobs are public static fields at the top of ShrinkingZonePlugin:
StartRadius = 30f;
FinalRadius = 3f;
ShrinkStartDelay = 5f;
ShrinkDuration = 25f;
DamagePercentPerSecond = 0.05f; // 5% of the player's MAX health per second
DamageTickInterval = 0.5f;
Damage scales off each player's max health rather than a flat number, so the zone hurts proportionally regardless of health-boosting cards a player has picked up. At the defaults, a player fully outside the zone loses 5% of their max health every second (i.e. dies in ~20s of standing outside it).
Multiplayer / networking
The zone is now host-authoritative:
- Only the master client (
PhotonNetwork.IsMasterClient) advances the shrink timer and computes the "true" radius. - Every
SyncInterval(default 0.2s) the host broadcasts(center, radius, timer)to all clients viaNetworkingManager.RPC(...), landing on the static[UnboundRPC] RPCA_SyncZoneStatemethod on every machine. - Non-host clients never compute their own radius — they just render whatever the last synced value was. This is what keeps everyone's ring visually identical instead of drifting apart from frame-rate/timing differences.
- Damage is still applied locally per-client, but only for players that
client actually owns (
PhotonView.IsMine), to avoid multiple machines applying the same damage tick to the same player.
Things to verify for the networking layer specifically
✅ Confirmed:NetworkingManager.RPCsignature
Code updated to passpublic static void RPC(Type targetType, string methodName, RaiseEventOptions options, SendOptions sendOptions, params object[] data)RaiseEventOptions.DefaultandSendOptions.SendReliablebefore the trailing float args. Addedusing ExitGames.Client.Photon;andusing Photon.Realtime;for these types (standard PUN2 namespaces) — if the build can't find them, recheck their actual namespace in dnSpy.✅ Confirmed:[UnboundRPC]public class UnboundRPC : Attributein namespaceUnboundLib.Networking— already covered by the existingusing UnboundLib.Networking;at the top ofZoneController.cs. No changes needed.✅ UsingPhotonViewonPlayerplayer.GetComponent<PhotonView>()— no cached field was found, so this resolves it the same way asDamagableEvent.- Does
HookRoundStartfire on all clients or just host? — the one item we haven't explicitly confirmed. If it turns out to be host-only in your UnboundLib version, theSpawnZonecall inShrinkingZonePlugin.OnRoundStartneeds to be wrapped in its own RPC so every client creates a localZoneControllerto receive syncs. Easiest way to check: just build and test in a 2-player session — if the non-host player never sees the ring at all, this is why.
Everything else in this mod has now been verified against the real assemblies via dnSpy — the pattern (host computes truth, broadcasts on an interval, clients render/apply damage locally) is standard for ROUNDS mods, and the exact method/field names are locked in.
Building
-
Open
ShrinkingZoneMod.csprojand edit the<GamePath>property near the top to point at your actual ROUNDS install folder, e.g.:<GamePath>C:\Program Files (x86)\Steam\steamapps\common\ROUNDS</GamePath>Everything else (Managed folder, plugins folder) is derived from this.
-
Double-check the reference filenames actually match what's on disk — open your
ROUNDS_Data\ManagedandBepInEx\pluginsfolders and confirm:Photon3Unity3D.dll,PhotonUnityNetworking.dll,PhotonRealtime.dll, andUnityEngine.CoreModule.dllexist with those exact names (Unity/Photon DLL naming varies a bit between versions — if a name doesn't match, update the<HintPath>in the.csprojaccordingly).- The
UnboundLibsubfolder name underBepInEx\plugins\matches what's in the.csproj(Willis81808-UnboundLibis a guess based on common naming — yours may differ, especially if you installed via a mod manager that names folders differently).
-
Build:
dotnet build -
Copy the resulting
ShrinkingZoneMod.dll(inbin/Debug/netstandard2.1/) intoROUNDS/BepInEx/plugins/. -
Launch the game and check the BepInEx console window (should pop up alongside the game if console logging is enabled in your BepInEx config) — any reference/load errors will show up there immediately.
Packaging for Thunderstore
Zip manifest.json, an icon (icon.png, 256x256), a README.md, and the built
.dll together, then upload via the Thunderstore package manager.