Skip to content
madeyexzPublic

About

角頭械鬥 · Turf War: Taipei — a round-based SWAT vs Militia browser shooter set in Taipei. TypeScript, Three.js and SpacetimeDB multiplayer; play free, nothing to install.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

7 stars

Watchers

0 watching

Forks

Latest commit

 

History

651 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Turf War: Taipei logo

角頭械鬥 · Turf War: Taipei

A round-based SWAT vs Militia shooter set in the streets of Taipei — free in your browser, nothing to install.

Play now CI License: MIT PRs welcome
TypeScript Three.js SpacetimeDB Bun

Play · Gameplay guide · Contributing

Contents

About

Turf War: Taipei is an open-source, browser-first team shooter. Two teams of up to 24 fight short, one-life rounds across Taipei — Ximending's neon streets, an office floor 383 m up Taipei 101, the Chiang Kai-shek Memorial Hall — with cash, a store and weapon attachments in the spirit of the classic browser shooter BeGone. It runs on TypeScript and Three.js in the browser and a server-authoritative SpacetimeDB module online, with the same simulation powering offline play.

Features

Gameplay

  • Two modes: Elimination and Sabotage (Militia arms the bomb at site A or B; SWAT defends or disarms). First to 10 rounds, one life per round, a 4 s freeze and 20 s of buy time.
  • Thirteen maps: Ximending (西門町), Taipei 101 · Xinyi, Taipei 101 · 88F with its tuned mass damper, Memorial Hall (中正紀念堂), Sanchong (三重) across the river, homages to BeGone's maps and original battlefields — see Maps.
  • Vehicles: cars and taxis with handbrake drifts, scooters you can shoot from, and a helicopter.
  • Economy: cash for kills, headshots, assists and objectives; five primaries, two secondaries, grenades, and optics, suppressors, lasers and more (weapons).

Multiplayer

  • Rooms of every size: 1v1, 6v6 and 24v24. Quick play joins the fullest open room, Start a server opens a public room or a private one with a 4-letter code, and Join a server lists every live room with its ping.
  • Bots fill empty slots, online and offline, at three difficulties.
  • Server-authoritative: damage, cash, purchases, rounds and the bomb run on the server; movement is client-predicted and server-validated.

Accessibility and platforms

  • English and 繁體中文, switchable at any time.
  • Rebindable controls for every action, using physical key positions (AZERTY and Dvorak work).
  • Phones and tablets: touch controls with an editable layout, hold-to-aim firing, and an installable app (PWA) — see Phones and tablets.
  • Solo and practice need no server at all.

Screenshots

Taipei 101 · 88F: the tuned mass damper hanging in the core of the office floor
Taipei 101 · 88F: the damper hall in the tower's core
The authored Ximending map's storefronts and covered street
Ximending: storefronts and covered streets
Memorial Hall: the white hall and its grand staircase seen from Liberty Square
Memorial Hall (中正紀念堂): the grand staircase from Liberty Square
Taipei 101 seen from the authored Xinyi plaza
Xinyi: the plaza beside Taipei 101

Getting started

Prerequisites: Bun 1.3.10 (use bun / bunx, never npm or npx). For local online play, also the SpacetimeDB CLI 2.10.2.

git clone https://github.com/madeyexz/turfwar.git
cd turfwar
bun install --frozen-lockfile
bun install --cwd spacetimedb --frozen-lockfile
bun run dev            # http://localhost:5173 — Solo and Practice work right away

Online play on your machine — in a second terminal:

bun run dev:spacetime  # local SpacetimeDB on :3000 with the module published as "turfwar"
VITE_SPACETIMEDB_URI=same-origin VITE_SPACETIMEDB_DATABASE=turfwar bun run dev

Checks (the same ones CI runs on every push and pull request):

bun run test             # Vitest (not `bun test`)
bun run build            # type-check and production build
bun run typecheck:module # the SpacetimeDB server module

Project structure

shared/          Simulation shared by the browser and the server: movement, collision,
  maps/          weapons, vehicles, bots, rounds and the thirteen maps
  match/         Match state, economy, rooms, the packed per-tick frame and the tick itself
spacetimedb/     The SpacetimeDB server module: tables, reducers, admin views, scheduled tick
src/             The browser game: rendering, HUD, input, audio, lobby, online client
  module_bindings/  Generated client bindings for the module
