The code has two distinct but intertwined sections: the graphics engine, and the game code. Both have slots for 24 objects. Slot 0 is reserved for the player, slots 1 and 2 for the player's cannon projectiles, and slot 3 for the Warplink. If all slots are full enemies will be unable to fire their weapons, but in practice the game doesn't fill the slots to that degree.
The graphics engine data starts at $6600. It includes for each object:
Most of these are set by the OBJ# file and then overwritten during object initialization. The X/Y/Z position and facing values are kept for the initial set of objects.
The game object data starts at $8400. It includes for each object:
In addition, it has parameters for things like how many enemies must be killed before the Warplink appears. The data is set by the DYN# file, with most of the fields being cleared during object initialization. The object type here is used to initialize the type in the graphics engine for the initial objects.
Some fields are used for the player, like the shot status, while others like armor remaining are tracked elsewhere.
The mission briefing code sits on top of the game object data and code. It uses the graphics engine to display the shapes.
Index | Img | Name | Move Class |
---|---|---|---|
0 ($00) | (player) | 0 | |
1 ($01) | Sandsled | 1 | |
2 ($02) | Laser Tank | 1 | |
3 ($03) | Hovercraft | 1 | |
4 ($04) | Prowler | 1 | |
5 ($05) | Heavy Tank | 1 | |
6 ($06) | Stalker | 1 | |
7 ($07) | Gir Draxon | 1 | |
8 ($08) | Laser Battery | 2 | |
9 ($09) | Gun Battery | 2 | |
10 ($0a) | Pulsar | 2 | |
11 ($0b) | Skimmer | 3 | |
12 ($0c) | Stinger | 3 | |
13 ($0d) | Guiser | 4 | |
14 ($0e) | Seeker | 4 | |
15 ($0f) | Projectile - Low Laser | 5 | |
16 ($10) | Projectile - Medium Laser | 5 | |
17 ($11) | Projectile - High Laser | 5 | |
18 ($12) | Projectile - Light Cannon | 6 | |
19 ($13) | Projectile - Medium Cannon | 6 | |
20 ($14) | Projectile - Heavy Cannon | 6 | |
21 ($15) | Projectile - Thunder Cannon | 7 | |
22 ($16) | Obstacle | 8 | |
23 ($17) | Fuelbay | 8 | |
24 ($18) | Warplink | 8 | |
31 ($1f) | Explosion 0 | 9 | |
32 ($20) | Explosion 1 | 9 | |
33 ($21) | Explosion 2 | 9 | |
34 ($22) | Explosion 3 | 9 | |
35 ($23) | Explosion 4 | 9 | |
36 ($24) | Explosion 5 | 9 | |
37 ($25) | Explosion 6 | 9 | |
38 ($26) | Explosion 7 | 9 | |
41 ($29) | Impact 0 | 9 | |
42 ($2a) | Impact 1 | 9 | |
43 ($2b) | Impact 2 | 9 | |
44 ($2c) | Impact 3 | 9 | |
45 ($2d) | Impact 4 | 9 | |
46 ($2e) | Impact 5 | 9 | |
47 ($2f) | Impact 6 | 9 | |
48 ($30) | Impact 7 | 9 |
There are three "meta types", $19, $1a, and $1b. When a unit or projectile is destroyed, it's replaced with one of the meta-types. The meta value is combined with a counter to set the unit type to one of the explosion or impact frames.
Movement classes determine how objects behave and under what circumstances they appear on radar. Some things always appear, some never appear, and some only appear in "full" mode.
Class | Description | Radar? |
---|---|---|
0 | Player | Always |
1 | Ground vehicle | Always |
2 | Turret | Always |
3 | Flier | Always |
4 | Bomb | Always |
5 | Laser projectile | Full mode |
6 | Cannon projectile | Full mode |
7 | Player projectile | Full mode |
8 | Immobile object | Always |
9 | Explosion animation | Never |
Movement class ranges are used in a number of places. For example, classes 1 through 3 can fire weapons. When two objects collide, their movement classes determine the result (e.g. did a tank drive into an obstacle or a projectile)?
The LEV# files and (for the mission briefing) BRIEF.ST hold the mesh data. Each file starts with a table of 16-bit offsets to 64 shapes. The offset will be >= $8000 if the entry is unused. The 0th entry in the table, which would correspond to the player's tank, instead holds the address of the first byte past the end of the table.
The mesh data has two tables, vertices and edges, terminated by stop symbols ($80). Vertex data is three bytes, but is not Cartesian X/Y/Z coordinates. Instead, the first two bytes define X/Z with polar coordinates (distance then angle), while the third byte holds the Y coordinate. This arrangement makes computation of rotation about the Y axis simpler. The edge data table is just pairs of vertex indices.
As an example, consider the Obstacle:
shp_obstacle .bulk $6a,$20,$5a .bulk $6a,$20,$c4 .bulk $6a,$60,$5a .bulk $6a,$60,$c4 .bulk $6a,$e0,$5a .bulk $6a,$e0,$c4 .bulk $6a,$a0,$5a .bulk $6a,$a0,$c4 .dd1 $80 .bulk $00,$01 .bulk $02,$03 .bulk $04,$05 .bulk $06,$07 .bulk $00,$02 .bulk $04,$06 .bulk $01,$03 .bulk $05,$07 .bulk $00,$04 .bulk $02,$06 .bulk $01,$05 .bulk $03,$07 .dd1 $80
The first 8 lines are the vertices. They all have the same distance ($6a), which makes sense for a cube. The cube edges are axis-aligned, so the vertex angles are at 45 degrees. With 256 angles making a circle, the compass points are 0/64/128/192 ($00/$40/$80/$c0), so the vertices are at angles 32/96/160/224 ($20/$60/$a0/$e0).
The Y coordinates are signed, and defined so they don't need to be translated for the viewer (at Y=0). The Obstacle uses $5a/$c4, which are +90/-60. This gives the impression that the player's eye level is 60 units above ground level. You can see Y=-60 ($c4) used elsewhere, e.g. for the skids on the Sandsled. (The tallest shape in the game is the Warplink, which extends from -100 to +120.)
The vertex transformation code has an optimization that allows it to save some time if a vertex has the same distance and angle as the previous vertex (i.e. if they're on the same vertical line). The Obstacle definition takes advantage of this.
The end of the vertex list is marked with $80.
The next section defines the 12 edges. Each pair of bytes holds the indices of the start and end vertices of an edge. Lines are generally drawn with inclusive coordinates, so it doesn't matter in what order the edges or vertices appear.
As with vertices, the end of the edge list is marked with $80.
Copyright 2020 by Andy McFadden