Skip to content

jengine-tanks

Toy tanks: shells that ricochet off walls, mines that blow cork, tanks that aim by the rebound and dodge, a campaign of missions each proved by a bot, and versus rounds - one fixed-step sim for a local game and a server room.

KindProject package: linked into the builds of the projects that list it
Sourcepackages/jengine-tanks
Version0.1.0
Entry pointjengine_tanks::TanksPackage
Script namespaceTanks
Listed byprojects/shooter-classics

List it in the top-level "packages" array of project.jengine:

{
"packages": [
{ "name": "jengine-tanks", "source": { "type": "path", "path": "../../packages/jengine-tanks" } }
]
}

Then rebuild the editor viewport for the project (jengine viewport-build projects/<name>, or the editor’s Rebuild viewport banner). Native and web builds link it on their own.

A scene that uses its components must be a v2 scene ("version": 2): a v1 scene has no field for a package component and keeps it as inert custom data.

A script can check that the package is linked with if Tanks == nil then return end.

Scene keyRust type
tanks_battleTanksBattlepackage only: needs a v2 scene

Commands run in every runtime and return nothing; a package publishes what scripts read onto components (see the notes below). The flat name also works.

CallFlat name
Tanks.setup(e, setup)tanks_setup
Tanks.retry(e)tanks_retry
Tanks.control(e, seat, mx, my, aim, fire, mine)tanks_control
Tanks.auto(e, seat, on)tanks_auto
Tanks.speed(e, x)tanks_speed
  • projects/shooter-classics/assets/scenes/tanks.scene

From packages/jengine-tanks/README.md.

Wii Play Tanks’ rules as one deterministic fixed-step simulation: tanks on a tabletop of wooden and cork blocks and holes, shells that ricochet, mines that blow cork apart, and bots that aim by the rebound - tracing shots off the walls through the very step a shell flies by - and step out of a shell’s way. One copy of the rules runs a local game (through the tanks_battle component and Tanks.* commands) and a server room (which owns a sim::Battle outright), so a versus round online is the round at home.

Used by Shooter Classics’ TOY TANKS (projects/shooter-classics, the tanks scene) and its online versus room (projects/shooter-classics/server/games/tanks).

ModuleWhat it is
mapThe table: an ASCII drawing (. floor, # wood, x cork, o hole, 1-4 people, a letter per enemy kind), the world geometry, pushing a tank’s circle out of what it cannot enter.
simBattle: tanks, shells, mines, events, one 1/60 s step at a time - a campaign mission (people against the table) or a versus round (everyone against everyone). Body::substep is the one place a shell’s flight is worked out.
aiBrain: rebound aiming over the shell step (a straight line to each enemy, and for a kind that looks round walls a fan of angles all the way round, against where each enemy will be when the shell arrives), dodging, mine avoidance, goal cells and breadth-first paths, at three levels. The same brain is a person’s advisor: what the attract demo drives and a test pilot presses.
contentThe ten kinds of tank, the campaign’s 24 missions and four versus arenas.
  • A tank drives the way the stick points, turning its hull first (either end leads); the turret aims on its own. Blocks, holes, the table’s rim and other tanks stop it.
  • A shell flies straight and flips the component of its velocity that crossed into a wall; each kind has a number of bounces (a person’s shells one, teal and black rockets none, green rockets two), and the next wall spends it. A shell fired point-blank into a block is spent at the muzzle. Two shells that meet cancel. One shell kills any tank it touches - its own too.
  • A person has at most five shells in flight and two mines down. A mine arms once its layer has driven clear, then goes off half a second after any tank comes near, at the end of a twelve-second fuse, or when a shell hits it. Its blast kills tanks, spends shells, sets off other mines and breaks cork.
  • A mission is cleared when every enemy is destroyed and failed when every person’s tank is. A retry (Tanks.retry) keeps the destroyed enemies destroyed, as the original does. A versus round ends when one team (or none) is left.
KindLetterMovesShellNotes
brownbnever1 bounce, slowturns its turret slowly, aims only straight
ashaslowly1 bouncelooks one wall round
tealtslowlyfast rocket, no bounce
yellowyquickly1 bouncelays mines
pinkpslowly1 bouncethree shells, fast reload
greengneverfast rocket, 2 bouncesthe sniper: looks two walls round, precise
violetvquickly1 bouncefive shells, mines, dodges well
whitewyes1 bounceinvisible - seen only when it fires (and by its treads)
blackkfastestfast rocketdodges almost everything, leads its aim

tanks_battle on one entity: mode ("mission" or "versus"), mission (0-based), arena (versus, 0-based), people (1, or 2 for co-op), level (1 rookie, 2 veteran, 3 ace), bots (versus: one per seat, true for a bot), auto (the people’s seats driven by their advisors - an attract demo), advise (publish the advisor’s keys for each person), speed, seed, dead (mission starts already destroyed).

CallDoes
Tanks.setup(e, table)change any field above and start that battle (dead is forgotten unless the table names it)
Tanks.retry(e)the same mission again, its destroyed tanks left out
Tanks.control(e, seat, mx, my, aim, fire, mine)a person’s stick (-1..1, y up), turret aim (radians) and presses - a press waits for the next step
Tanks.auto(e, seat, on)hand a person’s seat to its advisor, or take it back
Tanks.speed(e, x)run at x times speed
  • TanksState, every frame - phase (intro, fight, over), intro (the countdown), result (cleared, failed, win, draw), winner, name, left (enemies), rev (which build this state is from - wait for it to change after a setup), mission, missions, mode, level, and the compact lists Battle::view writes, the same a server room sends: tanks [x, y, heading, turret, alive, visible, kind, team, seat, ready], shells [id, x, y, vx, vy, rocket, bounces_left], mines [id, x, y, blink, fuse, armed] (fuse the seconds left - what a mine’s countdown is drawn from). With advise, advice per person: { seat, keys, aim, ax, ay } - keys the letters W A S D (stick), F (fire), M (mine), ax, ay a world point to aim at.
  • TanksArena, when a battle starts or a block breaks - cols, rows, cell, cells (ASCII rows), name, rev.
  • TanksEvents.events, this frame’s: go, fire (x, y, a, tank, rocket), bounce, puff, clash, kill (tank, by, x, y, kind, how, seat, and dx, dy: the way the blow drove it, for debris to fly), mine, arm (a mine’s layer has driven clear), boom, cork (c, r, x, y), over (result, winner). Events only describe - nothing in the rules reads them.

cargo test -p jengine-tanks: the geometry (cells, push-out, a shell’s bounce and its spending), the rules (a shell kills its own tank after a bounce, shells cancel, a mine arms and breaks cork, a retry keeps the dead dead), that the AI’s traced line is the line a real shell flies, that the three levels rank in versus, and that every campaign mission is cleared by the advisor playing the person’s seat within the lives the campaign gives. The sim uses the platform’s sin/cos, so it is deterministic on one machine (a room is authoritative, so nothing compares two machines’ bits).