public/          Models, textures, sounds, fonts, icons and the PWA manifest
admin/ privacy/  The owner's dashboard and the privacy page
about/ en/about/ The static about pages (繁體中文, English) with the FAQ, for search and AI answers (docs/SEO.md)
deploy/          The self-hosted SpacetimeDB container and its release scripts
tools/           Asset import and conversion tools (their own dependencies)
docs/            Gameplay, architecture and development guides

How it works

  • One simulation, two places. Movement, collision, weapons, bots and rounds live in shared/ and run unchanged in the browser (Solo, Practice, client prediction) and in the server module.
  • The server decides. Players' reducers only queue input; a 30 Hz scheduled tick applies it with the shared rules and publishes one packed frame per room, which clients interpolate.
  • Validated movement. Clients predict their own movement and report it; the server checks every report against speed, collision and vehicle rules and corrects anything out of bounds.
  • Always on. The game server (Taipei) never sleeps; if the lobby cannot reach it, it says so after 30 seconds and offers Retry.

The full design — validation, performance budgets and known trade-offs — is in docs/ARCHITECTURE.md.

Roadmap

Ideas the project would welcome help with (see known limitations):

  • Lag compensation (server-side rewind) for hit validation
  • Interest management, so clients receive only nearby soldiers
  • More Taipei maps, and authored reload animations
  • Skill-based matchmaking, vote kick and clans
  • Testing and polish on Safari and Firefox

Contributing

Contributions are welcome — bug reports, maps, balance, translations and code alike.

  1. Read CONTRIBUTING.md for setup, the checks and the rules of the codebase.
  2. Branch from dev and open your pull request against dev (never main).
  3. Make sure CI is green, and include screenshots for anything visual.

Please follow the Code of Conduct. Bugs and ideas go to GitHub issues.

Security and privacy

  • Security: please report vulnerabilities privately as described in SECURITY.md, not in a public issue.
  • Privacy: no accounts and no ads. The game keeps your settings and an anonymous ID in your browser, gameplay stats on the game server, and uses anonymous analytics. The full statement is on the privacy page (source).

Support the project

If you enjoy the game, ⭐ the repository and share it. Hosting is paid out of pocket — to sponsor the servers, email ian@rippling.computer.

Acknowledgements

License

The code is released under the MIT License. Third-party assets keep their own licences (CC0 and the SIL Open Font License). The names 角頭械鬥 and Turf War: Taipei and the logo are not licensed for reuse — forks are welcome under a different name.

The TW app icon was generated with Amp Painter. Its source is tools/app-icon.png; run bun tools/make-icons.ts (after installing tools/ dependencies) to regenerate the browser and home-screen sizes.

An approved, hand-traced SVG version is also available: eight editable vector paths with flat colors and no embedded raster image. It follows the same logo reuse restriction above. The icon generator still uses the original PNG; adding the SVG does not change the shipped icons.

For use without the black tile, download the transparent SVG or its 1024px PNG export. Both keep the same emblem and logo reuse restriction; the white W works best on dark backgrounds. These variants do not replace the app icons.

Asset credits and licences

