
VeryBalancedRoundsMod
Sixty-eight aggressively unbalanced cards, Legendary tier included: Icon of Sin, Atomic Bomb, Black Hole, Tree, Railgun, Xane's Wire Management, Go Blue, Claude's Choice and more. Very balanced. No notes.Very Balanced Rounds Mod
Custom cards for ROUNDS, built on UnboundLib, with ModdingUtils for deck surgery and RarityLib for the Legendary tier.
Cards
| Card | Rarity | Effect |
|---|---|---|
| Glasser Cannon | Rare | +200% damage, -75% health |
| McDonalds | Common | +400% health, -50% movement speed |
| Gremlin | Common | -30% size, +50% movement speed |
| Whipped | Common | +100% movement speed, -20% health |
| Density | Common | +70% health, -40% size |
| Leg Day | Common | +100% jump height, -0.5s block cooldown |
| Fraud | Uncommon | -30% size, -50% block cooldown |
| Super Quick Reload | Uncommon | -90% reload time, -20% damage |
| Lowe's Worker | Uncommon | -25% damage, heavy bullet drop, +150% damage to enemies below you |
| Invisible Bullets | Uncommon | Your bullets do not render |
| MTG Player | Uncommon | Touching you deals 50% of your bullet damage |
| Giga Drum Mag | Rare | Magazine maxed out, reload 20x longer |
| Sword | Rare | +777% damage, +75% movement speed, very short bullet range |
| Card Counter | Rare | +15% damage per card held, +0.5s reload |
| Titan Killer | Rare | Bullets deal +5% of the target max health |
| Random Crits | Rare | Every bullet has a 2% chance to instantly kill |
| Bird Killer | Uncommon | Your kills cannot be undone by Phoenix |
| Inevitable | Rare | +5% damage per second, compounding, resets each point |
| Rev Up | Rare | -50% attack speed, +5% per second, resets each point |
| Icon of Sin | Legendary | +5%/s damage, health, bullet and move speed; -5%/s reload and cooldown |
| Particle Accelerator | Rare | +50% movement speed per hit dealt, compounding, resets each point |
| Infinity | Rare | Enemy bullets freeze in place when they get close |
| Black Hole | Rare | Nearby players are pulled toward you |
| Tree | Legendary | +5000% health, immune to knockback, cannot move at all (unless you have The Lorax) |
| The Last Breath | Rare | At 20% health: full heal, invincible and doubled, then dead in 5s |
| Chris's Side Project | Legendary | No block cooldown; dead at 7 seconds if the point is not over |
| The Kirk Special | Rare | Every bullet hit explodes the victim 1s later for 2.5x your bullet damage; stacks per pellet |
| Portal Gun | Rare | Blocking teleports you to your cursor; +0.5s block cooldown |
| Prestige | Rare | Destroys your other cards, +100% health, damage and attack speed |
| Touch the Wall | Legendary | Your next 3 picks only offer Legendary cards |
| Conner's Pride | Rare | +100% health, damage and attack speed — in June only |
| Eight Handled Sword Divine General Mahoraga | Legendary | Each hit you take cuts further damage by 10%, compounding |
| Ihateblockbuild | Uncommon | Blocked bullets still deal 25% of their damage |
| Iloveblockbuild | Rare | Every block heals 25% of your max health |
| Balanced | Uncommon | Health and bullet damage both become their average |
| AK | Uncommon | +50% bullet speed, +10 ammo, no bullet gravity, +1s reload |
| Up | Uncommon | Bullet gravity reversed |
| Down | Common | Your gravity and bullet gravity x2 |
| Go Blue | Uncommon | If you die and everyone else dies within a moment, you win |
| Claude's Choice | Rare | A surprise. Deliberately undocumented - read src/Cards/ClaudesChoiceCard.cs only if you want it spoiled |
| Conner Hack Special | Rare | 3.5x damage at point blank on a steep curve; ~1.4x at 20 units, 0.5x across the map |
| Onceler | Uncommon | Hitting a Tree holder kills them instantly (unless they have The Lorax) |
| Banana | Uncommon | Stand still 5s, then heal 10% max health per second |
| Xane's Wire Management | Rare | Every card you hold is swapped for a random one of the same rarity |
| Border Hop | Uncommon | -80% damage from the map edge, -10% movement speed |
| Ronald McDonald | Rare | Bullets deal +25% of your current health, -20% attack speed |
| Hitscan | Rare | +1000% bullet speed, -40% attack speed |
| Railgun | Rare | +50% of bullet speed added as damage, +20% bullet speed, +1.5s reload |
| Discord Mod | Rare | Nearby players pushed away, harder the closer they are |
| Hollow Purple | Legendary | Massive purple bullets, +200% damage, ignore walls and gravity, -80% bullet speed, +60% cooldown, +4s reload |
| Dense Bullets | Uncommon | Much smaller bullets, +60% damage, +0.5s reload |
| The Block Build Special | Legendary | Shooting triggers your block effects (not the shield), -90% health |
| Killdozer | Legendary | Contact damage scaling with your speed and bullet damage, +25% movement speed |
| Shulker | Uncommon | Players you hit are launched upward as if they jumped with 1000% jump height |
| Drone Strike | Rare | Unlimited jumps in midair; picking it also gives you Homing and Explosive Bullet |
| C-Ram | Rare | Attack speed squared (150% becomes 225%), +2s reload; each copy squares again |
| The Island | Uncommon | Lose 2 random cards when picked, -75% block cooldown |
| The Lorax | Rare | Frees a Tree holder to move, immunity to Onceler, -40% health |
| Super Shotgun | Rare | +12 bullets, +9 ammo, -40% attack speed, range a little longer than Buckshot |
| 500 Cigarettes | Rare | +150% damage; toxic clouds heal you instead; lose 5% max health per second |
| Atomic Bomb | Legendary | Bullets per shot equal your max ammo, +50% damage, +10s reload |
| ASU Frat Leader | Rare | Bullets deal +(100 x size ratio)% damage to smaller players, and damage x size ratio to bigger ones |
| The Flavorful Garlic | Uncommon | yumby. Deliberately undocumented - read src/Cards/FlavorfulGarlicCard.cs only if you want it spoiled |
| Projectile Dysfunction | Uncommon | Bullets wander in random, ever-changing directions, +100% damage |
| Rare Artifact | Rare | -20% damage taken from everything |
| Legendary Artifact | Legendary | -40% damage taken from everything |
| Daedalus Stormbow | Legendary | Bullets fall from the top of the map toward your cursor, a little to one side at random, and go through walls |
| Patience | Legendary | Other players can't shoot for the first 3 seconds of each point |
Building
Requires the .NET SDK (8 or newer). From the project root:
dotnet build
That compiles the mod and copies VeryBalancedRoundsMod.dll straight into the
Thunderstore profile it was set up against, so the next launch picks it up.
Useful overrides:
dotnet build -p:DeployToRounds=false # compile only, don't copy
dotnet build -p:ProfileDir="...\profiles\Default" # deploy somewhere else
dotnet build -p:RoundsDir="D:\...\common\ROUNDS" # non-default game install
To produce a Thunderstore-ready zip in dist/:
dotnet build -t:Package
Adding a card
- Copy
src/Cards/GremlinCard.csto a new file and rename the class. - Add its title to
src/Util/CardNames.cs. - Register it in
VeryBalancedPlugin.RegisterCards(). dotnet build, then relaunch ROUNDS.
Things worth knowing before you tune numbers:
SetupCardruns once at game start, not on pickup. The values you assign togunandstatModifiersthere are multipliers over the player's current stats:2fdoubles,0.5fhalves,1fis neutral. Several fields are additive instead —Gun.ammo,Gun.reloadTimeAdd,Gun.percentageDamage,Block.cdAdd,CharacterStatModifiers.regen— and those treat0fas neutral.Gun.destroyBulletAfteris neither: it's a plain override that the game ignores when zero, so the last card to set it wins.Gun.attackSpeedis a cooldown, not a rate. Bigger is slower.2fmeans half speed.- Magazine size is hard-capped at 90 by
Mathf.Clamp(maxAmmo, 1, 90)in the game's own stat code. No card can exceed it. - Total gun damage has a floor of 0.25x, also clamped by the game.
- Bullet size is
Gun.size, additive padding on the bullet's collision trail (so it changes the hitbox, not just the sprite): the radius isdamage^0.85 / 400 + 0.3 + size.Gun.projectileSizealso exists and nothing in the game reads it. The game has no floor; this mod puts one at 0.05 (RayCastTrail_Start_Patch) so stacked Dense Bullets still hit. - Block cooldown is
(cooldown + cdAdd) * cdMultiplierwith no floor, so a zero multiplier really is instant. - Override the five-argument
SetupCardwhen you need the player'sBlock. That's the overloadCustomCard.Awakeactually calls; its default forwards to the four-argument one. GetStats()only draws the text on the card face. It doesn't affect gameplay, so it will happily lie to your players if you changeSetupCardand forget to update it.- Card text convention. Stat rows are only for changes to a stat (damage, health, speeds,
reload, ammo, gravity, size, block cooldown), including scaling ones like "+5% per second".
Everything else is a rule and goes in the description as a plain sentence: triggers,
special interactions, restrictions, resets. All-rules cards have no stat rows
(
NoStats); all-stats cards have no description. No flavour text, except on Hitscan, Onceler, MTG Player, Icon of Sin, Mahoraga and The Kirk Special - the last two replace their rules text with the joke on purpose. - Unless a player turns on card stat numbers in the game's options, a stat row shows its
SimpleAmountword ("A bunch more", "Lower") instead of the amount. Pick the word that reads correctly after the stat name, or leave it unset to always show the amount — needed for anything a word can't say, like "+5% per second" or "Reversed".
Effects that change during a fight
Prefer scaling a stat where the game reads it over writing to the stored value.
ApplyCardStats multiplies into the current value when cards are re-applied, and other
mods' temporary effects add to and subtract from the same fields, so a live write that is
"undone" later can leave the stat permanently wrong. The read points used here:
- damage:
HealthHandler.CallTakeDamage, on the one client that sends each hit (DamageMath). NeverProjectileHit.damage— the game reuses that field for bounce damage, poison and on-hit explosions on every client; - shot cooldown:
Gun.usedCooldown; reload time:GunAmmo.ReloadTime(); - movement speed:
PlayerMovement.Move, scaled for the call and put straight back; - bullet speed: each bullet's
MoveTransform.localForce, as it spawns.
Max health is read too widely to intercept, so Icon of Sin writes it relative to whatever
is there and takes it back out when the fight ends. Components that track a fight derive
from FightScoped, which handles the fight ending by death, by the point ending, and by a
full stat reset.
Damage that depends on positions or timers must be worked out once, by the client that sends it. Every client applies the broadcast number and nothing ever resyncs health, so a calculation repeated on each machine drifts apart for good.
Card art
All 68 cards have art.
Images live in art/ and are embedded into the DLL at build time, so the mod still
ships as a single file with no AssetBundle and no Unity project. At startup CardArt
decodes the bytes into a Texture2D, wraps it in a Sprite, and builds a RectTransform
Imageobject — which is whatCardVisuals.Startexpects, since it instantiatesCardInfo.cardArtas a child of the card'sCanvas/Front/Background/Arttransform.
To add art for another card:
- Drop a PNG or JPG in
art/. Unity'sTexture2D.LoadImagedecodes nothing else — WebP, GIF and BMP all fail silently at runtime, so convert first. - Add a line to
resourceByCardinsrc/Util/CardArt.csmapping the card's title to the filename. The key must matchGetTitle()exactly or the card just stays blank. dotnet build.
Keep source images at roughly 512px on the long edge. Prefer JPG unless the image genuinely
uses transparency — a PNG whose alpha channel is entirely opaque is several times larger for
an identical result. The art is drawn with preserveAspect, so any aspect ratio letterboxes
rather than distorting.
Known caveats
These are design limits, not bugs.
Prestige and Xane's Wire Management are the riskiest cards here. They're the only ones that rebuild a player's deck rather than change their stats, which they do through ModdingUtils. Each waits a frame after being picked before touching the deck. Worth testing on their own.
Infinity freezes enemy bullets that come within 2.5 units of the holder and releases
them if the holder moves away. A frozen bullet is not sweeping for collisions, so walking
into one does nothing. Radius is a knob in MoveTransform_Update_Patch. A standard bullet
has no visible body - its sprite is disabled in the prefab and all you see is a trail that
lives 0.03 seconds - so a frozen bullet is told to keep its trail, and a slow one too short
to see gets a small tail drawn behind it. The bullet's own 15-second despawn timer is also
held while it is frozen, so a long freeze can't destroy it mid-air.
Black Hole applies its pull only to players the local client owns, from the holder's
synced position. That keeps it from fighting the network sync, at the cost of each client
computing its own share. Radius and strength are knobs in BlackHole.
Tree pins the player to the first floor they land on each point, on the owning client only. Anything that moves them — knockback, a teleport card, a dash — gets undone on the next physics step. Walking is scaled to 0.001, not 0, to avoid feeding a zero into movement code that may divide by it, and jumps are refused outright. None of this is baked into the card's stats: every part checks live whether the player also holds The Lorax, which lifts all of it for as long as they hold it, in whichever order the two were picked.
The Kirk Special borrows an explosion prefab from the vanilla card pool at runtime (Supernova's, else Explosive Bullet's). Explosions deal damage through a broadcast RPC, so the visual is spawned on every client and only the attacker's own client keeps the damage. If no explosion prefab is found it falls back to plain area damage with no visual. Damage is 2.5x the holder's bullet damage in a 4-unit radius — both knobs on the card. One explosion per bullet hit, so shotguns and spray guns stack them. Only bullet hits schedule explosions; the explosions themselves never do.
MTG Player detects contact by overlapping collider bounds rather than collision callbacks, because ROUNDS moves players with kinematic physics. One hit per touch; the victim has to separate and come back.
Killdozer is calibrated by KilldozerContact.ReferenceSpeed, the speed a player has
at 150% movement speed. At that speed with a default gun, one touch does exactly a full
default health bar. The number is a guess until measured in-game; adjust it if one-taps
land too early or too late.
Portal Gun teleports to the mouse cursor. It does not check whether the destination is inside a wall. Controller players fall back to six units along their aim direction.
Icon of Sin's "-5% reload speed" is implemented as reload time shrinking 5%/s, matching the "stronger over time" intent.
Legendary is RarityLib's built-in tier: a yellow border, and a draw weight of 0.025 against Rare's 0.1, Uncommon's 0.4 and Common's 1 - a quarter as common as Rare. This mod puts twelve cards there. Xane's Wire Management swaps a Legendary for another Legendary.
Random Crits rolls once per bullet, so guns that fire several projectiles per shot get
several rolls per trigger pull. The roll happens on the client that owns the bullet, behind
the same hasControl flag the game uses, so all clients agree on who died.
The Last Breath makes you immune to everything while the window is open, including self-damage and damage over time. If you haven't won by the time the five seconds are up, it kills you outright. Winning the point resets it before the timer can fire.
Chris's Side Project and The Last Breath both end in a self-kill sent straight through the game's damage relay, which ignores blocking, so a well-timed shield cannot dodge it. Last Breath's invincibility window would still swallow Chris's deadline if both were active at once.
Go Blue works by briefly counting the dead holder as alive inside the game's
TeamsAlive() / GetLastTeamAlive() scans for 2.5 seconds (the card says one). If the
window closes with the point still open, the holder's death is announced a second time.
Multi-team lobbies are handled, but the card was designed around 1v1 ties.
Touch the Wall counts 3 pick phases in which you were offered cards — a phase where you don't pick (you won the round) doesn't use one up. It offers the rarest tier you can still be offered: Legendary, until every Legendary you could take is already in the hand or in your deck, then the next tier down. It works with and without CardChoiceSpawnUniqueCardPatch.
Daedalus Stormbow places each bullet at the top edge of the arena (y = 20), up to 3 units to either side of the cursor at random, pointed at the cursor. Only the shooter's client knows its cursor, so the spot is chosen there and travels with the bullet's own spawn message, which already carries position and rotation to everyone else. Controller players' "cursor" is six units along their aim. Every pellet of a multi-shot gun gets its own spot, so shotguns rain.
Patience stops other players' guns from firing for the first 3 seconds of each point, on each shooter's own machine, which is the only place a shot can start. The 3 seconds run on the holder's fight clock as each machine sees it, so the lock can end a few frames apart between players.
The Block Build Special fires block effects the same way the vanilla Empower card does: effects only, never the shield, once per trigger pull. Effects that behave differently for Empower (Teleport, Shield Charge) do the same here. Portal Gun teleports you on every shot.
Xane's Wire Management never draws itself or Prestige as a replacement. A card with no possible replacement is kept, and handed back without re-running its pick effects.
Mahoraga counts each hit once, even when Decay spreads it out. The reduction is worked out where the hit is sent, so everyone agrees on your health.
Conner's Pride genuinely checks the date — but at game start, since that's when
SetupCard and GetStats run. It reads the local system clock.
Bird Killer zeroes remainingRespawns for the duration of a killing blow, which also
suppresses any other respawn-granting mod.
Balanced averages two quantities that aren't in the same units (health ~100, bullet 55), so on a fresh player it reads as lose a quarter of your health, gain about 40% damage.
Giga Drum Mag cannot literally grant +100 ammo — the game clamps magazines at 90.
Ammo is spent per bullet, not per shot, everywhere in ROUNDS: each pellet a gun fires costs a round. So Super Shotgun (13 pellets against 3 + 9 ammo) fires once per reload, and Atomic Bomb empties the whole magazine every shot, which is the point of it.
Atomic Bomb sets bullets per shot to max ammo for the length of each shot and puts the stat straight back, so the link follows every ammo change and bullet count cards stop mattering while you hold it.
Super Shotgun copies the vanilla Buckshot card's range limit, read from the game's own data: spread 0.5, drag 10, bullets removed after 0.2s, bullet speed x3.5 - with bullet speed x3.85 instead, for about 10% more range. Drag takes a fixed fraction of a bullet's speed each frame, so range is roughly speed over drag. It does not copy Buckshot's -60% damage.
C-Ram squares attack speed as a percentage of the default rate, live and last, after every other attack speed effect, so cards picked later are squared too. Below 100% that makes you slower: 80% becomes 64%. It stacks, and each copy squares again - three copies turn 150% into about 2,560% and 80% into about 17%. The squared cooldown never drops below 0.0001s, which is already a shot every frame, and it is left out of the game's screen-shake maths so a nearly-never-firing gun can't fling the camera.
Drone Strike adds the real vanilla Homing and Explosive Bullet cards to your deck, a frame after it is picked. They are found by name; if another mod removes them from the game, nothing is added. Drone Strike, The Island and Prestige are never drawn by Xane's Wire Management, since each changes the deck as it is picked.
The Island picks its 2 cards at random, never another copy of The Island. Online, only the host's pick happens, so everyone loses the same cards.
500 Cigarettes converts damage from the vanilla Toxic Cloud card's clouds, recognised by
their prefab name, E_ToxicCloud. Those clouds also hit the player who made them, so a
holder who also has Toxic Cloud heals off their own clouds. The slow still applies. The
health loss is not damage: it doesn't flash, count as a hit for Mahoraga, or stop for a
shield, and it pauses during The Last Breath's invulnerability. Both the loss and the cloud
heal are decided on one client and broadcast, so every player sees the same health.
ASU Frat Leader compares actual body sizes, and the game grows a player with their max health as well as with size cards, so health cards count. Size ratio is your size over theirs: 10% bigger is 1.1, which is +110% damage. Anything not bigger - equal included - just scales damage by the ratio. Bullets only.
Projectile Dysfunction steers each bullet by an amount drawn from smooth random noise, keyed to the bullet's own random seed and how long it has been flying. The shooter sends that seed with every bullet, so every player watches each bullet take the same crazy path.
Rare Artifact cuts every piece of damage by 20% at the very last step, on every client - bullets, explosions, the map edge, poison. Instant kills are still instant kills.
Multiplayer. ROUNDS keeps health in sync only because every client applies the same broadcast damage. A few effects are still worked out on each machine separately, so they can differ by a hair between players:
- Poison (vanilla) ticks on each machine from the bullet's base damage. Damage multipliers don't boost it and Mahoraga doesn't reduce it.
- Icon of Sin's max health grows on each machine's own clock.
- Claude's Choice checks its cooldown on each machine.
- Banana's stand-still timer and heal run on each machine.
- The Last Breath window opens and closes on each machine's own clock, so a hit landing right at an edge of the window can count on one machine and not another.
Testing
Launch ROUNDS through Thunderstore Mod Manager, not Steam directly - the profile's BepInEx is what loads your DLL. To get a specific card reliably, open MODS → Toggle Cards from the main menu, hit Toggle All to disable everything, then re-enable just the card you want; every pick will then offer it.
Logs land in the profile's BepInEx/LogOutput.log.