TeamWars
WORK IN PROGRESS. Competitive 2-team mode: pick your team in the lobby; everyone hauls to the same truck, the money is split per team by who carried each valuable in, and each team gets its own colour-coded cart.TeamWars
R.E.P.O. mod: competitive 2-team mode. Pick your team in the lobby; everyone hauls to the same vanilla truck, the money is split per team by who carried each valuable in, and each team gets its own colour-coded cart.
Work in progress — the mode is playable per-level (Red vs Blue with a running rounds score) but lacks series/seed mechanics.
Teams
- Red vs Blue. Teams are picked in the lobby with the small red/blue squares that TeamWars places next to every lobby player row (right of the ping); clicking a square asks the host to put you on that team. You can also re-pick mid-run from the pause menu: the same two squares appear below the MENU title.
- The host is authoritative and may also assign a square to any player. Players who never pick get auto-assigned by the host (30 s grace in the lobby, again at every level start), evenly so both teams stay balanced. A player without a team is never treated as a rival.
- Everyone else sees your choice too: roster strip up top in the lobby, coloured squares with a yellow frame on your current team, score line showing "you: Rojo/Azul".
Money and haul
- Everyone delivers to the same vanilla extraction. A valuable counts for the team of whoever carried it in — credit is pinned at the exact delivery instant and never moves afterwards, even when the other team grabs the object inside the truck.
- A valuable that rides a team cart is credited to the cart's team (the cart is the durable clue once nobody is holding it).
- Taking a valuable back out of the check un-does its delivery: it stops counting.
- Value that disappears inside the check waits briefly in limbo: if an extraction consumes it, it is banked on its team (the game zeroes its own haul at that point — without banking, a won round would score as a draw); if nothing consumes it, it likely broke or slid out and is dropped instead of counted at its last value.
- Per-team goals start at a configurable percentage of the level's single-spawn value
times a goal multiplier, split evenly over the checks (round total per team =
base percent of single), and are capped by what is still unextracted, so they
always stay reachable. Every
check shows the higher team goal, so each goal is also capped at
(level total - 1) / check count: the sum of all checks stays below 100% of the level's spawned valuables. Each living player shoulders an even share: with even teams the goal is the base per check, deaths shrink it (a wiped rival never raises yours), late joiners dilute it. Extra spawned loot (double by default) never raises the goals, so the map always holds more than both teams need together. The level's own extraction check is kept in step with the (higher) team goal, so delivering enough also satisfies it. - Each non-last check caps intake per team (
Economy.CheckIntakeMaxPercent, % of single-spawn, 0 = no cap): past its cap that team's new deliveries stay in the tray uncounted (floatingLLENO ROJO/LLENO AZUL/LLENO ROJO Y AZUL) until capacity frees or they are carried to the next check, so loot spreads across checks instead of piling into the first one and a big team cannot eat the small team's slot. The last check takes everything. A lone oversized valuable always fits (the first delivery of a team is never refused). - If a team still goes over its cap at delivery time (same-tick races), the check keeps the best-precision subset (closest to, at most, the cap — exact subset-sum over that team's counted items, never an empty set) and the rest is ignored by the delivery: pulled out of the haul list before vanilla destroys it, so it stays in the tray uncounted instead of banked or folded into surplus bags.
- Each extracted check awards one point to the team with the best haul/goal ratio over what that extraction consumed (draws award nothing). The round goes to the points leader; the tug bar is the live score of the point in play. The level's last check, and a claimed check that is its owner's last deliverable one, do not pay a surplus moneybag: the leftover becomes run currency, like vanilla.
- The tug-of-war bar above the score shows each team's live progress toward its own goal (24 ■ squares, red from the left, blue from the right), so a small team close to its small goal leads. Once a team wins the round the bar goes solid in its colour.
Carts
- Each team gets its own cart next to the truck, spawned and tinted in its team colour at every level start, and respawned at base if it breaks mid-round (a destroyed cart would gate every check forever). The rival team cannot grab it (the grab is refused and even the aim prompt is suppressed). The run's free neutral starter cart is removed.
- Objects sitting in a team cart are claimed for that team — colour, grab block and haul credit — whether they are valuables or ordinary items.
- An extraction check only delivers while released: in the default
DualGrabmode each check has a side button (the check's own button look) and delivery starts only while at least one player of each team holds it with the vanilla grab. Letting go mid-countdown aborts back to Active; once Extracting runs it always finishes. A team with nobody alive does not gate. IfExtraction.SoloHoldMinutesis over 0, a single live team holding the button alone for that many minutes releases the check without the other team (0 disables); the floating text shows the countdown on the host. Once both teams already met their goals,Extraction.BothGoalsSoloMinutes(default 1) lets either live team release alone after holding that long (0 disables). InCartsmode each check instead needs both teams' main carts sitting in it to start (the old rule);Offleaves the vanilla way out untouched. - The button glows grey (nobody), red/blue (one team holding) or green (both: released), and a floating text above a non-empty check names the missing side(s). The rival-touch block never applies to the button: it is neutral.
Protection
- A rival cannot grab an object owned by the other team — unless someone from
that team is holding it right now: contested grabs (steals from hands) follow
their own switch,
Protection.AllowGrabHeldByRival(default false). - A rival cannot break/crush an owned valuable by throwing, dropping or crashing it: the culprit's team is checked at the single point where impact damage becomes lost value, consistently on every machine.
- Both blocks are one switch:
Protection.BlockRivalTouch(default true). Turning it off only removes the blocks — attribution, colours and haul credit keep working, so deliveries still count for the right team. TurningAllowGrabHeldByRivaloff additionally forbids grabbing anything a rival holds, even unowned.
Round close
- Normally the level ends when the vanilla check is met and someone grabs the truck screen. TeamWars adds a competitive round: once a team leads on check-points, the vanilla exit is held back until the minimum time for the level (base + per-module, host config) has passed and the living players are in the truck.
- A grace period after the minimum time is the safety valve: past it, players still outside cannot hold the close back, so a stuck player never locks the run.
- Grabbing the truck screen then closes the round, advances the level and scores the winner (GANA ROJO / GANA AZUL through the game's own center-screen popup). Ties score nothing. With no point scored, the vanilla exit is untouched and the round closes with no winner.
- Rounds won is shown for the whole session in the HUD counter.
- Series (optional, off by default): with
Series.Enableon, the team that leads bySeries.WinLeadrounds (default 3) is announced as series champion (CAMPEÓN ROJO / CAMPEÓN AZUL through the same popup, plus a tag in the HUD). Display only: rounds keep going in endless fashion, nothing is blocked.
Revive
- A living player buys back one dead teammate from the truck at the physical terminal (solid totem against the truck wall: grab and hold it like any object, then E within 3.5 m — it flares while held). The totem carries a vanilla static-grab dummy with no rigidbody, so it can never move or rotate. The buyback answers with the check success sting or the cancel buzz at the totem. The team funds pay (purse plus what is already delivered to the check, even before extraction), the vanilla head revive runs wherever the head lies. Each press brings back one teammate (no head left, no revive).
- Price:
Revive.CostFlat(default $5000, ignored while percent is over 0), orRevive.CostPercent(default 5) to pay that percent of the single-spawn level total instead. Only the host's values matter. The HUD shows price, dead count and whether the team funds cover it. - Each buyback makes the next one pricier:
Revive.EscalationPercent(default 1) adds that percent of the base price per previous buyback (base * (1 + percent * uses)). The use count persists across levels and save games; 0 disables. - Reviving grows the team's goal back (goals count living players).
Extraction surplus
- Vanilla folds every surplus dollar above the goal into a single moneybag — grab-and-run race, new owner dictated by speed. TeamWars pays each team its own bag instead, split by each team's contribution to the level's extraction (largest-remainder rounding), each bag claimed and coloured at spawn.
The shop
- Each item spawns twice, one copy per team (
Shop.DuplicateForTeams, default on): the red copy on one side, the blue on the other, each tinted its colour. Each copy is grabbable and payable only by its team, and it never converts — even if the rival puts it in the check it stays its team's (it simply is not charged until its team grabs it to buy). - An item is bought by putting it in the shop's check and confirming; the game charges the shared pot and destroys the object. TeamWars works on both charge paths (the tube's one-item-per-tick, and the shop's own extraction that charges the whole list at once): it pins which team the items in the check belong to (whoever put them there), only lets through what the owner team can pay, and books the charge per team.
- Each team pays out of its own purse: what it has extracted over the run minus what it has spent. Unaffordable items (or with no team attached yet) stay in the check untouched; the shop's shared money stays vanilla.
- An item nobody held (pushed in with something else) has no owner and is never charged: it is ignored. Grab an item to attribute it to your team.
- Items sitting in the check wear the tint of the team that placed them (taken back when they leave the check).
- The check's price tag shows each team's share (red
Rojo -$XK| blueAzul -$YK, same units as vanilla) instead of one combined total, so each team knows what it is about to spend. Items with no team attached yet are listed apart with a?(grab one to attribute it to your team). The split is computed by the host and synced, so clients see the same tag. - The shared money is kept topped up to what both teams own together, so the game's own gating can never jam what a team could pay.
- The upgrade stand's reroll press is booked on the presser's team as well.
- The purchase record is kept by item name, so when the game re-creates the bought items on future spawns they are given back to the buyer's team.
- When those bought items spawn in the truck, each team's purchases are put on its side (red left, blue right, same sides as the team carts): once, at spawn. Anything a player already holds or held stays where it is.
- While in the shop the vanilla single-pot currency display is hidden and the HUD shows both purses instead.
Charger energy
- The truck's charger runs one vanilla pool; TeamWars splits it per team. The base charge divides 50/50 at run start, and every crystal bought in the shop feeds the buyer's team. Draining an item bills that item's team (whoever brought it); with no credit left the item stops charging and the pool keeps the rest.
- The vanilla bar, visuals and crystal-break sound follow the summed pool, so what every machine sees stays honest. The HUD counter shows each team's charge, in the level as well as in the shop and the post-shop lobby/truck.
HUD
- Persistent counter at the top of the screen:
Rojo haul/goal (players) — Azul haul/goal (players), check-points per team, rounds won, a close-state line (countdown to the minimum time, waiting on N players outside the truck, round closed, etc.) and which team you are on. - The game's own top-right counter (entered / required) follows your team's check (the shared check when you have no own one live), instead of the two trays added together and whichever check activated last. When your team has nothing left to deliver it hides on your screen instead of showing the combined total.
- Uses the game's own font and styling; no external assets.
Networking
- Host-authoritative: the host owns rosters, goals, tallies, ownership and round results and broadcasts them as Photon events; clients ask to join teams and to close rounds. Singleplayer is untouched: with no multiplayer session the mod disables itself and the game runs fully vanilla.
- State is re-synced periodically (roster, ownership, tallies, cart ownership), so a client that joined late or lost an event always converges. Late joiners get current goals and tallies too, not just the roster.
Configuration (BepInEx config, host values win in multiplayer)
| Setting | Default | Meaning |
|---|---|---|
Economy.TeamGoalBasePercent |
50 | Round-total goal per team, as percent of the level's single-spawn value, split over the checks (caps and handicap scale it down). |
Economy.TeamGoalMultiplier |
1 | Extra scale on each team's goal (1 = normal x1 demand, 2 = double). |
Economy.CheckIntakeMaxPercent |
50 | Intake cap per team per check, as percent of the level's single-spawn value (0 = no cap). Never below that team's demand; no cap on the last check. |
Spawning.ValuableMultiplier |
2 | How many times more valuables spawn per level (2 = double, one share per team). Only the host's value matters. |
Debug.VerboseLogs |
false | Detailed per-tick logs (haul ticks, tally/roster syncs, blocked damage). Keep off: logging costs performance. |
Round.MinTimeBaseMinutes |
10 | Base minimum round time. |
Round.MinTimePerModuleMinutes |
3 | Extra minutes per level module. |
Round.CloseGraceMinutes |
5 | Extra minutes after the minimum time before players outside the truck stop blocking the close. |
Extraction.GateMode |
DualGrab | How a check is released: DualGrab (both teams hold its side button), Carts (both main carts inside it) or Off. |
Extraction.CartRadius |
12 | How close (meters) a main cart must be to the check to count as inside it (Carts mode). |
Extraction.SoloHoldMinutes |
0 | Minutes a single live team must hold the button alone to release without the other team (0 disables). |
Extraction.ExclusiveChecks |
false | Shared quota activates shared (1 check on odd totals, 2 on even totals), later ones are claimed per team (first vanilla-button press, max 1 active per team; freed on delivery or wipe). Claimed checks count and release for their owner only; rival deliveries feed the owner. Each check shows its own tray (screen, tax return and surplus are per check), only one check extracts at a time, and an extraction consumes only its own tray. Denied presses get cancel sound + floating notice. |
Extraction.BothGoalsSoloMinutes |
1 | Minutes a single live team must hold the button alone to release once both teams already met their goals (0 disables). |
Protection.BlockRivalTouch |
true | Block grabbing or breaking what the other team owns. Off removes only the blocks; attribution still counts. |
Protection.AllowGrabHeldByRival |
false | Allow grabbing what a rival holds (contested grabs, steals from hands). Off forbids it, even unowned. |
Revive.EscalationPercent |
1 | Extra revive cost per previous buyback, as percent of the base price. Persists across levels and saves; 0 disables. |
Series.Enable |
false | Decide a series champion by round-win lead instead of endless rounds. |
Series.WinLead |
3 | Round-win lead that decides the series champion. Display only, rounds keep going. |
Shop.DuplicateForTeams |
true | Each shop item spawns twice, one per team, each grabbable and payable only by its team. Only the host's value matters. |
Shop.FreeWalkies |
0 | Minimum walkies guaranteed in the shop, free ($0). The host tops up on entry; bought walkies are not replaced. 0 disables. |
Map
- On the TAB map each team sees its own main cart's dot but not the
rival's, and the same goes for valuables (surplus moneybags included)
carrying the rival's ownership mark (re-hidden every 0.4 s; the game's own
Hideonly parks each dot for 0.5 s). Own and unattributed dots stay visible; viewers without a team see everything. - With exclusive checks the white trail guides you to your team's check (the
shared one when you have no own check), including while it waits in
Surplus/Warningfor the two-team release.
Installation
Requirements: BepInEx-BepInExPack (5.4.2305).
Install through r2modman / Thunderstore as cacus-TeamWars, or drop the DLL into
BepInEx/plugins.
Credits
- Fansastica — playtesting and bug reports that shaped this mod.