No proprietary game assets are used: the weapon and mode names follow BeGone, but every model, texture, sound and line of code here is original or CC0/OFL.

  • Characters, animations, props and modular kit pieces/trim sheets: free “Standard” editions of Quaternius' Universal Base Characters, Universal Animation Library 1 & 2, Sci-Fi Essentials Kit and Modular Sci-Fi MegaKit — CC0 1.0 (https://quaternius.com). Converted by tools/import-assets.ts (textures resized to WebP, animation tracks pruned and meshopt-compressed); see public/assets/LICENSE.txt.
  • Soldiers: Quaternius' Ultimate Modular Men (the SWAT operator and six street-gang irregulars with their idle, walk, run and death animations) — CC0 1.0 (https://quaternius.com/packs/ultimatemodularcharacters.html). Converted by tools/import-modular.ts into public/assets/soldiers.glb and soldier-anims.glb; ?soldiers=classic brings back the Universal Base Characters soldier.
  • Weapons: models from Quaternius' Ultimate Gun Pack (a bayonet for the knife) — CC0 1.0 (https://opengameart.org/content/low-poly-guns-pack). Converted from OBJ by tools/import-guns.ts (bun tools/import-guns.ts, with the pack extracted under GUN_SRC; --preview <dir> renders measured side views for placing grips, sights and muzzles) into public/assets/guns.glb.
  • Gunshots: recordings from The Free Firearm Sound Library by Ben Jaszczak, Brian Nelson, Kevin Heras and Matthew Nanney — CC0 1.0 (https://opengameart.org/content/the-free-firearm-sound-library). Each firearm plays two to four separate near-distance takes round-robin (M9A1: Walther PPQ, MP7: PPSh, MP5: Carl Gustav M45, M4A1: AR-15, M249: AK-47, M1014: Benelli Nova and Winchester Model 12, M110: Tikka T3 and Springfield 1917 .30-06), with a mid-distance take per class as the distant layer.
  • Reload and handling foley, all CC0 1.0 on OpenGameArt: SpringySpringo's Gun Reload Sounds, BMacZero's Gun Reload Sound Effects, zer0_sol's Handgun Reload Sound Effect and Shotgun Reload Sound Effects, and LFA's Equipment Clicks III.
  • tools/fetch-sounds.ts downloads both, cuts the takes, trims, limits, normalizes and encodes them to mono MP3 in public/assets/sfx (~570 KB); see public/assets/sfx/LICENSE.txt. In game (src/audio.ts) each shot layers the recording with a synthesized crack and low thump, the recorded action, an outdoor tail and casing bounces; remote shots are delayed by distance and crossfade to the distant take, and suppressed shots are muffled.
  • Terrain and surface textures (including the brick, plank, corrugated-iron, plaster and cobblestone architecture of the BeGone maps): Poly Haven, CC0 1.0, fetched by tools/fetch-textures.ts; see public/assets/tex/LICENSE.txt.
  • Font: Rajdhani by Indian Type Foundry, SIL Open Font License 1.1 (public/fonts/OFL.txt).
  • Font: Oswald (Latin, the lobby) by the Oswald Project Authors, SIL Open Font License 1.1 (public/fonts/Oswald-OFL.txt).
  • Chinese font: Noto Sans TC by Google and Adobe, SIL Open Font License 1.1 (public/fonts/NotoSansTC-OFL.txt), from google/fonts. tools/subset-font.ts (bun tools/subset-font.ts; needs uv, or Python with fontTools and brotli) cuts the variable font to weights 500–900 and to the characters the game uses (every Chinese character in src/, shared/ and index.html, Taiwan's 4,808 common characters from tools/font/edu-standard-4808.txt, CJK punctuation, Bopomofo and full-width forms), in two files: public/fonts/noto-sans-tc.woff2 (the game's own characters and the punctuation, about 280 KB), loaded only for Chinese text (unicode-range), and public/fonts/noto-sans-tc-extra.woff2 (the rest of the common characters, about 1.1 MB), loaded only when a name or chat line needs one. src/ui/font.test.ts fails when the source uses a character the first file lacks.
  • The Taipei maps (Ximending, Taipei 101 · Xinyi, Taipei 101 · 88F), the Taipei 101 outline and the other city maps' layouts are this project's own, drawn from public knowledge of the real places (shared/maps/ximending*.ts, xinyi*.ts, taipei101.ts, tower101.ts); the city past each map is a procedural skyline (src/render/cityscape.ts); the props (scooters, trees, plants) and cars are built in code (src/render/props.ts, dressing.ts). No data from another game or from a map database is used.
  • Sky, terrain, architecture, effects and the remaining audio (knife, casings, footsteps, explosions, heartbeat, bomb, round and cash cues, UI, and the fallback weapon voices used before the recordings load) are procedural.
  • Engine/libraries: Three.js (MIT), Vite (MIT). The spacetimedb npm package declares ISC but ships the SpacetimeDB Business Source License 1.1 text; the SpacetimeDB server/CLI is BSL 1.1. Review those terms before any commercial self-hosting.

The asset tools live in tools/ with their own package.json; the source packs themselves are not committed.

About

角頭械鬥 · Turf War: Taipei — a round-based SWAT vs Militia browser shooter set in Taipei. TypeScript, Three.js and SpacetimeDB multiplayer; play free, nothing to install.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages