Use the original handling, walls and projection from the executable

Decoded from SKIDMARKS_CD32's machine code:

- Car physics: the per-frame engine/grip/steering update, gravity and
  jumps, the hill effect on the throttle, and body tilt. Replaces the
  reconstructed handling model.
- The meaning of every .set value (separate drag, grip and power for
  human and computer drivers; steering, gravity, tow hitch), and the
  six handling presets, offered by a new Handling menu option.
- Walls: the road edges are quadratic B-splines through the gates'
  ends. They match the painted road edges.
- The real projection, which lifts points by their height; the height
  map is indexed by world cells, not screen pixels.
- Tracks have one more gate than the stored count: the last gate was
  being dropped on every track.

Computer drivers now follow the road between the walls and steer like
the original's. Vehicle assets store the values as `handling`; older
files still load. Docs describe the decoded logic.
This commit is contained in:
2026-10-20 07:32:46 +00:00
parent ee895e0455
commit 6226c88d4e
44 changed files with 1283 additions and 791 deletions
+25 -13
View File
@@ -28,32 +28,44 @@ give either to also gets the game data.
## What works ## What works
- All 24 tracks across the four track files, in their original palettes. - All 24 tracks across the four track files, in their original palettes.
Each has its isometric height map and lap gates. Each has its isometric height map and lap gates, and walls along the
road edges, curved the way the original curves them.
- All 9 cars (Mini, Beetle, Porsche, Formula 1, Midget, Truck, Police, - All 9 cars (Mini, Beetle, Porsche, Formula 1, Midget, Truck, Police,
Cow, Camaro) with their drivers' names, each with 32 headings and 25 Cow, Camaro) with their drivers' names, each with 32 headings and 25
body-tilt frames for hills. body-tilt frames for hills.
- The original handling: each car drives with the values from its `.set`
file, decoded from the executable, with separate profiles for human and
computer drivers. The menu's **Handling** option also offers the
original's six presets (Classic, Grunty, Zippy, Rippy, Slippery and Pedal
Car).
- The caravan: turn on **Caravans** in the menu and every car tows one. - The caravan: turn on **Caravans** in the menu and every car tows one.
- The original sound effects, and the original tune played by a built-in - The original sound effects, and the original tune played by a built-in
OctaMED player. The menu has a **Music** option: menus, always, or off. OctaMED player. The menu has a **Music** option: menus, always, or off.
- The original title picture. - The original title picture.
- 1 or 2 players (split screen) plus up to 7 computer drivers. - 1 or 2 players (split screen) plus up to 7 computer drivers.
- Power-slide handling, slopes, car-to-car bumping, and skid marks burnt - Power-slides, jumps, hills that slow you going up and speed you up coming
into the track. down, car-to-car bumping, and skid marks burnt into the track.
- Lap and position tracking, lap times, and a results table. - Lap and position tracking, lap times, and a results table.
- Engine note pitched with revs, tyre screech, crashes, backfires, a horn - Engine note pitched with revs, tyre screech, crashes, backfires, a horn
and start beeps, all from the original samples. and start beeps, all from the original samples.
## What's reconstructed ## What's from the original, and what isn't
The executable is on disk 1, but its game logic, a compiled Blitz Basic 2 The game logic is a compiled Blitz Basic 2 program on disk 1. These parts
program, hasn't been decoded. So the **handling, computer drivers and race were decoded from its machine code and work the same way here:
rules** are new code written to feel like the original, not a translation - the car physics: engine, grip, steering, gravity and jumps, and hills
of it. - the meaning of every `.set` value, and the handling presets
- the walls along the road edges
- how the world is projected onto the screen, including height
Each car's original settings file has eleven handling numbers whose The rest is new code written to feel like the original:
meaning lives in that code. They're kept in the car's settings asset for - how computer drivers choose their line (they steer and drive as the
when it is decoded. [docs/FORMATS.md](docs/FORMATS.md) covers everything original's do)
reverse engineered so far. - car-to-car collisions
- lap counting and the rescue of stuck cars
[docs/FORMATS.md](docs/FORMATS.md) covers everything reverse engineered so
far.
## Running ## Running
@@ -88,7 +100,7 @@ Options: `--scale 1|2|4` (window size), `--mute`.
skids info --data DIR # list tracks and vehicles found skids info --data DIR # list tracks and vehicles found
skids extract --data ADFS [--out assets] # convert original disks into editable assets skids extract --data ADFS [--out assets] # convert original disks into editable assets
skids bench --data DIR --seconds 120 # race CPU cars on every track: laps and time on road skids bench --data DIR --seconds 120 # race CPU cars on every track: laps and time on road
skids trace --data DIR --track 11 --out t.png # plot CPU racing lines skids trace --data DIR --track 11 --out t.png # plot walls, gates and CPU racing lines
skids render --data DIR --track 3 --out shot.png # headless screenshot skids render --data DIR --track 3 --out shot.png # headless screenshot
skids music --out tune.wav --seconds 60 # render the tune to WAV skids music --out tune.wav --seconds 60 # render the tune to WAV
skids unpack disk.adf --out files/ # extract a disk or LHA archive as-is skids unpack disk.adf --out files/ # extract a disk or LHA archive as-is
+50 -21
View File
@@ -18,8 +18,9 @@ The track picture: 1024 × 512, a 16-colour indexed (palette) image.
- **Palette:** the PNG's palette is the track's palette. Change a palette - **Palette:** the PNG's palette is the track's palette. Change a palette
entry to recolour every pixel that uses it. entry to recolour every pixel that uses it.
- **Road vs scenery:** colours 8–15 are road (full grip, full speed). - **Road vs scenery:** colours 8–15 are road; colours 0–7 are scenery,
Colours 0–7 are scenery (less grip, slower). where cars throw up dust. The picture doesn't affect handling. The
walls come from the gates (below).
- **Painting a new route:** paint it with the upper eight colours and - **Painting a new route:** paint it with the upper eight colours and
update the gates to match. update the gates to match.
- **Saving:** keep the image indexed if your editor allows it. If it saves - **Saving:** keep the image indexed if your editor allows it. If it saves
@@ -32,16 +33,26 @@ Plain text; lines starting with `#` are comments.
- **`gate x1 y1 x2 y2`:** lap gates in race order. - **`gate x1 y1 x2 y2`:** lap gates in race order.
- Gate 0 is the start/finish line, and the grid forms up behind it. - Gate 0 is the start/finish line, and the grid forms up behind it.
- Cars must cross every gate, in order, to complete a lap. Computer - Cars must cross every gate, in order, to complete a lap.
drivers steer from one gate to the next. - **Walls:** the gates' ends also shape the road. One edge is a smooth
curve through every gate's first end (`x1 y1`), the other through
every second end (`x2 y2`). Those curves are the walls, and computer
drivers follow the road between them. The curves pass halfway
between neighbouring gates' ends and round off the corners, as in
the original.
- Coordinates are on the isometric ground plane. To find where a point - Coordinates are on the isometric ground plane. To find where a point
sits in the picture: on the ground sits in the picture, with `z` its height in pixels
`picture x = wx - wy + 512`, `picture y = (wx + wy) × 31/64 − 252`. (from `height`):
- `skids trace --data assets --track N --out t.png` draws the gates `picture x = wx - wy + 512`, `picture y = (wx + wy) / 2 − 156 − z`.
(gate 0 in yellow) and the computer cars' paths over the picture. - `skids trace --data assets --track N --out t.png` draws the walls
- **`height`:** 64 rows of 64 numbers giving ground height × 256. Each (grey), the gates (gate 0 in yellow) and the computer cars' paths over
number covers a 16 × 8 pixel cell of the picture. Slopes push cars the picture.
downhill and tilt the car sprites. - **`height`:** 64 rows of 64 numbers giving ground height in pixels × 64.
- **Layout:** the rows run along world y and the numbers within a row
along world x. Each covers 16 × 16 world units.
- **Effect:** climbing slows cars and descending speeds them up. Cars
leave the ground over crests that drop away faster than gravity, and
the car sprites tilt with the slope.
- **`set`:** the name of the set of tracks this one belongs to, shown under - **`set`:** the name of the set of tracks this one belongs to, shown under
the menu thumbnail. the menu thumbnail.
- **`start`, `flags`:** values from the original data that the game - **`start`, `flags`:** values from the original data that the game
@@ -91,17 +102,35 @@ The display name, driver and handling:
| `name` | name shown in the menu | | `name` | name shown in the menu |
| `driver` | driver's name, shown next to it | | `driver` | driver's name, shown next to it |
| `trailer` | `yes` for something towed behind cars (the caravan) rather than driven | | `trailer` | `yes` for something towed behind cars (the caravan) rather than driven |
| `original`| the 16 values from the original `.set` file, kept for reference; the game doesn't use them yet | | `handling`| the 16 values from the original `.set` file, used as the original game uses them (below) |
| `top` | top speed on tarmac | | `mass` | weight when cars collide; not from the original data |
| `accel` | acceleration |
| `brake` | braking |
| `grip` | sideways grip; higher means less sliding |
| `turn` | steering rate |
| `mass` | weight in collisions |
| `offroad` | speed and grip multiplier on scenery |
The handling values are a reconstruction, so tune them freely. To add a The `handling` values, counting from 1:
car, add a new `<id>.png` / `<id>.txt` pair.
| Value | Meaning |
|-------|---------|
| 1, 2, 6 | computer drivers: drag, grip, power |
| 9, 10, 11 | human drivers: drag, grip, power |
| 3 | steering: 16 = 1/64 of a turn per frame at full lock |
| 5 | gravity: 8 = 1 pixel per frame² |
| 7 | tow hitch distance behind the car: 2 = 1 world unit |
| 4, 8, 12–16 | unused |
What the driving values do:
- **Drag** (below 64): how much of the engine's speed survives each frame.
Low drag gives fast pick-up and fast slowing.
- **Power:** what the throttle adds.
- **Top speed:** `power × drag / (64 − drag) / 32` world units per frame,
at 50 frames a second.
- **Grip:** how quickly the car's movement turns to follow where it's
pointing. Low grip means long slides.
The menu's **Handling** option can replace every car's values with one of
the original's presets.
Older files with `original`, `top`, `accel`, `brake`, `grip`, `turn` and
`offroad` lines still load: `original` is read as `handling`, and the
others are ignored. To add a car, add a new `<id>.png` / `<id>.txt` pair.
## Sounds: `sounds/*.wav` ## Sounds: `sounds/*.wav`
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK1-1. Lines starting with # are comments. # Skids track TRACK1-1. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 103.28125 584.9375 265.3125 631.09375 gate 103.28125 584.9375 265.3125 631.09375
gate 135.53125 439.90625 280.96875 551 gate 135.53125 439.90625 280.96875 551
gate 271.90625 440.125 343.78125 550.96875 gate 271.90625 440.125 343.78125 550.96875
@@ -26,6 +29,7 @@ gate 602.125 632.875 489.125 633.375
gate 600.6875 768.8125 488.4375 696.3125 gate 600.6875 768.8125 488.4375 696.3125
gate 599.25 904.75 487.75 759.25 gate 599.25 904.75 487.75 759.25
gate 456.75 935 408.5 776.75 gate 456.75 935 408.5 776.75
gate 276.5 753.46875 329.40625 696.40625
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Racing In The Name Of set Racing In The Name Of
@@ -34,8 +38,8 @@ set Racing In The Name Of
start 458 107 start 458 107
flags 1 flags 1
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK1-2. Lines starting with # are comments. # Skids track TRACK1-2. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 514.25 827.25 587.71875 941.65625 gate 514.25 827.25 587.71875 941.65625
gate 577 786 679.0625 854.1875 gate 577 786 679.0625 854.1875
gate 585 721 681.25 727.75 gate 585 721 681.25 727.75
@@ -35,6 +38,7 @@ gate 186 552 73.5 530.5
gate 253.125 618.375 144.34375 626.03125 gate 253.125 618.375 144.34375 626.03125
gate 311.4375 676.8125 239.25 719.28125 gate 311.4375 676.8125 239.25 719.28125
gate 357.25 723.75 333.1875 815.5625 gate 357.25 723.75 333.1875 815.5625
gate 438 802 452.875 928.625
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Racing In The Name Of set Racing In The Name Of
@@ -43,8 +47,8 @@ set Racing In The Name Of
start 461 295 start 461 295
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK1-3. Lines starting with # are comments. # Skids track TRACK1-3. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 754 375 827.25 340 gate 754 375 827.25 340
gate 686.25 304.25 692.875 209.25 gate 686.25 304.25 692.875 209.25
gate 614.375 275.875 608.6875 199.875 gate 614.375 275.875 608.6875 199.875
@@ -30,6 +33,7 @@ gate 376.5 563.5 316.5 653.5
gate 487.75 549.25 491.5 648.5 gate 487.75 549.25 491.5 648.5
gate 708 556 686.5 649.5 gate 708 556 686.5 649.5
gate 766.5 529.5 861 616.5 gate 766.5 529.5 861 616.5
gate 776 448.5 882.625 465.75
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Racing In The Name Of set Racing In The Name Of
@@ -38,8 +42,8 @@ set Racing In The Name Of
start 608 305 start 608 305
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK1-4. Lines starting with # are comments. # Skids track TRACK1-4. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 545.875 864.625 548 968 gate 545.875 864.625 548 968
gate 629.75 789.25 689.5 852.5 gate 629.75 789.25 689.5 852.5
gate 673.90625 729.21875 768.1875 735.3125 gate 673.90625 729.21875 768.1875 735.3125
@@ -30,6 +33,7 @@ gate 906.25 678.75 828.75 569.25
gate 761 667 688.5 575.5 gate 761 667 688.5 575.5
gate 645 675 623 573 gate 645 675 623 573
gate 550.4375 729.8125 495.75 655.25 gate 550.4375 729.8125 495.75 655.25
gate 465.40625 784.96875 375.375 789.125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Racing In The Name Of set Racing In The Name Of
@@ -38,8 +42,8 @@ set Racing In The Name Of
start 608 305 start 608 305
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6432 6700 6444 7135 6400 6400 6400 6692 6924 8802 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6432 6700 6444 7135 6400 6400 6400 6692 6924 8802 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK1-5. Lines starting with # are comments. # Skids track TRACK1-5. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 456.53125 333.8125 497.53125 236.8125 gate 456.53125 333.8125 497.53125 236.8125
gate 361.375 315.15625 308.875 219.65625 gate 361.375 315.15625 308.875 219.65625
gate 367.4375 414.6875 278.6875 347.4375 gate 367.4375 414.6875 278.6875 347.4375
@@ -31,6 +34,7 @@ gate 333.03125 801.21875 431.53125 800.71875
gate 381.40625 687.25 449.78125 749.875 gate 381.40625 687.25 449.78125 749.875
gate 472.28125 690.78125 551.53125 741.53125 gate 472.28125 690.78125 551.53125 741.53125
gate 468.5625 611.34375 549.5625 610.34375 gate 468.5625 611.34375 549.5625 610.34375
gate 473.71875 458.5 555.71875 434.5
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Racing In The Name Of set Racing In The Name Of
@@ -39,8 +43,8 @@ set Racing In The Name Of
start 301 79 start 301 79
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK1-6. Lines starting with # are comments. # Skids track TRACK1-6. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 826.15625 521.3125 744.65625 537.3125 gate 826.15625 521.3125 744.65625 537.3125
gate 826.0625 647.40625 745.0625 616.65625 gate 826.0625 647.40625 745.0625 616.65625
gate 825 776.5 727 680.5 gate 825 776.5 727 680.5
@@ -47,6 +50,7 @@ gate 539.1875 318.125 439.9375 263.375
gate 535.65625 389.6875 437.78125 406.5625 gate 535.65625 389.6875 437.78125 406.5625
gate 576.625 424.75 529.125 502.25 gate 576.625 424.75 529.125 502.25
gate 646.96875 425.4375 648.46875 504.4375 gate 646.96875 425.4375 648.46875 504.4375
gate 741.8125 425.625 705.8125 502.625
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Racing In The Name Of set Racing In The Name Of
@@ -55,8 +59,8 @@ set Racing In The Name Of
start 434 354 start 434 354
flags 1 flags 1
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK2-1. Lines starting with # are comments. # Skids track TRACK2-1. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 169.5 489.0625 266.625 584.9375 gate 169.5 489.0625 266.625 584.9375
gate 297.90625 488.4375 298.03125 583.625 gate 297.90625 488.4375 298.03125 583.625
gate 393.09375 489.125 394.71875 584.96875 gate 393.09375 489.125 394.71875 584.96875
@@ -27,6 +30,7 @@ gate 489.96875 839.53125 456.15625 757.59375
gate 360.5 837.5 380 756 gate 360.5 837.5 380 756
gate 222.9375 708.375 281.25 659.65625 gate 222.9375 708.375 281.25 659.65625
gate 80.25 559.03125 229.5625 610.21875 gate 80.25 559.03125 229.5625 610.21875
gate 97.875 487.03125 229.84375 585.8125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Even Flow set Even Flow
@@ -35,8 +39,8 @@ set Even Flow
start 474 167 start 474 167
flags 1 flags 1
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK2-2. Lines starting with # are comments. # Skids track TRACK2-2. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 177.09375 661.0625 280.84375 667.78125 gate 177.09375 661.0625 280.84375 667.78125
gate 94.03125 579.53125 231.53125 622.75 gate 94.03125 579.53125 231.53125 622.75
gate 133.53125 502.1875 237.1875 597 gate 133.53125 502.1875 237.1875 597
@@ -32,6 +35,7 @@ gate 671 856 590 781
gate 622.375 910.5 544.6875 822.6875 gate 622.375 910.5 544.6875 822.6875
gate 508.6875 934.5 481.71875 820.71875 gate 508.6875 934.5 481.71875 820.71875
gate 418 903.5 421.75 805.75 gate 418 903.5 421.75 805.75
gate 296.78125 781.03125 363.28125 749.75
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Even Flow set Even Flow
@@ -40,8 +44,8 @@ set Even Flow
start 339 117 start 339 117
flags 1 flags 1
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK2-3. Lines starting with # are comments. # Skids track TRACK2-3. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 444.875 209.125 371.875 150.125 gate 444.875 209.125 371.875 150.125
gate 449.84375 275.15625 378.09375 263.90625 gate 449.84375 275.15625 378.09375 263.90625
gate 440.8125 388.65625 373.4375 396.53125 gate 440.8125 388.65625 373.4375 396.53125
@@ -39,6 +42,7 @@ gate 514.25 270.75 628.25 356.75
gate 610 280 715.5 340.5 gate 610 280 715.5 340.5
gate 627.25 212.75 703.5 208.5 gate 627.25 212.75 703.5 208.5
gate 565.5 156.5 587 89 gate 565.5 156.5 587 89
gate 480 148 466 70
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Even Flow set Even Flow
@@ -47,8 +51,8 @@ set Even Flow
start 420 97 start 420 97
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK2-4. Lines starting with # are comments. # Skids track TRACK2-4. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 439.5 408.5 359.21875 465.90625 gate 439.5 408.5 359.21875 465.90625
gate 489 427 455.5 518.5 gate 489 427 455.5 518.5
gate 552 426 553 519 gate 552 426 553 519
@@ -46,6 +49,7 @@ gate 584.0625 247.5625 537.125 136.6875
gate 551.3125 264.84375 488.125 183.375 gate 551.3125 264.84375 488.125 183.375
gate 504.84375 311.25 424.9375 247.21875 gate 504.84375 311.25 424.9375 247.21875
gate 465.90625 350.15625 376.78125 298.09375 gate 465.90625 350.15625 376.78125 298.09375
gate 426 376 306.9375 363.3125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Even Flow set Even Flow
@@ -54,8 +58,8 @@ set Even Flow
start 689 201 start 689 201
flags 1 flags 1
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK2-5. Lines starting with # are comments. # Skids track TRACK2-5. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 327.5 333.375 262 304.875 gate 327.5 333.375 262 304.875
gate 326.6875 383.96875 265.1875 378.46875 gate 326.6875 383.96875 265.1875 378.46875
gate 327.8125 521.375 262.8125 516.375 gate 327.8125 521.375 262.8125 516.375
@@ -50,6 +53,7 @@ gate 664.96875 296.4375 609.71875 230.6875
gate 598.9375 295.90625 581.4375 233.40625 gate 598.9375 295.90625 581.4375 233.40625
gate 486.375 293.34375 450.375 235.34375 gate 486.375 293.34375 450.375 235.34375
gate 350.8125 298.75 320.0625 235.5 gate 350.8125 298.75 320.0625 235.5
gate 327.75 299.6875 267.75 237.6875
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Even Flow set Even Flow
@@ -58,8 +62,8 @@ set Even Flow
start 353 47 start 353 47
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK2-6. Lines starting with # are comments. # Skids track TRACK2-6. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 361.125 706.8125 424.75 670.6875 gate 361.125 706.8125 424.75 670.6875
gate 277 621 365 609 gate 277 621 365 609
gate 279.6875 502.3125 364.6875 525.3125 gate 279.6875 502.3125 364.6875 525.3125
@@ -53,6 +56,7 @@ gate 732.4375 701.46875 647.6875 702.21875
gate 722.125 791.0625 640.125 753.0625 gate 722.125 791.0625 640.125 753.0625
gate 659.5 864.25 598.5 787.25 gate 659.5 864.25 598.5 787.25
gate 556.375 884.4375 560.75 793.5625 gate 556.375 884.4375 560.75 793.5625
gate 474.75 821.125 516.5 762.375
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Even Flow set Even Flow
@@ -61,8 +65,8 @@ set Even Flow
start 353 47 start 353 47
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK3-1. Lines starting with # are comments. # Skids track TRACK3-1. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 556.09375 597.4375 540.59375 694.9375 gate 556.09375 597.4375 540.59375 694.9375
gate 643.5625 601.125 638.8125 697.875 gate 643.5625 601.125 638.8125 697.875
gate 696.53125 599.34375 791.03125 692.84375 gate 696.53125 599.34375 791.03125 692.84375
@@ -43,6 +46,7 @@ gate 282 602.75 277.375 692.65625
gate 307.5 604.875 307.6875 694.3125 gate 307.5 604.875 307.6875 694.3125
gate 358.5 603.5 386 696 gate 358.5 603.5 386 696
gate 437.34375 599.28125 412.75 695.25 gate 437.34375 599.28125 412.75 695.25
gate 448.1875 597.0625 481.5 696.5
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Surf 'n' Slam set Surf 'n' Slam
@@ -51,8 +55,8 @@ set Surf 'n' Slam
start 641 315 start 641 315
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK3-2. Lines starting with # are comments. # Skids track TRACK3-2. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 463.875 679.71875 550.09375 653.59375 gate 463.875 679.71875 550.09375 653.59375
gate 393.6875 588.65625 498.75 595.40625 gate 393.6875 588.65625 498.75 595.40625
gate 371.5 487.625 481.90625 548.71875 gate 371.5 487.625 481.90625 548.71875
@@ -29,6 +32,7 @@ gate 420.46875 755.125 419 867.0625
gate 498.8125 807.25 578.75 940.28125 gate 498.8125 807.25 578.75 940.28125
gate 566.5 820.5 690.75 890.25 gate 566.5 820.5 690.75 890.25
gate 597.75 790.3125 707.8125 768.96875 gate 597.75 790.3125 707.8125 768.96875
gate 550.0625 735.75 637.21875 701.03125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Surf 'n' Slam set Surf 'n' Slam
@@ -37,8 +41,8 @@ set Surf 'n' Slam
start 475 288 start 475 288
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK3-3. Lines starting with # are comments. # Skids track TRACK3-3. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 810 502 905 589 gate 810 502 905 589
gate 806 456 904.5 475.5 gate 806 456 904.5 475.5
gate 787.5 406.5 859.5 378.5 gate 787.5 406.5 859.5 378.5
@@ -37,6 +40,7 @@ gate 565.375 326.125 574.3125 437.6875
gate 635 363 636.75 487.25 gate 635 363 636.75 487.25
gate 689.25 419.75 707.625 558.375 gate 689.25 419.75 707.625 558.375
gate 741.4375 471.8125 785.84375 638.65625 gate 741.4375 471.8125 785.84375 638.65625
gate 782.125 507.375 850.5625 636.4375
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Surf 'n' Slam set Surf 'n' Slam
@@ -45,8 +49,8 @@ set Surf 'n' Slam
start 809 244 start 809 244
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK3-4. Lines starting with # are comments. # Skids track TRACK3-4. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 562.5 134.625 626 267.125 gate 562.5 134.625 626 267.125
gate 669.125 175.03125 665.875 301.28125 gate 669.125 175.03125 665.875 301.28125
gate 835.25 345.9375 720.75 356.4375 gate 835.25 345.9375 720.75 356.4375
@@ -38,6 +41,7 @@ gate 597.0625 448.21875 736.5625 554.71875
gate 573.59375 394.84375 716.59375 427.84375 gate 573.59375 394.84375 716.59375 427.84375
gate 483.625 301.96875 614.125 331.46875 gate 483.625 301.96875 614.125 331.46875
gate 450.3125 217.03125 589.3125 298.03125 gate 450.3125 217.03125 589.3125 298.03125
gate 485.90625 155.3125 589.15625 269.0625
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Surf 'n' Slam set Surf 'n' Slam
@@ -46,8 +50,8 @@ set Surf 'n' Slam
start 796 294 start 796 294
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK3-5. Lines starting with # are comments. # Skids track TRACK3-5. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 522.3125 64.1875 567.8125 186.6875 gate 522.3125 64.1875 567.8125 186.6875
gate 734.5 221.5 638 256 gate 734.5 221.5 638 256
gate 792 413.6875 637.5 300.1875 gate 792 413.6875 637.5 300.1875
@@ -35,6 +38,7 @@ gate 308.625 399.375 423.625 431.375
gate 304.5 269.5 405 337 gate 304.5 269.5 405 337
gate 335.75 207.5 415.75 287.5 gate 335.75 207.5 415.75 287.5
gate 386.125 161.5 465.375 240.25 gate 386.125 161.5 465.375 240.25
gate 432.5 111.5 512.5 191.5
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Surf 'n' Slam set Surf 'n' Slam
@@ -43,8 +47,8 @@ set Surf 'n' Slam
start 744 137 start 744 137
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK3-6. Lines starting with # are comments. # Skids track TRACK3-6. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 486.125 379.375 471.1875 273.8125 gate 486.125 379.375 471.1875 273.8125
gate 441.75 391.25 369.125 274.875 gate 441.75 391.25 369.125 274.875
gate 404.875 417.625 314.8125 321.1875 gate 404.875 417.625 314.8125 321.1875
@@ -46,6 +49,7 @@ gate 785.40625 579.625 944.625 631.8125
gate 796.78125 544.59375 896.84375 523.4375 gate 796.78125 544.59375 896.84375 523.4375
gate 760.125 503.53125 816.78125 446.34375 gate 760.125 503.53125 816.78125 446.34375
gate 658.5 417.5 705.75 366.25 gate 658.5 417.5 705.75 366.25
gate 585.5625 383.1875 589.46875 297.03125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Surf 'n' Slam set Surf 'n' Slam
@@ -54,8 +58,8 @@ set Surf 'n' Slam
start 356 188 start 356 188
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK4-1. Lines starting with # are comments. # Skids track TRACK4-1. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 310.09375 599.75 182.5 594.25 gate 310.09375 599.75 182.5 594.25
gate 306.09375 660.3125 203 706.375 gate 306.09375 660.3125 203 706.375
gate 341.625 703.375 328.5 831.5 gate 341.625 703.375 328.5 831.5
@@ -36,6 +39,7 @@ gate 402 504 402 378
gate 402 504 402 378 gate 402 504 402 378
gate 342.53125 501.71875 264 374.5 gate 342.53125 501.71875 264 374.5
gate 316.5625 513.03125 192.25 387 gate 316.5625 513.03125 192.25 387
gate 310.09375 542.65625 184 495.5
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Sounf Of Speed set Sounf Of Speed
@@ -44,8 +48,8 @@ set Sounf Of Speed
start 213 275 start 213 275
flags 1 flags 1
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK4-2. Lines starting with # are comments. # Skids track TRACK4-2. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 495.78125 950.1875 540.1875 849.78125 gate 495.78125 950.1875 540.1875 849.78125
gate 400.03125 867.9375 513.3125 821.65625 gate 400.03125 867.9375 513.3125 821.65625
gate 374.78125 805.1875 508.4375 791.53125 gate 374.78125 805.1875 508.4375 791.53125
@@ -46,6 +49,7 @@ gate 711.6875 750.40625 583.65625 688.46875
gate 715.375 842.65625 586.40625 755 gate 715.375 842.65625 586.40625 755
gate 701.84375 891.1875 585.65625 814.03125 gate 701.84375 891.1875 585.65625 814.03125
gate 663.3125 939.1875 581.0625 837.78125 gate 663.3125 939.1875 581.0625 837.78125
gate 608.78125 967.1875 571.4375 852.53125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Sounf Of Speed set Sounf Of Speed
@@ -54,8 +58,8 @@ set Sounf Of Speed
start 122 216 start 122 216
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK4-3. Lines starting with # are comments. # Skids track TRACK4-3. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 503.90625 560.59375 573.40625 679.09375 gate 503.90625 560.59375 573.40625 679.09375
gate 582.71875 565.78125 651.21875 689.28125 gate 582.71875 565.78125 651.21875 689.28125
gate 699.375 589.34375 743.375 713.34375 gate 699.375 589.34375 743.375 713.34375
@@ -39,6 +42,7 @@ gate 346.25 489.65625 296.0625 570.84375
gate 372.1875 581.84375 301.1875 652.34375 gate 372.1875 581.84375 301.1875 652.34375
gate 402.625 626.75 392.625 759.25 gate 402.625 626.75 392.625 759.25
gate 435.0625 628.59375 502.1875 772.34375 gate 435.0625 628.59375 502.1875 772.34375
gate 460.5 581.40625 544.75 708.40625
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Sounf Of Speed set Sounf Of Speed
@@ -47,8 +51,8 @@ set Sounf Of Speed
start 672 360 start 672 360
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK4-4. Lines starting with # are comments. # Skids track TRACK4-4. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 633.9375 408.65625 639.0625 527.6875 gate 633.9375 408.65625 639.0625 527.6875
gate 714.9375 491.65625 738.0625 620.6875 gate 714.9375 491.65625 738.0625 620.6875
gate 773.125 529.625 824.1875 651.21875 gate 773.125 529.625 824.1875 651.21875
@@ -42,6 +45,7 @@ gate 298.375 350.65625 413.6875 398.3125
gate 293.90625 281.8125 414.1875 352 gate 293.90625 281.8125 414.1875 352
gate 347.40625 233.625 448.21875 353.125 gate 347.40625 233.625 448.21875 353.125
gate 423.4375 212.9375 488.71875 377.71875 gate 423.4375 212.9375 488.71875 377.71875
gate 564.25 334.1875 561.15625 443.375
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Sounf Of Speed set Sounf Of Speed
@@ -50,8 +54,8 @@ set Sounf Of Speed
start 800 350 start 800 350
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK4-5. Lines starting with # are comments. # Skids track TRACK4-5. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 361 644.5 356 731.5 gate 361 644.5 356 731.5
gate 430.75 680.5 500.25 773 gate 430.75 680.5 500.25 773
gate 490.1875 665 569.1875 744 gate 490.1875 665 569.1875 744
@@ -46,6 +49,7 @@ gate 344.875 359.8125 261.375 281.3125
gate 265.75 441.875 165.75 375.875 gate 265.75 441.875 165.75 375.875
gate 247.84375 489.21875 133.09375 438.96875 gate 247.84375 489.21875 133.09375 438.96875
gate 259.9375 536.59375 154.9375 525.59375 gate 259.9375 536.59375 154.9375 525.59375
gate 295.96875 583.03125 220.96875 624.03125
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Sounf Of Speed set Sounf Of Speed
@@ -54,8 +58,8 @@ set Sounf Of Speed
start 362 264 start 362 264
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+9 -5
View File
@@ -1,11 +1,14 @@
# Skids track TRACK4-6. Lines starting with # are comments. # Skids track TRACK4-6. Lines starting with # are comments.
# #
# The picture is track.png: 1024x512, 16 colours. Its palette is the # The picture is track.png: 1024x512, 16 colours. Its palette is the
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery. # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).
# #
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is
# the start/finish line; the grid forms up behind it. World to picture: # the start/finish line; the grid forms up behind it. The gates' ends
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252 # also shape the walls: each edge of the road is a smooth curve
# through the matching ends of the gates. World to picture, for a
# point at height z (pixels):
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z
gate 833.125 731.5 782.625 620 gate 833.125 731.5 782.625 620
gate 679.4375 694.5 660.6875 577.25 gate 679.4375 694.5 660.6875 577.25
gate 522.59375 598 507.96875 476.625 gate 522.59375 598 507.96875 476.625
@@ -41,6 +44,7 @@ gate 497.5625 440.6875 597.5625 484.6875
gate 506.1875 355.09375 607.4375 450.84375 gate 506.1875 355.09375 607.4375 450.84375
gate 628.3125 300 668.3125 426 gate 628.3125 300 668.3125 426
gate 841.875 457.5 766.875 502.5 gate 841.875 457.5 766.875 502.5
gate 919.96875 630.5 800.96875 579.5
# Name of the set of tracks this one belongs to, shown in the menu. # Name of the set of tracks this one belongs to, shown in the menu.
set Sounf Of Speed set Sounf Of Speed
@@ -49,8 +53,8 @@ set Sounf Of Speed
start 501 153 start 501 153
flags 0 flags 0
# height: 64 rows of 64 values, ground height x 256. Each value covers # height: 64 rows (world y) of 64 values (world x), ground height in
# 16x8 pixels of the picture. # pixels x 64. Each value covers 16 x 16 world units.
height height
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400 6400
+10 -16
View File
@@ -4,20 +4,14 @@ name CAMARO
driver ROD driver ROD
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 54 35 32 8 7 22 22 16 49 14 53 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 200 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 170 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 54 35 32 8 7 22 22 16 49 14 53 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 5
# steering rate, radians per second
turn 3.6
# weight in collisions
mass 1 mass 1
# speed and grip multiplier off the road
offroad 0.55
+10 -16
View File
@@ -4,20 +4,14 @@ name CARAVAN
driver SIMON driver SIMON
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer yes trailer yes
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 2 65532 65532 1 12 10 16 14 0 0 0 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 200 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 170 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 2 65532 65532 1 12 10 16 14 0 0 0 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 5
# steering rate, radians per second
turn 3.6
# weight in collisions
mass 1 mass 1
# speed and grip multiplier off the road
offroad 0.55
+10 -16
View File
@@ -4,20 +4,14 @@ name COW
driver ROB driver ROB
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 15 14 34 8 4 598 15 12 13 4 2003 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 170 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 150 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 15 14 34 8 4 598 15 12 13 4 2003 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 7
# steering rate, radians per second
turn 4.6
# weight in collisions
mass 1.4 mass 1.4
# speed and grip multiplier off the road
offroad 0.9
+10 -16
View File
@@ -4,20 +4,14 @@ name FORMULA 1
driver SIMON driver SIMON
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 60 71 30 0 6 16 24 12 58 42 29 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 240 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 210 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 60 71 30 0 6 16 24 12 58 42 29 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 6.5
# steering rate, radians per second
turn 3.7
# weight in collisions
mass 0.9 mass 0.9
# speed and grip multiplier off the road
offroad 0.45
+10 -16
View File
@@ -4,20 +4,14 @@ name MIDGET
driver ROB driver ROB
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 51 21 30 16 3 45 16 13 44 13 93 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 205 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 185 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 51 21 30 16 3 45 16 13 44 13 93 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 4
# steering rate, radians per second
turn 4
# weight in collisions
mass 0.85 mass 0.85
# speed and grip multiplier off the road
offroad 0.6
+10 -16
View File
@@ -4,20 +4,14 @@ name MINI
driver ROB driver ROB
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 56 24 30 12 3 24 20 12 46 10 100 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 190 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 200 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 56 24 30 12 3 24 20 12 46 10 100 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 5.8
# steering rate, radians per second
turn 4.4
# weight in collisions
mass 0.8 mass 0.8
# speed and grip multiplier off the road
offroad 0.55
+10 -16
View File
@@ -4,20 +4,14 @@ name POLICE
driver SIMON driver SIMON
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 56 34 22 6 2 15 19 11 49 14 44 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 215 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 180 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 56 34 22 6 2 15 19 11 49 14 44 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 5
# steering rate, radians per second
turn 3.5
# weight in collisions
mass 1.2 mass 1.2
# speed and grip multiplier off the road
offroad 0.55
+10 -16
View File
@@ -4,20 +4,14 @@ name PORSCHE
driver IMAGINE driver IMAGINE
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 56 34 22 6 2 15 21 12 49 14 44 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 225 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 190 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 56 34 22 6 2 15 21 12 49 14 44 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 5.2
# steering rate, radians per second
turn 3.6
# weight in collisions
mass 1 mass 1
# speed and grip multiplier off the road
offroad 0.55
+10 -16
View File
@@ -4,20 +4,14 @@ name TRUCK
driver ROD driver ROD
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 55 26 28 18 2 29 20 17 54 19 35 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 195 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 140 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 55 26 28 18 2 29 20 17 54 19 35 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 4.2
# steering rate, radians per second
turn 3
# weight in collisions
mass 1.8 mass 1.8
# speed and grip multiplier off the road
offroad 0.75
+10 -16
View File
@@ -4,20 +4,14 @@ name BEETLE
driver ROB driver ROB
# yes = towed behind cars (Caravans option) instead of driven # yes = towed behind cars (Caravans option) instead of driven
trailer no trailer no
# The 16 values from the original .set file. Their meaning is not #
# decoded yet, so the game does not use them; the handling below is # handling: the 16 values from the original .set file, used as the
# a reconstruction. # original game does. Counting from 1:
original 57 28 32 12 3 29 20 14 51 15 63 0 0 0 0 0 # 1 2 6 computer drivers: drag, grip, power
# top speed on tarmac, world pixels per second # 9 10 11 human drivers: drag, grip, power
top 185 # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.
# acceleration and braking, world pixels per second squared # Top speed = power * drag / (64 - drag) / 32 world units per frame,
accel 160 # 50 frames a second (drag below 64). Higher grip, less sliding.
brake 320 handling 57 28 32 12 3 29 20 14 51 15 63 0 0 0 0 0
# sideways grip (higher = less sliding) # weight when cars collide (not from the original data)
grip 4.6
# steering rate, radians per second
turn 3.6
# weight in collisions
mass 1 mass 1
# speed and grip multiplier off the road
offroad 0.55
+175 -29
View File
@@ -1,8 +1,8 @@
# Super Skidmarks data formats # Super Skidmarks data formats
Notes from reverse engineering the AGA/CD32 release of *Super Skidmarks* Notes from reverse engineering the AGA/CD32 release of *Super Skidmarks*
(Acid Software, 1995), worked out from all six disks. The one thing not (Acid Software, 1995), worked out from all six disks and from the main
yet decoded is the executable's game logic. See [Gaps](#gaps). executable's machine code. See [Gaps](#gaps) for what's still unknown.
All multi-byte values are big-endian unless noted. All multi-byte values are big-endian unless noted.
@@ -70,7 +70,7 @@ The last 142 bytes of section 0 hold:
|--------|------|----------| |--------|------|----------|
| 0 | 2 | File number (1–4) | | 0 | 2 | File number (1–4) |
| 2 | 96 | The palette shared by the file's six tracks: 16 colours × R, G, B, each a `u16` from 0 to 15 (Amiga 12-bit colour) | | 2 | 96 | The palette shared by the file's six tracks: 16 colours × R, G, B, each a `u16` from 0 to 15 (Amiga 12-bit colour) |
| 98 | 12 | Unknown | | 98 | 12 | Six `u16`, one per track, meaning unknown. The values are 0–5, some with bit 6 (`0x40`) set, plus one `0x3F`. |
| 110 | … | The name of the set of tracks, NUL-terminated | | 110 | … | The name of the set of tracks, NUL-terminated |
The first 20480 bytes of section 0 are the track-selector thumbnails: The first 20480 bytes of section 0 are the track-selector thumbnails:
@@ -88,38 +88,63 @@ Each track section unpacks to 281240 (`0x44A98`) bytes:
| Offset | Size | Contents | | Offset | Size | Contents |
|----------|--------|----------| |----------|--------|----------|
| `0x0000` | 8192 | Height map: 64 × 64 `u16`, 8.8 fixed point, row-major. Each cell covers 16 × 8 screen pixels. | | `0x0000` | 8192 | Height map: 64 rows (world y) × 64 `u16` (world x). Each cell is one unit of the game's world; the ground there is value / 64 pixels high. |
| `0x2000` | 8192 | Unused, zero on every track. | | `0x2000` | 8192 | Unused, zero on every track. |
| `0x4000` | 8 | `u16` gate count, `u16` start x, `u16` start y, `u16` flags. | | `0x4000` | 8 | `u16` index of the last gate (so the track has one more gate than this), `u16` start x, `u16` start y, `u16` flags. |
| `0x4008` | 8 × n | Gates: `x1 y1 x2 y2`, world coordinates × 32. The list repeats after the last gate. | | `0x4008` | 70 × 8 | Gate slots: `x1 y1 x2 y2`, world coordinates × 32 (× 512 in the game's own units). Slots after the last gate repeat the list from the start. |
| … | | Unknown data, mostly a bit pattern, up to `0x4A98`. | | `0x4238` | 2144 | Unknown. 88 bytes of mostly doubled bytes, then what looks like leftover picture data. |
| `0x4A98` | 4 × 65536 | Four non-interleaved 1024 × 512 bitplanes, giving 16 colours. | | `0x4A98` | 4 × 65536 | Four non-interleaved 1024 × 512 bitplanes, giving 16 colours. |
The executable replaces the start point and flags of two tracks with
fixed values: (844, 50, flags 1) for file 1's track 2 and (187, 119, flags 0)
for file 2's track 4. It isn't clear whether those track numbers count from
0 or 1.
### Track geometry ### Track geometry
Gates are line segments across the road and run in race order; gate 0 is Gates are line segments across the road and run in race order; gate 0 is
the start/finish line. They're defined on an isometric ground plane rotated the start/finish line. They're defined on an isometric ground plane rotated
45° to the picture. The projection that best fits the data is: 45° to the picture.
The rest of this section uses this port's world units, which are 1/16 of
the game's own unit (the 16.16 fixed-point values the executable works in).
So a height-map cell is 16 × 16 units.
The executable projects a point `z` pixels above the ground plane like
this:
``` ```
screen x = wx - wy + 512 screen x = wx - wy + 512
screen y = (wx + wy) × 31/64 - 252 screen y = (wx + wy) / 2 - 156 - z
``` ```
With these constants, 96% of points sampled along the gates land on road The car sprite position uses 182 in place of 156, putting the sprite's top
pixels across all 24 tracks (92% minimum). A plain screen-aligned 2:1 squash left 26 pixels higher. With `z` set to the ground height, 98% of points
gives 40–70%. Adding a height term doesn't improve the fit, so the height sampled along the gates land on road pixels across all 24 tracks.
map is used only for slope physics and body tilt.
**Walls.** The gates are also the control points of the road edges:
- **The curves:** routine `$24670` evaluates a uniform quadratic B-spline
through the gates' first ends for one edge, and their second ends for
the other. At parameter `i + f` that is
`(1-f)²/2 · G[i] + (1/2 + f - f²) · G[i+1] + f²/2 · G[i+2]`.
- **Zones:** at set-up the game cuts the road into zones, two per gate,
between consecutive cross-sections of the two curves. Each zone's four
sides are tested against the car (routine `$25b2c`).
- **Collisions:** crossing a side edge is a wall hit; crossing the front
or back moves the car on to the next or previous zone.
- **Fit:** points 5% in from either edge are on road pixels 95% of the
time, and points 15% outside only 25% of the time. So the curves follow
the painted road edge.
The stored start point is on the road for 14 of the 24 tracks read as screen The stored start point is on the road for 14 of the 24 tracks read as screen
coordinates, and 10 read as world coordinates. It isn't used; the grid forms coordinates, and 10 read as world coordinates. The game seems to use it for
up behind gate 0. the starting camera position. This port forms the grid up behind gate 0.
### Road and scenery ### Road and scenery
Colours 8–15 are the driving surface on every track, so the fourth Colours 8–15 are the driving surface on every track, so the fourth
bitplane is effectively a road mask. Off the road means less grip and bitplane is effectively a road mask. The game's physics don't read the
lower speed. picture; the walls keep cars on the road.
## Vehicles ## Vehicles
@@ -153,15 +178,56 @@ intact when read directly from the disk image.
- **Header:** the car's name and its driver's name, each followed by a - **Header:** the car's name and its driver's name, each followed by a
newline. newline.
- **Settings:** 16 `u16` values. Eleven are used by the cars; the rest are - **Settings:** 16 `u16` values (see below). The caravan's values aren't
zero. The caravan's values differ in kind; two of them are −4. driving values; two of them are −4.
- **Menu picture:** 512 bytes. A 64 × 32 picture in two bitplanes, plane 0 - **Menu picture:** 512 bytes. A 64 × 32 picture in two bitplanes, plane 0
(256 bytes) then plane 1. (256 bytes) then plane 1.
The settings are clearly handling values. The first is smallest for the Routine `$332a` copies the settings into each car, as 16.16 fixed point
Cow, the second largest for the F1, and the Police car and Porsche are with these shifts. Word `k` is the `k`th `u16`, counting from 0:
nearly identical. What each one means lives in the executable and isn't
decoded yet. | Word | Car field | Value | Meaning |
|------|-----------|-------|---------|
| 0 | `$bc` | w / 64 | drag (computer drivers) |
| 1 | `$e2` | w / 512 | grip (computer drivers) |
| 2 | `$e6` | w / 16 | steering, 64ths of a turn per frame |
| 3 | `$c0` | −w / 64 | stored but never read |
| 4 | `$106` | w / 8 | gravity, pixels per frame² |
| 5 | `$b8` | w / 512 | power (computer drivers) |
| 6 | (towing) | w / 32 | tow hitch distance behind the car, in game units |
| 8 | `$bc` | w / 64 | drag (human drivers) |
| 9 | `$e2` | w / 512 | grip (human drivers) |
| 10 | `$b8` | w / 512 | power (human drivers) |
Words 7 and 11–15 aren't read. [Game logic](#game-logic) shows how drag,
grip and power work together. In short, top speed is
`power × drag / (1 − drag)` game units per frame. For the Camaro that is
271 of this port's units a second for human drivers and 186 for computer
ones.
### Handling presets
The race menu's handling option is CUSTOM (each car's own settings, as
above) or one of six presets that replace them for every car. The table
is at `$21dc6`, 8 bytes each. Bytes 0–2 are for human drivers, 3–5 for
computer drivers, and byte 6 is steering for both:
| Preset | Human | Computer | Steering |
|--------|-------|----------|----------|
| CLASSIC | 98 26 24 | 98 24 24 | 44 |
| GRUNTY | 92 40 30 | 108 33 25 | 60 |
| ZIPPY | 102 61 47 | 115 59 45 | 64 |
| RIPPY | 100 80 60 | 110 50 60 | 72 |
| SLIPPERY | 25 127 101 | 29 45 122 | 68 |
| PEDAL CAR | 117 23 17 | 104 17 10 | 30 |
For a preset `(a, t, g)`:
- drag = a/128 × 0.98 + 0.01
- p = t/16 × (1/drag − 1), and power = p/8
- grip = g/256 / p / drag / 8
- steering = byte 6 / 32, gravity = 0.5
So top speed is simply t/128 game units per frame.
## Sounds (`sfx/*.iff`, `gfx/*.iff`) ## Sounds (`sfx/*.iff`, `gfx/*.iff`)
@@ -208,6 +274,82 @@ The song uses these commands:
module second by second through the whole song (correlation 0.996 over the module second by second through the whole song (correlation 0.996 over the
first minute), and it loops at the same point. first minute), and it loops at the same point.
## Game logic
`SKIDMARKS_CD³²` is one code hunk of 222,032 bytes, compiled from Blitz
Basic 2. Addresses below are offsets into it. All arithmetic is 16.16
fixed point (multiply at `$2ceda`, divide at `$2e122`).
### Car record
Each car is a 266-byte (`$10a`) record:
| Offset | Field |
|--------|-------|
| `$0`, `$4`, `$8` | x, y, height z |
| `$c`, `$10`, `$14` | velocity x, y, vertical |
| `$18`, `$1c` | this frame's velocity change |
| `$58`, `$5c` | heading (64 per turn) and turn rate |
| `$60`, `$64` | heading vector, from sine/cosine tables of 64 entries |
| `$68` | computer steering, −1, 0 or 1 |
| `$6c`, `$6e` | pitch and roll notch, 0–4 |
| `$76` | controller: 0–7 for joysticks and keyboards, 8 for computer |
| `$78` | sprite frame: roll × 160 + pitch × 32 + heading / 2 |
| `$7a` | airborne |
| `$7e` | state: negative while racing, 1 while warping |
| `$a6` | engine speed |
| `$aa` | forward speed |
| `$b8`, `$bc`, `$e2`, `$e6`, `$106` | handling (above) |
| `$d2`, `$d6` | links to the car towing this one, and the one it tows |
| `$ea` | zone (half-gate) progress |
### One frame
The race loop runs at 50 Hz. Each car gets steering (`$1c618`), drawing
(`$1cafa`), and moving (`$1982c`) in turn. While drawing, a car sprite
is skipped if the beam is already past a given line, so a frame always
fits in one field. Then `$247ac` integrates every car.
1. **Steering:**
- Input is −1, 0 or 1, from the joystick or the computer driver.
- turn = (turn + input × steering) / 2. Airborne cars can't steer, and
their turn rate is limited to ±1.
2. **Height:**
- vertical −= gravity.
- The car looks two frames ahead: if `z + 2 × vertical` is still above
the ground at `pos + 2 × velocity`, it's airborne.
- Otherwise vertical = (ground there − z) / 2.
- Pitch and roll each step one notch a frame towards
`floor(rise + 2.5)`, clamped to 0–4. The rise is the ground height
change over 0.2 units along world y (pitch) or world −x (roll).
- heading += turn.
3. **Engine and grip**, skipped while airborne:
- throttle is −1, 0 or 1. Half the vertical speed is taken off it
going forwards (added going backwards), so hills slow cars down and
speed them up.
- target = (engine speed + throttle × power) × drag
- change = grip × (heading vector × target − velocity)
- engine speed += (target − engine speed) / 4
4. **Walls:**
- The zone test runs on `pos + 4 × velocity + 10 × change`.
- On a wall the velocity points back off it. The car is held at the
contact point and loses 1.5× its velocity into the wall, so it
bounces back at half speed.
- Crossing a zone's front or back moves `$ea` on, and counts the laps.
5. **Integrate:** velocity += change, position += velocity, z += vertical.
A warping car (state 1) instead flies straight at a point on the track:
its velocity is the distance to it / 16.
### Computer drivers
Computer cars use words 0, 1 and 5 for handling rather than 8, 9 and 10.
They steer with the sign of the cross product between their heading and
the direction to a target point, as if holding a joystick hard over. The
targets come from a table of points built along the road. The driver
picks one side of the road or the other depending on which way the road
bends, and the throttle comes from fixed values.
## Executables ## Executables
- **`SKIDMARKS_CD³²`** (237 KB): a single-hunk compiled Blitz Basic 2 - **`SKIDMARKS_CD³²`** (237 KB): a single-hunk compiled Blitz Basic 2
@@ -217,12 +359,16 @@ first minute), and it loops at the same point.
## Gaps ## Gaps
- **Game logic.** The handling, AI and rules in the executable aren't - **Game logic still to decode:**
decoded; this port's versions are reconstructions. Decoding the `.set` - car-to-car collisions
values would be the first step towards the original handling. - exactly how computer drivers pick target points and throttle
- **Unknown data:** the 12 bytes after each track file's palette, the bit - the warp rules
pattern after the gates in each track section, and the colours of - the towing physics, beyond the hitch distance
`ball.spr` (probably set by the executable). - the race rules and scoring
- **Unknown data:**
- the six per-track words after each track file's palette
- the 2144 bytes after the gate slots in each track section
- the colours of `ball.spr`, probably set by the executable
The engine's own editable formats, which `skids extract` converts all of The engine's own editable formats, which `skids extract` converts all of
the above into, are described in [assets/README.md](../assets/README.md). the above into, are described in [assets/README.md](../assets/README.md).
+19 -3
View File
@@ -60,6 +60,8 @@ pub struct Settings {
pub opponents: usize, pub opponents: usize,
/// Every car tows a caravan. /// Every car tows a caravan.
pub caravans: bool, pub caravans: bool,
/// 0 = each car's own handling, else a preset (`vehicle::PRESETS`).
pub handling: usize,
pub music: Music, pub music: Music,
} }
@@ -73,6 +75,7 @@ enum Item {
Car2, Car2,
Opponents, Opponents,
Caravans, Caravans,
Handling,
Music, Music,
Quit, Quit,
} }
@@ -150,6 +153,7 @@ impl App {
cars, cars,
opponents: 3, opponents: 3,
caravans: false, caravans: false,
handling: 0,
music, music,
}, },
screen: Screen::Menu { cursor: 0 }, screen: Screen::Menu { cursor: 0 },
@@ -179,6 +183,7 @@ impl App {
if self.data.vehicles.iter().any(|v| v.trailer) { if self.data.vehicles.iter().any(|v| v.trailer) {
v.push(Item::Caravans); v.push(Item::Caravans);
} }
v.push(Item::Handling);
if self.audio.has_music() { if self.audio.has_music() {
v.push(Item::Music); v.push(Item::Music);
} }
@@ -209,6 +214,7 @@ impl App {
team: p, team: p,
name: format!("PLAYER {}", p + 1), name: format!("PLAYER {}", p + 1),
trailer, trailer,
preset: self.settings.handling,
}); });
} }
let mut rng = seed.wrapping_mul(2654435761) | 1; let mut rng = seed.wrapping_mul(2654435761) | 1;
@@ -222,6 +228,7 @@ impl App {
team: humans + i, team: humans + i,
name: CPU_NAMES[(i + seed as usize) % CPU_NAMES.len()].to_string(), name: CPU_NAMES[(i + seed as usize) % CPU_NAMES.len()].to_string(),
trailer, trailer,
preset: self.settings.handling,
}); });
} }
// Humans start at the back, as is tradition. // Humans start at the back, as is tradition.
@@ -347,6 +354,7 @@ impl App {
} }
Item::Opponents => s.opponents = wrap(s.opponents, 8 - s.players + 1, delta), Item::Opponents => s.opponents = wrap(s.opponents, 8 - s.players + 1, delta),
Item::Caravans => s.caravans = !s.caravans, Item::Caravans => s.caravans = !s.caravans,
Item::Handling => s.handling = wrap(s.handling, vehicle::PRESETS.len() + 1, delta),
Item::Music => { Item::Music => {
const ORDER: [Music; 3] = [Music::Menus, Music::Always, Music::Off]; const ORDER: [Music; 3] = [Music::Menus, Music::Always, Music::Off];
let at = ORDER.iter().position(|&m| m == s.music).unwrap_or(0); let at = ORDER.iter().position(|&m| m == s.music).unwrap_or(0);
@@ -370,6 +378,7 @@ impl App {
| Item::Laps | Item::Laps
| Item::Players | Item::Players
| Item::Caravans | Item::Caravans
| Item::Handling
| Item::Music | Item::Music
| Item::Opponents => { | Item::Opponents => {
let s = &self.settings; let s = &self.settings;
@@ -505,6 +514,13 @@ impl App {
Item::Caravans => { Item::Caravans => {
format!("CARAVANS < {} >", if s.caravans { "ON" } else { "OFF" }) format!("CARAVANS < {} >", if s.caravans { "ON" } else { "OFF" })
} }
Item::Handling => format!(
"HANDLING < {} >",
match s.handling {
0 => "CUSTOM",
n => vehicle::PRESETS[n - 1].0,
}
),
Item::Music => format!( Item::Music => format!(
"MUSIC < {} >", "MUSIC < {} >",
match s.music { match s.music {
@@ -729,7 +745,7 @@ pub fn draw_race(
}; };
let e = &race.entrants[ei]; let e = &race.entrants[ei];
// Look ahead in the direction of travel. // Look ahead in the direction of travel.
let target = track::to_screen(e.car.x + e.car.vx * 0.3, e.car.y + e.car.vy * 0.3); let target = track::project(e.car.x + e.car.vx * 0.3, e.car.y + e.car.vy * 0.3, e.car.z);
cams[p].0 += (target.0 - cams[p].0) * 0.12; cams[p].0 += (target.0 - cams[p].0) * 0.12;
cams[p].1 += (target.1 - cams[p].1) * 0.12; cams[p].1 += (target.1 - cams[p].1) * 0.12;
draw_world(f, data, race, *view, &mut cams[p], false, 1.0); draw_world(f, data, race, *view, &mut cams[p], false, 1.0);
@@ -774,7 +790,7 @@ pub fn draw_world(
for (i, e) in race.entrants.iter().enumerate() { for (i, e) in race.entrants.iter().enumerate() {
order.push((e.car.screen_pos().1, i, false)); order.push((e.car.screen_pos().1, i, false));
if let Some(t) = e.trailer { if let Some(t) = e.trailer {
order.push((track::to_screen(t.x, t.y).1, i, true)); order.push((race.track.to_screen(t.x, t.y).1, i, true));
} }
} }
order.sort_by(|a, b| a.0.total_cmp(&b.0)); order.sort_by(|a, b| a.0.total_cmp(&b.0));
@@ -784,7 +800,7 @@ pub fn draw_world(
let pal = palette::car_palette(c.team); let pal = palette::car_palette(c.team);
if is_trailer { if is_trailer {
let t = e.trailer.unwrap(); let t = e.trailer.unwrap();
let (sx, sy) = track::to_screen(t.x, t.y); let (sx, sy) = race.track.to_screen(t.x, t.y);
let x = view.x + sx as i32 - cx - (vehicle::SPR_W as i32 / 2); let x = view.x + sx as i32 - cx - (vehicle::SPR_W as i32 / 2);
let y = view.y + sy as i32 - cy - (vehicle::SPR_H as i32 / 2); let y = view.y + sy as i32 - cy - (vehicle::SPR_H as i32 / 2);
let pix = let pix =
+124 -35
View File
@@ -13,18 +13,27 @@
//! name of the set of tracks, NUL-terminated. //! name of the set of tracks, NUL-terminated.
//! //!
//! Each track section (281240 bytes once decrunched): //! Each track section (281240 bytes once decrunched):
//! 0x0000 height map, 64x64 BE16 8.8 fixed point, one cell per 16x8 pixels //! 0x0000 height map, 64x64 BE16, row-major by world y then x. Each cell
//! is one unit of the original's world (16 of ours); the ground
//! stands value / 64 pixels above the plane
//! 0x2000 8192 bytes, unused (always zero on the retail disks) //! 0x2000 8192 bytes, unused (always zero on the retail disks)
//! 0x4000 BE16 gate count, start x, start y (screen pixels), flags //! 0x4000 BE16 last gate index (one less than the gate count), start x,
//! 0x4008 gates: x1 y1 x2 y2 BE16, world coordinates in 1/32 units //! start y (screen pixels), flags
//! 0x4008 70 gate slots: x1 y1 x2 y2 BE16, world coordinates in 1/32
//! units. Slots past the last gate repeat the list.
//! 0x4A98 four 1024x512 bitplanes (65536 bytes each), giving 16 colours //! 0x4A98 four 1024x512 bitplanes (65536 bytes each), giving 16 colours
//! //!
//! The world is an isometric ground plane rotated 45 degrees to the picture: //! The world is an isometric ground plane rotated 45 degrees to the picture.
//! screen x = wx - wy + 512, screen y = (wx + wy) * 31/64 - 252. The constants //! The executable projects a point at height z (pixels) with
//! were fitted so that 96% of gate samples over all 24 tracks land on road //! screen x = wx - wy + 512, screen y = (wx + wy) / 2 - 156 - z, which puts
//! pixels (against 40-70% for a plain 2:1 squash). The stored start point //! 98% of gate samples over all 24 tracks on road pixels once z is the
//! only lands on the road on 14 of the 24 tracks, so the grid is placed from //! ground height. The stored start point only lands on the road on 14 of
//! the gates instead. //! the 24 tracks, so the grid is placed from the gates instead.
//!
//! The gates are also the control points of the road edges: the executable
//! runs a uniform quadratic B-spline through the gates' first ends for one
//! edge and their second ends for the other, and those curves are the
//! walls. They follow the painted road edge closely (see `Track::edges`).
use super::image; use super::image;
use super::palette::{self, Rgb}; use super::palette::{self, Rgb};
@@ -35,19 +44,24 @@ pub const W: usize = 1024;
pub const H: usize = 512; pub const H: usize = 512;
pub const HM_W: usize = 64; pub const HM_W: usize = 64;
pub const HM_H: usize = 64; pub const HM_H: usize = 64;
pub const CELL_W: f32 = (W / HM_W) as f32; /// World units per height-map cell: one unit of the original's world.
pub const CELL_H: f32 = (H / HM_H) as f32; pub const CELL: f32 = 16.0;
/// Samples per gate along the road edges.
pub const EDGE_STEPS: usize = 8;
const SECTION_SIZE: usize = 0x44a98; const SECTION_SIZE: usize = 0x44a98;
const GATES_AT: usize = 0x4000; const GATES_AT: usize = 0x4000;
/// Room for gates in a track section; slots past the last gate repeat the
/// list from the start.
const GATE_SLOTS: usize = 70;
const PLANES_AT: usize = 0x4a98; const PLANES_AT: usize = 0x4a98;
const PLANE_SIZE: usize = W * H / 8; const PLANE_SIZE: usize = W * H / 8;
const SECTIONS: usize = 7; const SECTIONS: usize = 7;
const ISO_X0: f32 = 512.0; const ISO_X0: f32 = 512.0;
const ISO_Y_SCALE: f32 = 31.0 / 64.0; const ISO_Y0: f32 = -156.0;
const ISO_Y0: f32 = -252.0;
pub fn to_screen(wx: f32, wy: f32) -> (f32, f32) { /// Screen position of a world point `z` pixels above the ground plane.
(wx - wy + ISO_X0, (wx + wy) * ISO_Y_SCALE + ISO_Y0) pub fn project(wx: f32, wy: f32, z: f32) -> (f32, f32) {
(wx - wy + ISO_X0, (wx + wy) * 0.5 + ISO_Y0 - z)
} }
pub fn on_screen(sx: f32, sy: f32, margin: f32) -> bool { pub fn on_screen(sx: f32, sy: f32, margin: f32) -> bool {
@@ -89,10 +103,13 @@ pub struct Track {
/// Palette indices, W x H. /// Palette indices, W x H.
pub pixels: Vec<u8>, pub pixels: Vec<u8>,
pub palette: [Rgb; 16], pub palette: [Rgb; 16],
/// Height samples in 8.8 fixed point. /// Height samples; the ground is value / 64 pixels high.
pub height: Vec<u16>, pub height: Vec<u16>,
/// Gates in world coordinates. /// Gates in world coordinates.
pub gates: Vec<Gate>, pub gates: Vec<Gate>,
/// The road edges sampled EDGE_STEPS times per gate, as a closed loop
/// of cross-sections (one point on each edge). Built from the gates.
pub edges: Vec<Gate>,
/// Start position and flags as stored. Kept for reference: the start /// Start position and flags as stored. Kept for reference: the start
/// point does not line up with the road reliably, and the flags' meaning /// point does not line up with the road reliably, and the flags' meaning
/// is unknown. /// is unknown.
@@ -201,10 +218,11 @@ impl Track {
} }
let height = (0..HM_W * HM_H).map(|i| be16(d, i * 2)).collect(); let height = (0..HM_W * HM_H).map(|i| be16(d, i * 2)).collect();
let count = be16(d, GATES_AT) as usize; // The stored value is the index of the last gate.
let count = be16(d, GATES_AT) as usize + 1;
let start = (be16(d, GATES_AT + 2) as f32, be16(d, GATES_AT + 4) as f32); let start = (be16(d, GATES_AT + 2) as f32, be16(d, GATES_AT + 4) as f32);
let flags = be16(d, GATES_AT + 6); let flags = be16(d, GATES_AT + 6);
if count == 0 || GATES_AT + 8 + count * 8 > PLANES_AT { if count > GATE_SLOTS {
return Err(format!("implausible gate count {count}")); return Err(format!("implausible gate count {count}"));
} }
let gates = (0..count) let gates = (0..count)
@@ -242,28 +260,35 @@ impl Track {
thumbnail: None, thumbnail: None,
height, height,
gates, gates,
edges: Vec::new(),
start, start,
flags, flags,
}) }
.with_edges())
} }
/// Ground height (in height units) at a screen position, bilinear. fn with_edges(mut self) -> Track {
pub fn height_at(&self, sx: f32, sy: f32) -> f32 { self.edges = edges(&self.gates);
let fx = (sx / CELL_W - 0.5).clamp(0.0, (HM_W - 1) as f32); self
let fy = (sy / CELL_H - 0.5).clamp(0.0, (HM_H - 1) as f32); }
/// Ground height in pixels at a world position, bilinear between cells
/// as the executable does.
pub fn ground(&self, wx: f32, wy: f32) -> f32 {
let fx = (wx / CELL).clamp(0.0, (HM_W - 1) as f32);
let fy = (wy / CELL).clamp(0.0, (HM_H - 1) as f32);
let (x0, y0) = (fx as usize, fy as usize); let (x0, y0) = (fx as usize, fy as usize);
let (x1, y1) = ((x0 + 1).min(HM_W - 1), (y0 + 1).min(HM_H - 1)); let (x1, y1) = ((x0 + 1).min(HM_W - 1), (y0 + 1).min(HM_H - 1));
let (tx, ty) = (fx - x0 as f32, fy - y0 as f32); let (tx, ty) = (fx - x0 as f32, fy - y0 as f32);
let h = |x: usize, y: usize| self.height[y * HM_W + x] as f32 / 256.0; let h = |x: usize, y: usize| self.height[y * HM_W + x] as f32 / 64.0;
let top = h(x0, y0) * (1.0 - tx) + h(x1, y0) * tx; let top = h(x0, y0) * (1.0 - tx) + h(x1, y0) * tx;
let bot = h(x0, y1) * (1.0 - tx) + h(x1, y1) * tx; let bot = h(x0, y1) * (1.0 - tx) + h(x1, y1) * tx;
top * (1.0 - ty) + bot * ty top * (1.0 - ty) + bot * ty
} }
/// Height at a world position. /// Screen position of a point on the ground.
pub fn height_world(&self, wx: f32, wy: f32) -> f32 { pub fn to_screen(&self, wx: f32, wy: f32) -> (f32, f32) {
let (sx, sy) = to_screen(wx, wy); project(wx, wy, self.ground(wx, wy))
self.height_at(sx, sy)
} }
pub fn pixel(&self, sx: f32, sy: f32) -> u8 { pub fn pixel(&self, sx: f32, sy: f32) -> u8 {
@@ -279,7 +304,7 @@ impl Track {
} }
pub fn on_road_world(&self, wx: f32, wy: f32) -> bool { pub fn on_road_world(&self, wx: f32, wy: f32) -> bool {
let (sx, sy) = to_screen(wx, wy); let (sx, sy) = self.to_screen(wx, wy);
on_screen(sx, sy, 0.0) && self.on_road(sx, sy) on_screen(sx, sy, 0.0) && self.on_road(sx, sy)
} }
@@ -382,9 +407,11 @@ impl Track {
thumbnail: None, thumbnail: None,
height, height,
gates, gates,
edges: Vec::new(),
start, start,
flags, flags,
}) }
.with_edges())
} }
/// The thumbnail as an indexed PNG in the track's palette. /// The thumbnail as an indexed PNG in the track's palette.
@@ -414,11 +441,14 @@ impl Track {
"# Skids track {}. Lines starting with # are comments.\n\ "# Skids track {}. Lines starting with # are comments.\n\
#\n\ #\n\
# The picture is track.png: 1024x512, 16 colours. Its palette is the\n\ # The picture is track.png: 1024x512, 16 colours. Its palette is the\n\
# track's palette. Colours 8-15 are road (full grip), 0-7 are scenery.\n\ # track's palette. Colours 8-15 are road, 0-7 are scenery (dusty).\n\
#\n\ #\n\
# gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is\n\ # gate x1 y1 x2 y2: lap gates in race order, in world units. Gate 0 is\n\
# the start/finish line; the grid forms up behind it. World to picture:\n\ # the start/finish line; the grid forms up behind it. The gates' ends\n\
# picture x = wx - wy + 512, picture y = (wx + wy) * 31/64 - 252\n", # also shape the walls: each edge of the road is a smooth curve\n\
# through the matching ends of the gates. World to picture, for a\n\
# point at height z (pixels):\n\
# picture x = wx - wy + 512, picture y = (wx + wy) / 2 - 156 - z\n",
self.name self.name
); );
for g in &self.gates { for g in &self.gates {
@@ -427,8 +457,8 @@ impl Track {
t.push_str(&format!( t.push_str(&format!(
"\n# Name of the set of tracks this one belongs to, shown in the menu.\nset {}\n\n\ "\n# Name of the set of tracks this one belongs to, shown in the menu.\nset {}\n\n\
# As stored in the original data; not used by the game.\nstart {} {}\nflags {}\n\n\ # As stored in the original data; not used by the game.\nstart {} {}\nflags {}\n\n\
# height: 64 rows of 64 values, ground height x 256. Each value covers\n\ # height: 64 rows (world y) of 64 values (world x), ground height in\n\
# 16x8 pixels of the picture.\nheight\n", # pixels x 64. Each value covers 16 x 16 world units.\nheight\n",
self.set_name, self.start.0, self.start.1, self.flags self.set_name, self.start.0, self.start.1, self.flags
)); ));
for row in self.height.chunks(HM_W) { for row in self.height.chunks(HM_W) {
@@ -444,3 +474,62 @@ impl Track {
self.pixels.iter().map(|&i| pal[i as usize]).collect() self.pixels.iter().map(|&i| pal[i as usize]).collect()
} }
} }
/// The road edges: a uniform quadratic B-spline through the gates' ends,
/// sampled EDGE_STEPS times per gate. Sample k * EDGE_STEPS lies halfway
/// between gates k and k + 1, as in the executable.
pub fn edges(gates: &[Gate]) -> Vec<Gate> {
let n = gates.len();
if n < 3 {
return gates.to_vec();
}
let mut out = Vec::with_capacity(n * EDGE_STEPS);
for i in 0..n {
let (p0, p1, p2) = (gates[i], gates[(i + 1) % n], gates[(i + 2) % n]);
for s in 0..EDGE_STEPS {
let f = s as f32 / EDGE_STEPS as f32;
let (w0, w2) = ((1.0 - f) * (1.0 - f) * 0.5, f * f * 0.5);
let w1 = 1.0 - w0 - w2;
let mix = |a: (f32, f32), b: (f32, f32), c: (f32, f32)| {
(
a.0 * w0 + b.0 * w1 + c.0 * w2,
a.1 * w0 + b.1 * w1 + c.1 * w2,
)
};
out.push(Gate {
a: mix(p0.a, p1.a, p2.a),
b: mix(p0.b, p1.b, p2.b),
});
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn edges_run_between_gate_midpoints() {
let g = |x: f32| Gate {
a: (x, 0.0),
b: (x, 10.0),
};
let gates = [g(0.0), g(10.0), g(30.0), g(60.0)];
let e = edges(&gates);
assert_eq!(e.len(), gates.len() * EDGE_STEPS);
// Each gate's run of samples starts halfway between it and the next.
assert_eq!(e[0].a, (5.0, 0.0));
assert_eq!(e[EDGE_STEPS].b, (20.0, 10.0));
// In between, the curve stays inside the control polygon.
assert!(e[EDGE_STEPS / 2].a.0 > 5.0 && e[EDGE_STEPS / 2].a.0 < 20.0);
}
#[test]
fn projection_lifts_by_height() {
let (x0, y0) = project(100.0, 50.0, 0.0);
let (x1, y1) = project(100.0, 50.0, 10.0);
assert_eq!((x0, y0), (562.0, -81.0));
assert_eq!((x1, y1), (x0, y0 - 10.0));
}
}
+197 -168
View File
@@ -7,9 +7,9 @@
//! file holds the display name and handling. //! file holds the display name and handling.
//! //!
//! Settings (`vehicles/*.set`, disk 1): car name and driver name, each //! Settings (`vehicles/*.set`, disk 1): car name and driver name, each
//! ending in a newline, then 544 bytes: 16 BE16 values (11 used; their //! ending in a newline, then 544 bytes: 16 BE16 handling values (see
//! meaning lives in the executable and is not decoded yet) and a 64x32 //! `SetValues`) and a 64x32 two-colour-plane picture for the menu, plane 0
//! two-colour-plane picture for the menu, plane 0 then plane 1. //! then plane 1.
//! //!
//! Original data (`vehicles/*.aga`): //! Original data (`vehicles/*.aga`):
//! 409600 bytes once decrunched: 25 tilt blocks x 32 headings x 512 bytes. //! 409600 bytes once decrunched: 25 tilt blocks x 32 headings x 512 bytes.
@@ -35,105 +35,135 @@ const BANK_BYTES: usize = TILTS * HEADINGS * FRAME_BYTES;
pub const SHEET_W: usize = SPR_W * HEADINGS; pub const SHEET_W: usize = SPR_W * HEADINGS;
pub const SHEET_H: usize = SPR_H * TILTS; pub const SHEET_H: usize = SPR_H * TILTS;
/// Handling. The retail executable is not available, so these values are a /// The 16 handling values of a `.set` file. Their meaning, from the
/// reconstruction; edit them in the vehicle's `.txt` asset. /// executable's set-up code (word k is `set[k]`):
///
/// | Word | Use |
/// |------|-----|
/// | 0, 1, 5 | computer drivers' drag, grip and power |
/// | 2 | steering rate |
/// | 3 | read into the car but never used |
/// | 4 | gravity |
/// | 6 | tow hitch distance behind the car |
/// | 8, 9, 10 | human drivers' drag, grip and power |
///
/// Words 7 and 11-15 aren't read.
pub type SetValues = [u16; 16];
/// The Camaro's values, for vehicles that come without any.
pub const DEFAULT_SET: SetValues = [54, 35, 32, 8, 7, 22, 22, 16, 49, 14, 53, 0, 0, 0, 0, 0];
/// One driver profile. The original's per-frame update (in its own units,
/// where one world unit is a height-map cell) is:
///
/// ```text
/// target = (speed + throttle * power) * drag
/// velocity += grip * (heading * target - velocity)
/// speed += (target - speed) / 4
/// ```
///
/// so top speed is `power * drag / (1 - drag)` per frame, and `grip` is
/// the fraction of the gap between where the car points and where it is
/// going that closes each frame.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Spec { pub struct Handling {
/// Top speed on tarmac, world pixels per second. pub drag: f32,
pub top: f32,
pub accel: f32,
pub brake: f32,
/// Lateral grip: how quickly sideways motion is killed (1/s).
pub grip: f32, pub grip: f32,
/// Steering rate at speed (rad/s). pub power: f32,
pub turn: f32,
pub mass: f32,
/// Speed and grip multiplier off the road.
pub offroad: f32,
} }
pub const BASE_SPEC: Spec = Spec { #[derive(Clone, Copy, Debug, PartialEq)]
top: 200.0, pub struct Spec {
accel: 170.0, pub player: Handling,
brake: 320.0, pub computer: Handling,
grip: 5.0, /// Steering at full lock, in 64ths of a turn per frame.
turn: 3.6, pub turn: f32,
mass: 1.0, /// Pixels per frame per frame.
offroad: 0.55, pub gravity: f32,
}; /// Tow hitch distance behind the car's centre, world units.
pub hitch: f32,
/// Weight in collisions between cars. Not from the original data.
pub mass: f32,
}
/// Starting values used when importing the original data. impl Spec {
pub fn default_spec(id: &str) -> Spec { /// Decodes the values as the executable does (fixed-point shifts).
let b = BASE_SPEC; pub fn from_set(v: &SetValues, mass: f32) -> Spec {
let f = |k: usize, shift: u32| v[k] as i16 as f32 / (1u32 << shift) as f32;
Spec {
player: Handling {
drag: f(8, 6),
grip: f(9, 9),
power: f(10, 9),
},
computer: Handling {
drag: f(0, 6),
grip: f(1, 9),
power: f(5, 9),
},
turn: f(2, 4),
gravity: f(4, 3),
hitch: f(6, 5) * super::track::CELL,
mass,
}
}
}
/// The original's handling presets, which replace every car's own values
/// (its "CUSTOM" setting). Bytes 0-2 are drag, top speed and grip for
/// human drivers, 3-5 the same for computer drivers, and 6 is steering.
pub const PRESETS: [(&str, [u8; 7]); 6] = [
("CLASSIC", [98, 26, 24, 98, 24, 24, 44]),
("GRUNTY", [92, 40, 30, 108, 33, 25, 60]),
("ZIPPY", [102, 61, 47, 115, 59, 45, 64]),
("RIPPY", [100, 80, 60, 110, 50, 60, 72]),
("SLIPPERY", [25, 127, 101, 29, 45, 122, 68]),
("PEDAL CAR", [117, 23, 17, 104, 17, 10, 30]),
];
impl Handling {
/// Decodes a preset's three bytes as the executable does: top speed
/// comes out at `top / 128` world units per frame.
fn from_preset(drag: u8, top: u8, grip: u8) -> Handling {
let drag = drag as f32 / 128.0 * 0.98 + 0.01;
let top = top as f32 / 16.0;
let power = top * (1.0 / drag - 1.0);
Handling {
drag,
grip: grip as f32 / 256.0 / power / drag / 8.0,
power: power / 8.0,
}
}
}
impl Spec {
/// The spec with preset `n` (1-based, as in the original's menu; 0
/// keeps the car's own values).
pub fn with_preset(self, n: usize) -> Spec {
let Some((_, b)) = n.checked_sub(1).and_then(|i| PRESETS.get(i)) else {
return self;
};
Spec {
player: Handling::from_preset(b[0], b[1], b[2]),
computer: Handling::from_preset(b[3], b[4], b[5]),
turn: b[6] as f32 / 32.0,
gravity: 0.5,
..self
}
}
}
/// Collision weight. The original's car-to-car physics isn't decoded, so
/// these are guesses.
pub fn default_mass(id: &str) -> f32 {
match id { match id {
"f1" => Spec { "f1" => 0.9,
top: 240.0, "mini" => 0.8,
accel: 210.0, "midget" => 0.85,
grip: 6.5, "truck" => 1.8,
turn: 3.7, "police" => 1.2,
mass: 0.9, "cow" => 1.4,
offroad: 0.45, _ => 1.0,
..b
},
"porsche" => Spec {
top: 225.0,
accel: 190.0,
grip: 5.2,
turn: 3.6,
..b
},
"mini" => Spec {
top: 190.0,
accel: 200.0,
grip: 5.8,
turn: 4.4,
mass: 0.8,
..b
},
"vw" => Spec {
top: 185.0,
accel: 160.0,
grip: 4.6,
turn: 3.6,
mass: 1.0,
..b
},
"midget" => Spec {
top: 205.0,
accel: 185.0,
grip: 4.0,
turn: 4.0,
mass: 0.85,
offroad: 0.6,
..b
},
"truck" => Spec {
top: 195.0,
accel: 140.0,
grip: 4.2,
turn: 3.0,
mass: 1.8,
offroad: 0.75,
..b
},
"police" => Spec {
top: 215.0,
accel: 180.0,
grip: 5.0,
turn: 3.5,
mass: 1.2,
..b
},
"cow" => Spec {
top: 170.0,
accel: 150.0,
grip: 7.0,
turn: 4.6,
mass: 1.4,
offroad: 0.9,
..b
},
_ => b,
} }
} }
@@ -143,8 +173,8 @@ pub struct Vehicle {
pub driver: String, pub driver: String,
/// Towed behind cars rather than driven (the caravan). /// Towed behind cars rather than driven (the caravan).
pub trailer: bool, pub trailer: bool,
/// The 16 values from the original `.set` file, kept for reference. /// The 16 handling values from the original `.set` file.
pub original: Vec<u16>, pub set: SetValues,
/// 64x32 menu picture (indices 0-3), from the `.set` file. /// 64x32 menu picture (indices 0-3), from the `.set` file.
pub menu_picture: Option<Vec<u8>>, pub menu_picture: Option<Vec<u8>>,
pub spec: Spec, pub spec: Spec,
@@ -180,9 +210,9 @@ impl Vehicle {
name: display_name(&id), name: display_name(&id),
driver: String::new(), driver: String::new(),
trailer: id == "caravan", trailer: id == "caravan",
original: Vec::new(), set: DEFAULT_SET,
menu_picture: None, menu_picture: None,
spec: default_spec(&id), spec: Spec::from_set(&DEFAULT_SET, default_mass(&id)),
id, id,
frames, frames,
})) }))
@@ -202,7 +232,10 @@ impl Vehicle {
self.name = name; self.name = name;
} }
self.driver = crate::formats::latin1(&d[a + 1..b]).trim().to_uppercase(); self.driver = crate::formats::latin1(&d[a + 1..b]).trim().to_uppercase();
self.original = (0..16).map(|i| crate::formats::be16(body, i * 2)).collect(); for (i, v) in self.set.iter_mut().enumerate() {
*v = crate::formats::be16(body, i * 2);
}
self.spec = Spec::from_set(&self.set, self.spec.mass);
let pic = &body[32..32 + 512]; let pic = &body[32..32 + 512];
let mut px = vec![0u8; SPR_W * SPR_H]; let mut px = vec![0u8; SPR_W * SPR_H];
for (i, p) in px.iter_mut().enumerate() { for (i, p) in px.iter_mut().enumerate() {
@@ -258,9 +291,9 @@ impl Vehicle {
name: display_name(id), name: display_name(id),
driver: String::new(), driver: String::new(),
trailer: id == "caravan", trailer: id == "caravan",
original: Vec::new(), set: DEFAULT_SET,
menu_picture: None, menu_picture: None,
spec: default_spec(id), spec: Spec::from_set(&DEFAULT_SET, default_mass(id)),
frames, frames,
}; };
if let Some(txt) = txt { if let Some(txt) = txt {
@@ -284,7 +317,6 @@ impl Vehicle {
} }
pub fn settings_text(&self) -> String { pub fn settings_text(&self) -> String {
let s = &self.spec;
format!( format!(
"# Skids vehicle settings. Lines starting with # are comments.\n\ "# Skids vehicle settings. Lines starting with # are comments.\n\
# The sprite sheet is {id}.png: 32 headings across, 25 tilts down.\n\ # The sprite sheet is {id}.png: 32 headings across, 25 tilts down.\n\
@@ -292,40 +324,28 @@ impl Vehicle {
driver {driver}\n\ driver {driver}\n\
# yes = towed behind cars (Caravans option) instead of driven\n\ # yes = towed behind cars (Caravans option) instead of driven\n\
trailer {trailer}\n\ trailer {trailer}\n\
# The 16 values from the original .set file. Their meaning is not\n\ #\n\
# decoded yet, so the game does not use them; the handling below is\n\ # handling: the 16 values from the original .set file, used as the\n\
# a reconstruction.\n\ # original game does. Counting from 1:\n\
original {original}\n\ # 1 2 6 computer drivers: drag, grip, power\n\
# top speed on tarmac, world pixels per second\n\ # 9 10 11 human drivers: drag, grip, power\n\
top {top}\n\ # 3 steering, 5 gravity, 7 tow hitch distance; the rest are unused.\n\
# acceleration and braking, world pixels per second squared\n\ # Top speed = power * drag / (64 - drag) / 32 world units per frame,\n\
accel {accel}\n\ # 50 frames a second (drag below 64). Higher grip, less sliding.\n\
brake {brake}\n\ handling {set}\n\
# sideways grip (higher = less sliding)\n\ # weight when cars collide (not from the original data)\n\
grip {grip}\n\ mass {mass}\n",
# steering rate, radians per second\n\
turn {turn}\n\
# weight in collisions\n\
mass {mass}\n\
# speed and grip multiplier off the road\n\
offroad {offroad}\n",
id = self.id, id = self.id,
name = self.name, name = self.name,
driver = self.driver, driver = self.driver,
trailer = if self.trailer { "yes" } else { "no" }, trailer = if self.trailer { "yes" } else { "no" },
original = self set = self
.original .set
.iter() .iter()
.map(|v| v.to_string()) .map(|v| v.to_string())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" "), .join(" "),
top = s.top, mass = self.spec.mass,
accel = s.accel,
brake = s.brake,
grip = s.grip,
turn = s.turn,
mass = s.mass,
offroad = s.offroad
) )
} }
@@ -337,43 +357,36 @@ impl Vehicle {
} }
let (key, val) = line.split_once(char::is_whitespace).unwrap_or((line, "")); let (key, val) = line.split_once(char::is_whitespace).unwrap_or((line, ""));
let val = val.trim(); let val = val.trim();
let bad = |what: &str| format!("line {}: {what}", n + 1);
match key { match key {
"name" => { "name" => self.name = val.to_uppercase(),
self.name = val.to_uppercase(); "driver" => self.driver = val.to_uppercase(),
continue; "trailer" => self.trailer = matches!(val, "yes" | "true" | "1"),
} // `original` is what older asset files called it.
"driver" => { "handling" | "original" => {
self.driver = val.to_uppercase(); let vals: Vec<u16> = val
continue;
}
"trailer" => {
self.trailer = matches!(val, "yes" | "true" | "1");
continue;
}
"original" => {
self.original = val
.split_whitespace() .split_whitespace()
.filter_map(|x| x.parse().ok()) .map(|x| x.parse::<u16>())
.collect(); .collect::<Result<_, _>>()
continue; .map_err(|_| bad("handling values must be whole numbers 0-65535"))?;
if vals.len() > 16 {
return Err(bad("handling has more than 16 values"));
}
self.set = [0; 16];
self.set[..vals.len()].copy_from_slice(&vals);
} }
_ => {} "mass" => {
} self.spec.mass = val
let num: f32 = val .parse::<f32>()
.parse() .map_err(|_| bad(&format!("`{val}` is not a number")))?
.map_err(|_| format!("line {}: `{val}` is not a number", n + 1))?; .max(0.05)
let s = &mut self.spec; }
match key { // Settings from before the original handling was decoded.
"top" => s.top = num, "top" | "accel" | "brake" | "grip" | "turn" | "offroad" => {}
"accel" => s.accel = num, _ => return Err(bad(&format!("unknown setting `{key}`"))),
"brake" => s.brake = num,
"grip" => s.grip = num,
"turn" => s.turn = num,
"mass" => s.mass = num.max(0.05),
"offroad" => s.offroad = num,
_ => return Err(format!("line {}: unknown setting `{key}`", n + 1)),
} }
} }
self.spec = Spec::from_set(&self.set, self.spec.mass);
Ok(()) Ok(())
} }
@@ -436,15 +449,15 @@ mod tests {
name: String::new(), name: String::new(),
driver: String::new(), driver: String::new(),
trailer: false, trailer: false,
original: Vec::new(), set: DEFAULT_SET,
menu_picture: None, menu_picture: None,
spec: BASE_SPEC, spec: Spec::from_set(&DEFAULT_SET, 1.0),
frames, frames,
}; };
let mut v = blank(vec![0; TILTS * HEADINGS * SPR_W * SPR_H]); let mut v = blank(vec![0; TILTS * HEADINGS * SPR_W * SPR_H]);
v.driver = "SIMON".into(); v.driver = "SIMON".into();
v.original = vec![60, 71, 30]; v.set = [60, 71, 30, 0, 6, 16, 24, 12, 58, 42, 29, 0, 0, 0, 0, 0];
v.spec.grip = 6.25; v.spec = Spec::from_set(&v.set, 0.9);
v.name = "FORMULA 1".into(); v.name = "FORMULA 1".into();
let txt = v.settings_text(); let txt = v.settings_text();
let mut w = blank(Vec::new()); let mut w = blank(Vec::new());
@@ -452,8 +465,24 @@ mod tests {
assert_eq!(w.spec, v.spec); assert_eq!(w.spec, v.spec);
assert_eq!(w.name, "FORMULA 1"); assert_eq!(w.name, "FORMULA 1");
assert_eq!(w.driver, "SIMON"); assert_eq!(w.driver, "SIMON");
assert_eq!(w.original, v.original); assert_eq!(w.set, v.set);
assert!(w.apply_settings("grip fast").is_err()); assert!(w.apply_settings("mass heavy").is_err());
// Older files with reconstructed handling still load.
assert!(w.apply_settings("original 1 2 3\ntop 200").is_ok());
assert_eq!(&w.set[..4], &[1, 2, 3, 0]);
}
#[test]
fn decodes_set_values() {
let s = Spec::from_set(&DEFAULT_SET, 1.0);
assert_eq!(s.player.drag, 49.0 / 64.0);
assert_eq!(s.computer.power, 22.0 / 512.0);
assert_eq!(s.turn, 2.0);
assert_eq!(s.hitch, 11.0);
let p = s.with_preset(1);
let top = p.player.power * p.player.drag / (1.0 - p.player.drag);
assert!((top - 26.0 / 128.0).abs() < 1e-5);
assert_eq!(s.with_preset(0), s);
} }
#[test] #[test]
+51 -117
View File
@@ -1,24 +1,25 @@
//! Computer drivers. //! Computer drivers.
//! //!
//! Each driver follows a line through the gates (at its own offset across //! As in the executable, a computer car steers like a joystick: full lock
//! the track) by pure pursuit: aim at a point a speed-dependent distance //! towards a target point ahead, or straight on when it is dead ahead, and
//! further along the line. Short look-ahead rays nudge the chosen heading //! drives flat out. The cars' computer handling (`vehicle::Handling`) is
//! away from the grass, and a driver that stops moving backs out. //! what makes them take bends at that. The target is a point a little way
//! along the road (`Track::edges`) at the driver's own line across it. A
//! driver that stops moving backs out.
use super::car::{angle_diff, Car, Controls}; use super::car::{angle_diff, Car, Controls};
use crate::data::track::{Gate, Track}; use crate::data::track::Track;
const RAYS: i32 = 7; /// Heading error, in radians, below which the driver doesn't steer.
const RAY_STEP: f32 = 0.13; const DEAD_AHEAD: f32 = 0.06;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Ai { pub struct Ai {
/// Where across each gate this driver aims (0 = end a, 1 = end b). /// Where across the road this driver aims (0 = one edge, 1 = the other).
pub line: f32, pub line: f32,
/// Fraction of the car's top speed this driver uses. /// Fraction of full throttle this driver uses.
pub pace: f32, pub pace: f32,
stuck_for: f32, stuck_for: f32,
blocked_for: f32,
reversing: f32, reversing: f32,
} }
@@ -28,83 +29,59 @@ impl Ai {
line, line,
pace, pace,
stuck_for: 0.0, stuck_for: 0.0,
blocked_for: 0.0,
reversing: 0.0, reversing: 0.0,
} }
} }
fn point(&self, g: &Gate) -> (f32, f32) { /// The point `dist` along the road from the car's cross-section, at
( /// this driver's line.
g.a.0 + (g.b.0 - g.a.0) * self.line, pub fn target(&self, car: &Car, track: &Track, dist: f32) -> (f32, f32) {
g.a.1 + (g.b.1 - g.a.1) * self.line, let e = &track.edges;
) let m = e.len();
let at = |k: usize| {
let g = e[k % m];
(
g.a.0 + (g.b.0 - g.a.0) * self.line,
g.a.1 + (g.b.1 - g.a.1) * self.line,
)
};
let mut k = car.section + 1;
let mut p = at(k);
let mut left = dist - (p.0 - car.x).hypot(p.1 - car.y);
while left > 0.0 && k < car.section + m {
let q = at(k + 1);
let len = (q.0 - p.0).hypot(q.1 - p.1);
if len >= left {
let f = left / len;
return (p.0 + (q.0 - p.0) * f, p.1 + (q.1 - p.1) * f);
}
left -= len;
p = q;
k += 1;
}
p
} }
/// Point `dist` along the driving line from the car's projection onto it. pub fn drive(&mut self, car: &Car, track: &Track, dt: f32, racing: bool) -> Controls {
fn pursue(&mut self, car: &Car, gates: &[Gate], next_gate: usize, dist: f32) -> (f32, f32) { if !racing || track.edges.len() < 3 {
let n = gates.len();
// Only the stretch into the gate the lap counter needs next, or the
// one after it (crossing counts with one gate skipped), so a car
// knocked backwards turns round for the gate it missed.
let seg = next_gate;
let mut best = (f32::MAX, seg, 0.0f32);
for k in 0..2 {
let i = (seg + k) % n;
let a = self.point(&gates[(i + n - 1) % n]);
let b = self.point(&gates[i]);
let (dx, dy) = (b.0 - a.0, b.1 - a.1);
let len2 = (dx * dx + dy * dy).max(1e-3);
let t = (((car.x - a.0) * dx + (car.y - a.1) * dy) / len2).clamp(0.0, 1.0);
let d = (a.0 + dx * t - car.x).hypot(a.1 + dy * t - car.y);
if d < best.0 - 1.0 {
best = (d, i, t);
}
}
let (_, mut i, t) = best;
let mut a = self.point(&gates[(i + n - 1) % n]);
let mut b = self.point(&gates[i]);
let mut remaining = dist + (b.0 - a.0).hypot(b.1 - a.1) * t;
for _ in 0..n {
let len = (b.0 - a.0).hypot(b.1 - a.1);
if remaining <= len {
let k = remaining / len.max(1e-3);
return (a.0 + (b.0 - a.0) * k, a.1 + (b.1 - a.1) * k);
}
remaining -= len;
i = (i + 1) % n;
a = b;
b = self.point(&gates[i]);
}
b
}
pub fn drive(
&mut self,
car: &Car,
track: &Track,
next_gate: usize,
dt: f32,
racing: bool,
) -> Controls {
if !racing {
return Controls::default(); return Controls::default();
} }
let gates = &track.gates;
let speed = car.speed(); let speed = car.speed();
let look = 34.0 + speed * 0.32; let look = 30.0 + speed * 0.2;
let target = self.pursue(car, gates, next_gate, look); let (tx, ty) = self.target(car, track, look);
let far = self.pursue(car, gates, next_gate, look * 2.2); let diff = angle_diff((ty - car.y).atan2(tx - car.x), car.heading);
let steer = if diff.abs() < DEAD_AHEAD {
let want = (target.1 - car.y).atan2(target.0 - car.x); 0.0
} else {
diff.signum()
};
if self.reversing > 0.0 { if self.reversing > 0.0 {
self.reversing -= dt; self.reversing -= dt;
let diff = angle_diff(want, car.heading);
return Controls { return Controls {
throttle: 0.0, throttle: 0.0,
brake: 1.0, brake: 1.0,
steer: -diff.signum(), steer: -steer,
}; };
} }
if speed < 10.0 { if speed < 10.0 {
@@ -116,49 +93,6 @@ impl Ai {
} else { } else {
self.stuck_for = 0.0; self.stuck_for = 0.0;
} }
// Pick the heading closest to the target that keeps us on the road.
let ray_len = 18.0 + speed * 0.25;
let mut best = (f32::MAX, want);
for k in -RAYS..=RAYS {
let cand = want + k as f32 * RAY_STEP;
let mut grass = 0.0;
for step in 1..=4 {
let d = ray_len * step as f32 / 4.0;
if !track.on_road_world(car.x + cand.cos() * d, car.y + cand.sin() * d) {
grass += 1.0 / step as f32;
}
}
let cost = (k as f32 * RAY_STEP).abs() + grass * 0.9;
if cost < best.0 {
best = (cost, cand);
}
}
let diff = angle_diff(best.1, car.heading);
let steer = (diff * 3.0).clamp(-1.0, 1.0);
// Boxed in (nose in the scenery or against the edge): back out.
if best.0 > 1.6 && speed < 30.0 {
self.blocked_for += dt;
if self.blocked_for > 0.5 {
self.blocked_for = 0.0;
self.reversing = 0.7;
}
} else {
self.blocked_for = 0.0;
}
// Slow for what is coming: compare heading with the far probe.
let bend = angle_diff((far.1 - car.y).atan2(far.0 - car.x), car.heading).abs();
let corner_speed = car.spec.top * self.pace * (1.0 - (bend / 1.3).min(0.75));
let (mut throttle, mut brake) = (1.0, 0.0);
if speed > corner_speed {
throttle = 0.0;
if speed > corner_speed + 25.0 {
brake = 0.7;
}
} else if diff.abs() > 0.8 {
throttle = 0.6;
}
if diff.abs() > 2.3 && speed < 35.0 { if diff.abs() > 2.3 && speed < 35.0 {
// Facing the wrong way: reverse-turn round. // Facing the wrong way: reverse-turn round.
return Controls { return Controls {
@@ -168,8 +102,8 @@ impl Ai {
}; };
} }
Controls { Controls {
throttle, throttle: self.pace,
brake, brake: 0.0,
steer, steer,
} }
} }
+233 -94
View File
@@ -1,19 +1,36 @@
//! Vehicle handling. //! Vehicle handling, as decoded from the executable.
//! //!
//! The retail executable is not available, so handling is a reconstruction //! The original runs one update per 50 Hz frame in its own units, where one
//! (per-car values live in the vehicle assets): an arcade model with separate forward and lateral friction, which gives //! world unit is a height-map cell (`track::CELL` of ours). Each frame:
//! the long power-slides the series is known for. Physics runs on the //!
//! isometric ground plane (see `data::track::to_screen`). //! - **Steering:** spin = (spin + steer * turn) / 2, then the heading
//! turns by spin (64ths of a turn). Steering is digital, as the
//! joystick is.
//! - **Height:** vertical speed loses `gravity`. If the car would still be
//! above the ground two frames ahead it's airborne; otherwise its
//! vertical speed is half the climb to that point. Body tilt steps one
//! notch a frame towards the slope under the car.
//! - **Engine and grip:** see `vehicle::Handling`. Climbing takes half the
//! vertical speed off the throttle; descending adds it.
//! - **Move:** velocity is added to the position. Airborne cars keep
//! their velocity and engine speed.
//! - **Walls:** the road edges (`Track::edges`) are walls. A car that
//! reaches one loses the part of its velocity into the wall and bounces
//! back with half of it.
//!
//! Human and computer drivers use separate handling from the vehicle's
//! `.set` values.
use crate::data::track::{self, Track}; use super::DT;
use crate::data::vehicle::{self, Spec}; use crate::data::track::{self, Gate, Track, CELL};
use crate::data::vehicle::{self, Handling, Spec};
use std::f32::consts::{PI, TAU}; use std::f32::consts::{PI, TAU};
pub const RADIUS: f32 = 11.0; pub const RADIUS: f32 = 11.0;
const SLOPE_GRAVITY: f32 = 110.0; /// How close the car's centre may come to a wall, in world units.
/// Cap on slope acceleration so the steep banking on some tracks cannot pin const WALL_MARGIN: f32 = 4.0;
/// a car against the outside of a bend. /// The executable samples the slope 0.2 cells from the car.
const SLOPE_MAX: f32 = 45.0; const TILT_PROBE: f32 = 0.2 * CELL;
#[derive(Clone, Copy, Default, Debug)] #[derive(Clone, Copy, Default, Debug)]
pub struct Controls { pub struct Controls {
@@ -28,21 +45,39 @@ pub struct Car {
pub vehicle: usize, pub vehicle: usize,
pub spec: Spec, pub spec: Spec,
pub team: usize, pub team: usize,
/// Uses the vehicle's computer-driver handling.
pub computer: bool,
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
/// Height above the ground plane, pixels.
pub z: f32,
/// World units per second.
pub vx: f32, pub vx: f32,
pub vy: f32, pub vy: f32,
/// Pixels per frame.
pub vz: f32,
/// World angle, 0 = +x, increasing clockwise on screen. /// World angle, 0 = +x, increasing clockwise on screen.
pub heading: f32, pub heading: f32,
/// Turn per frame, 64ths of a turn.
pub spin: f32,
/// The engine's speed, in the original's units per frame.
pub drive: f32,
pub airborne: bool,
pub controls: Controls, pub controls: Controls,
/// Sideways speed this frame, used for skid marks and sound. /// Sideways speed this frame, used for skid marks and sound.
pub slip: f32, pub slip: f32,
pub on_road: bool, pub on_road: bool,
/// Tilt notches 0-4 (2 = level): pitch follows the slope along world y
/// and roll the slope along world x, as in the executable.
pub pitch: i32,
pub roll: i32,
pub tilt: usize, pub tilt: usize,
/// Throttle/rpm proxy for the engine sound, 0..1. /// Throttle/rpm proxy for the engine sound, 0..1.
pub rpm: f32, pub rpm: f32,
/// Strength of the last collision, decays each frame. /// Strength of the last collision, decays each frame.
pub bump: f32, pub bump: f32,
/// The road cross-section (`Track::edges`) the car is past.
pub section: usize,
} }
impl Car { impl Car {
@@ -51,22 +86,44 @@ impl Car {
vehicle, vehicle,
spec, spec,
team, team,
computer: false,
x, x,
y, y,
z: 0.0,
vx: 0.0, vx: 0.0,
vy: 0.0, vy: 0.0,
vz: 0.0,
heading, heading,
spin: 0.0,
drive: 0.0,
airborne: false,
controls: Controls::default(), controls: Controls::default(),
slip: 0.0, slip: 0.0,
on_road: true, on_road: true,
pitch: 2,
roll: 2,
tilt: vehicle::LEVEL, tilt: vehicle::LEVEL,
rpm: 0.0, rpm: 0.0,
bump: 0.0, bump: 0.0,
section: 0,
} }
} }
/// Puts the car on the ground at its position, stopped, and finds its
/// place along the road.
pub fn place(&mut self, track: &Track) {
self.z = track.ground(self.x, self.y);
self.vx = 0.0;
self.vy = 0.0;
self.vz = 0.0;
self.spin = 0.0;
self.drive = 0.0;
self.airborne = false;
self.section = nearest_section(&track.edges, (self.x, self.y));
}
pub fn screen_pos(&self) -> (f32, f32) { pub fn screen_pos(&self) -> (f32, f32) {
track::to_screen(self.x, self.y) track::project(self.x, self.y, self.z)
} }
pub fn speed(&self) -> f32 { pub fn speed(&self) -> f32 {
@@ -77,84 +134,147 @@ impl Car {
(self.heading.cos(), self.heading.sin()) (self.heading.cos(), self.heading.sin())
} }
pub fn step(&mut self, track: &Track, dt: f32, top_scale: f32) { pub fn handling(&self) -> Handling {
self.on_road = track.on_road_world(self.x, self.y); if self.computer {
let surf = if self.on_road { 1.0 } else { self.spec.offroad }; self.spec.computer
let c = self.controls;
let (fx, fy) = self.forward();
let (rx, ry) = (-fy, fx);
let mut vf = self.vx * fx + self.vy * fy;
let mut vr = self.vx * rx + self.vy * ry;
let top = self.spec.top * surf * top_scale;
if c.throttle > 0.0 && vf < top {
let headroom = (1.0 - (vf / top).max(0.0).powi(2)).max(0.05);
vf += self.spec.accel * c.throttle * headroom * dt;
}
if c.brake > 0.0 {
if vf > 4.0 {
vf = (vf - self.spec.brake * c.brake * dt).max(0.0);
} else if vf > -top * 0.35 {
vf -= self.spec.accel * 0.6 * c.brake * dt;
}
}
let drag = if self.on_road { 0.35 } else { 1.6 };
vf -= vf * drag * dt;
if c.throttle == 0.0 && c.brake == 0.0 {
vf -= vf.signum() * (30.0 * dt).min(vf.abs());
}
// Sideways friction: grip drops as the slide gets faster, so once a
// car breaks away it keeps sliding.
let grip = self.spec.grip * surf.max(0.6) / (1.0 + vr.abs() / 120.0);
vr *= (-grip * dt).exp();
self.slip = vr.abs();
let steer_auth = (vf.abs() / 45.0).min(1.0) * vf.signum();
let turn = self.spec.turn * (1.0 - (vf.abs() / (top * 2.2)).min(0.35));
self.heading = (self.heading + c.steer * turn * steer_auth * dt).rem_euclid(TAU);
self.vx = fx * vf + rx * vr;
self.vy = fy * vf + ry * vr;
// Hills: the height map is sampled in screen space.
let (gx, gy) = gradient(track, self.x, self.y);
let (mut ax, mut ay) = (-gx * SLOPE_GRAVITY, -gy * SLOPE_GRAVITY);
let a = ax.hypot(ay);
if a > SLOPE_MAX {
ax *= SLOPE_MAX / a;
ay *= SLOPE_MAX / a;
}
self.vx += ax * dt;
self.vy += ay * dt;
let (ox, oy) = (self.x, self.y);
self.x += self.vx * dt;
self.y += self.vy * dt;
self.keep_in_bounds(ox, oy);
// Body tilt from the slope along and across the car.
let (fx, fy) = self.forward();
let along = gx * fx + gy * fy;
let across = gx * -fy + gy * fx;
let q = |s: f32| (s / 0.035).round() as i32;
self.tilt = vehicle::tilt_block(-q(along), q(across));
let target_rpm = if c.throttle > 0.0 {
0.35 + 0.65 * (vf.abs() / top).min(1.0)
} else { } else {
(vf.abs() / top).min(1.0) * 0.7 self.spec.player
}; }
self.rpm += (target_rpm - self.rpm) * (1.0 - (-6.0 * dt).exp());
self.bump *= (-8.0 * dt).exp();
} }
/// The picture edge is a wall. Slide along it by keeping whichever axis /// Top speed on the flat, world units per second.
/// of the move stays inside, so cars never get pinned in a corner. pub fn top_speed(&self) -> f32 {
fn keep_in_bounds(&mut self, ox: f32, oy: f32) { let h = self.handling();
if h.drag >= 1.0 {
return f32::INFINITY;
}
h.power * h.drag / (1.0 - h.drag) * CELL / DT
}
/// One frame. `pace` scales the throttle (finished cars cruise).
pub fn step(&mut self, track: &Track, pace: f32) {
let c = self.controls;
let h = self.handling();
// Velocity in world units per frame.
let (mut vx, mut vy) = (self.vx * DT, self.vy * DT);
if self.airborne {
self.spin = self.spin.clamp(-1.0, 1.0);
} else {
let steer = c.steer.clamp(-1.0, 1.0);
self.spin = (self.spin + steer * self.spec.turn) * 0.5;
}
self.vz -= self.spec.gravity;
let (ax, ay) = (self.x + 2.0 * vx, self.y + 2.0 * vy);
let ground = track.ground(ax, ay);
if self.z + 2.0 * self.vz > ground {
self.airborne = true;
} else {
self.airborne = false;
self.vz = (ground - self.z) * 0.5;
let notch = |probe: f32| ((probe - ground + 2.5).floor() as i32).clamp(0, 4);
let pitch = notch(track.ground(ax, ay + TILT_PROBE));
let roll = notch(track.ground(ax - TILT_PROBE, ay));
self.pitch += (pitch - self.pitch).signum();
self.roll += (roll - self.roll).signum();
}
self.tilt = vehicle::tilt_block(self.pitch - 2, self.roll - 2);
self.heading = (self.heading + self.spin * TAU / 64.0).rem_euclid(TAU);
let (fx, fy) = self.forward();
if !self.airborne {
// The original's units: world cells per frame.
let (cx, cy) = (vx / CELL, vy / CELL);
let along = cx * fx + cy * fy;
let mut throttle = (c.throttle - c.brake).clamp(-1.0, 1.0) * pace;
throttle += if along < 0.0 { self.vz } else { -self.vz } * 0.5;
let target = (self.drive + throttle * h.power) * h.drag;
let (sx, sy) = (h.grip * (fx * target - cx), h.grip * (fy * target - cy));
self.drive += (target - self.drive) * 0.25;
vx += sx * CELL;
vy += sy * CELL;
}
let (ox, oy) = (self.x, self.y);
self.x += vx;
self.y += vy;
self.z += self.vz;
self.vx = vx / DT;
self.vy = vy / DT;
self.walls(track);
self.keep_in_bounds(track, ox, oy);
self.on_road = track.on_road_world(self.x, self.y);
self.slip = (self.vy * fx - self.vx * fy).abs();
let top = self.top_speed().max(1.0);
let fwd = (self.vx * fx + self.vy * fy).abs();
let target_rpm = if c.throttle > 0.0 {
0.35 + 0.65 * (fwd / top).min(1.0)
} else {
(fwd / top).min(1.0) * 0.7
};
self.rpm += (target_rpm - self.rpm) * (1.0 - (-6.0 * DT).exp());
self.bump *= (-8.0 * DT).exp();
}
/// Keeps the car between the road edges of the section it is in, and
/// moves `section` along as the car crosses from one to the next.
fn walls(&mut self, track: &Track) {
let e = &track.edges;
let m = e.len();
if m < 3 {
return;
}
let p = (self.x, self.y);
for _ in 0..m {
let next = (self.section + 1) % m;
if !past(e, next, p) {
break;
}
self.section = next;
}
for _ in 0..m {
if past(e, self.section, p) {
break;
}
self.section = (self.section + m - 1) % m;
}
let (s0, s1) = (e[self.section], e[(self.section + 1) % m]);
for (p0, p1, inside) in [(s0.a, s1.a, s0.b), (s0.b, s1.b, s0.a)] {
let (dx, dy) = (p1.0 - p0.0, p1.1 - p0.1);
let len = dx.hypot(dy);
if len < 1e-3 {
continue;
}
// Unit normal pointing into the road.
let (mut nx, mut ny) = (-dy / len, dx / len);
if nx * (inside.0 - p0.0) + ny * (inside.1 - p0.1) < 0.0 {
nx = -nx;
ny = -ny;
}
let dist = nx * (self.x - p0.0) + ny * (self.y - p0.1);
if dist >= WALL_MARGIN {
continue;
}
self.x += nx * (WALL_MARGIN - dist);
self.y += ny * (WALL_MARGIN - dist);
let vn = self.vx * nx + self.vy * ny;
if vn < 0.0 {
self.vx -= 1.5 * vn * nx;
self.vy -= 1.5 * vn * ny;
self.bump = self.bump.max((-vn / 200.0).min(0.6));
}
}
}
/// The picture edge is a wall too, for tracks whose road edges run off
/// it. Slide along it by keeping whichever axis of the move stays
/// inside, so cars never get pinned in a corner.
fn keep_in_bounds(&mut self, track: &Track, ox: f32, oy: f32) {
const MARGIN: f32 = 3.0; const MARGIN: f32 = 3.0;
let inside = |x: f32, y: f32| { let inside = |x: f32, y: f32| {
let (sx, sy) = track::to_screen(x, y); let (sx, sy) = track.to_screen(x, y);
track::on_screen(sx, sy, MARGIN) track::on_screen(sx, sy, MARGIN)
}; };
if inside(self.x, self.y) { if inside(self.x, self.y) {
@@ -177,23 +297,42 @@ impl Car {
} }
/// Rear wheel contact points in screen space, for skid marks. /// Rear wheel contact points in screen space, for skid marks.
pub fn rear_wheels(&self) -> [(f32, f32); 2] { pub fn rear_wheels(&self, track: &Track) -> [(f32, f32); 2] {
let (fx, fy) = self.forward(); let (fx, fy) = self.forward();
let (rx, ry) = (-fy, fx); let (rx, ry) = (-fy, fx);
let (bx, by) = (self.x - fx * 10.0, self.y - fy * 10.0); let (bx, by) = (self.x - fx * 10.0, self.y - fy * 10.0);
[ [
track::to_screen(bx + rx * 6.0, by + ry * 6.0), track.to_screen(bx + rx * 6.0, by + ry * 6.0),
track::to_screen(bx - rx * 6.0, by - ry * 6.0), track.to_screen(bx - rx * 6.0, by - ry * 6.0),
] ]
} }
} }
/// Height change per world unit along world x and y. /// Whether `p` is past cross-section `k`, going round the road.
fn gradient(track: &Track, wx: f32, wy: f32) -> (f32, f32) { fn past(e: &[Gate], k: usize, p: (f32, f32)) -> bool {
let d = 6.0; let m = e.len();
let gx = (track.height_world(wx + d, wy) - track.height_world(wx - d, wy)) / (2.0 * d); let (a, b) = (e[k].a, e[k].b);
let gy = (track.height_world(wx, wy + d) - track.height_world(wx, wy - d)) / (2.0 * d); let side = |q: (f32, f32)| (b.0 - a.0) * (q.1 - a.1) - (b.1 - a.1) * (q.0 - a.0);
(gx, gy) side(p) * side(e[(k + 1) % m].mid()) > 0.0
}
/// The cross-section just behind the point closest to `p`.
pub fn nearest_section(e: &[Gate], p: (f32, f32)) -> usize {
let mut best = (f32::MAX, 0);
for (k, g) in e.iter().enumerate() {
let (mx, my) = g.mid();
let d = (mx - p.0).hypot(my - p.1);
if d < best.0 {
best = (d, k);
}
}
let k = best.1;
let m = e.len().max(1);
if past(e, k, p) || e.len() < 3 {
k
} else {
(k + m - 1) % m
}
} }
/// Pushes overlapping cars apart and exchanges momentum. /// Pushes overlapping cars apart and exchanges momentum.
+58 -23
View File
@@ -46,15 +46,15 @@ pub struct Trailer {
pub heading: f32, pub heading: f32,
} }
/// Hitch distance behind the car's centre, and tow bar length to the /// Tow bar length from the car's hitch (`Spec::hitch`) to the trailer's
/// trailer's centre, in world units. /// centre, in world units.
const HITCH: f32 = 12.0;
const TOW_BAR: f32 = 20.0; const TOW_BAR: f32 = 20.0;
const TRAILER_RADIUS: f32 = 10.0; const TRAILER_RADIUS: f32 = 10.0;
/// Seconds without crossing a gate before a computer car is rescued. /// Seconds without crossing a gate before a computer car is rescued.
const RESCUE_AFTER: f32 = 8.0; const RESCUE_AFTER: f32 = 8.0;
fn rescue(e: &mut Entrant, gates: &[Gate]) { fn rescue(e: &mut Entrant, track: &Track) {
let gates = &track.gates;
let n = gates.len(); let n = gates.len();
let from = gates[(e.next_gate + n - 1) % n].mid(); let from = gates[(e.next_gate + n - 1) % n].mid();
let to = gates[e.next_gate].mid(); let to = gates[e.next_gate].mid();
@@ -62,11 +62,10 @@ fn rescue(e: &mut Entrant, gates: &[Gate]) {
let c = &mut e.car; let c = &mut e.car;
c.x = from.0; c.x = from.0;
c.y = from.1; c.y = from.1;
c.vx = 0.0;
c.vy = 0.0;
c.heading = heading.rem_euclid(std::f32::consts::TAU); c.heading = heading.rem_euclid(std::f32::consts::TAU);
c.place(track);
if let Some(t) = &mut e.trailer { if let Some(t) = &mut e.trailer {
let back = HITCH + TOW_BAR; let back = c.spec.hitch + TOW_BAR;
t.x = from.0 - heading.cos() * back; t.x = from.0 - heading.cos() * back;
t.y = from.1 - heading.sin() * back; t.y = from.1 - heading.sin() * back;
t.heading = heading; t.heading = heading;
@@ -78,7 +77,7 @@ impl Trailer {
/// hitch and swings to point at it. /// hitch and swings to point at it.
fn follow(&mut self, car: &Car) { fn follow(&mut self, car: &Car) {
let (fx, fy) = car.forward(); let (fx, fy) = car.forward();
let (hx, hy) = (car.x - fx * HITCH, car.y - fy * HITCH); let (hx, hy) = (car.x - fx * car.spec.hitch, car.y - fy * car.spec.hitch);
let (dx, dy) = (self.x - hx, self.y - hy); let (dx, dy) = (self.x - hx, self.y - hy);
let d = dx.hypot(dy).max(1e-3); let d = dx.hypot(dy).max(1e-3);
self.x = hx + dx / d * TOW_BAR; self.x = hx + dx / d * TOW_BAR;
@@ -130,6 +129,8 @@ pub struct EntrantSetup {
pub name: String, pub name: String,
/// Vehicle index of a caravan to tow, if any. /// Vehicle index of a caravan to tow, if any.
pub trailer: Option<usize>, pub trailer: Option<usize>,
/// Handling preset (`Spec::with_preset`); 0 = the car's own.
pub preset: usize,
} }
impl Race { impl Race {
@@ -153,15 +154,18 @@ impl Race {
let row = (i / 2) as f32; let row = (i / 2) as f32;
let side = if i % 2 == 0 { -1.0 } else { 1.0 }; let side = if i % 2 == 0 { -1.0 } else { 1.0 };
// Leave room for a caravan between rows when towing. // Leave room for a caravan between rows when towing.
let spec = vehicles[s.vehicle].spec.with_preset(s.preset);
let spacing = if s.trailer.is_some() { let spacing = if s.trailer.is_some() {
30.0 + HITCH + TOW_BAR + TRAILER_RADIUS 30.0 + spec.hitch + TOW_BAR + TRAILER_RADIUS
} else { } else {
30.0 30.0
}; };
let ((px, py), heading) = behind(&path, 18.0 + row * spacing); let ((px, py), heading) = behind(&path, 18.0 + row * spacing);
let (rx, ry) = (-heading.sin(), heading.cos()); let (rx, ry) = (-heading.sin(), heading.cos());
let (x, y) = (px + rx * side * 12.0, py + ry * side * 12.0); let (x, y) = (px + rx * side * 12.0, py + ry * side * 12.0);
let car = Car::new(s.vehicle, vehicles[s.vehicle].spec, s.team, x, y, heading); let mut car = Car::new(s.vehicle, spec, s.team, x, y, heading);
car.computer = s.driver == Driver::Cpu;
car.place(&track);
let line = 0.3 + 0.4 * rand01(&mut rng); let line = 0.3 + 0.4 * rand01(&mut rng);
let pace = 0.86 + 0.12 * rand01(&mut rng); let pace = 0.86 + 0.12 * rand01(&mut rng);
entrants.push(Entrant { entrants.push(Entrant {
@@ -178,7 +182,7 @@ impl Race {
progress_at: 0.0, progress_at: 0.0,
trailer: s.trailer.map(|t| { trailer: s.trailer.map(|t| {
let (fx, fy) = (heading.cos(), heading.sin()); let (fx, fy) = (heading.cos(), heading.sin());
let back = HITCH + TOW_BAR; let back = spec.hitch + TOW_BAR;
Trailer { Trailer {
vehicle: t, vehicle: t,
x: x - fx * back, x: x - fx * back,
@@ -187,11 +191,6 @@ impl Race {
} }
}), }),
}); });
if let Some(e) = entrants.last_mut().filter(|e| e.trailer.is_some()) {
// Towing costs some speed and pick-up.
e.car.spec.top *= 0.9;
e.car.spec.accel *= 0.8;
}
} }
let ground = track.rgb(); let ground = track.rgb();
Race { Race {
@@ -236,15 +235,15 @@ impl Race {
for e in self.entrants.iter_mut() { for e in self.entrants.iter_mut() {
let controls = match (e.driver, e.finished) { let controls = match (e.driver, e.finished) {
(_, Some(_)) => e.ai.drive(&e.car, &self.track, e.next_gate, DT, racing), (_, Some(_)) => e.ai.drive(&e.car, &self.track, DT, racing),
(Driver::Human(p), None) if racing => human.get(p).copied().unwrap_or_default(), (Driver::Human(p), None) if racing => human.get(p).copied().unwrap_or_default(),
(Driver::Human(_), None) => Controls::default(), (Driver::Human(_), None) => Controls::default(),
(Driver::Cpu, None) => e.ai.drive(&e.car, &self.track, e.next_gate, DT, racing), (Driver::Cpu, None) => e.ai.drive(&e.car, &self.track, DT, racing),
}; };
e.car.controls = controls; e.car.controls = controls;
// Finished cars cruise round on autopilot at reduced pace. // Finished cars cruise round on autopilot at reduced pace.
let pace = if e.finished.is_some() { 0.6 } else { 1.0 }; let pace = if e.finished.is_some() { 0.6 } else { 1.0 };
e.car.step(&self.track, DT, pace); e.car.step(&self.track, pace);
} }
for i in 0..self.entrants.len() { for i in 0..self.entrants.len() {
@@ -298,7 +297,7 @@ impl Race {
&& e.finished.is_none() && e.finished.is_none()
&& rt - e.progress_at > RESCUE_AFTER && rt - e.progress_at > RESCUE_AFTER
{ {
rescue(e, &self.track.gates); rescue(e, &self.track);
e.progress_at = rt; e.progress_at = rt;
} }
} }
@@ -326,14 +325,14 @@ impl Race {
for e in &self.entrants { for e in &self.entrants {
let c = &e.car; let c = &e.car;
let speed = c.speed(); let speed = c.speed();
if c.slip > 55.0 && speed > 40.0 && c.on_road { if c.slip > 55.0 && speed > 40.0 && c.on_road && !c.airborne {
let strength = ((c.slip - 55.0) / 120.0).clamp(0.08, 0.35); let strength = ((c.slip - 55.0) / 120.0).clamp(0.08, 0.35);
for (wx, wy) in c.rear_wheels() { for (wx, wy) in c.rear_wheels(&self.track) {
burn(&mut self.ground, wx, wy, strength); burn(&mut self.ground, wx, wy, strength);
} }
} }
if !c.on_road && speed > 50.0 { if !c.on_road && speed > 50.0 {
spawn.push((c.rear_wheels()[0], c.track_colour(&self.track))); spawn.push((c.rear_wheels(&self.track)[0], c.track_colour(&self.track)));
} }
} }
for ((x, y), colour) in spawn { for ((x, y), colour) in spawn {
@@ -487,4 +486,40 @@ mod tests {
let ((x, y), _) = behind(&path, 130.0); let ((x, y), _) = behind(&path, 130.0);
assert!((x - 30.0).abs() < 1e-3 && (y - 100.0).abs() < 1e-3); assert!((x - 30.0).abs() < 1e-3 && (y - 100.0).abs() < 1e-3);
} }
#[test]
fn walls_keep_cars_on_the_road() {
let gates: Vec<Gate> = (0..12)
.map(|i| {
let a = i as f32 / 12.0 * std::f32::consts::TAU;
let (c, s) = (a.cos(), a.sin());
Gate {
a: (512.0 + c * 150.0, 512.0 + s * 150.0),
b: (512.0 + c * 210.0, 512.0 + s * 210.0),
}
})
.collect();
let track = Track {
name: "ring".into(),
set_name: String::new(),
thumbnail: None,
pixels: vec![8; crate::data::track::W * crate::data::track::H],
palette: crate::data::palette::TRACK_DEFAULT,
height: vec![0; 64 * 64],
edges: crate::data::track::edges(&gates),
gates,
start: (0.0, 0.0),
flags: 0,
};
let spec = crate::data::vehicle::Spec::from_set(&crate::data::vehicle::DEFAULT_SET, 1.0);
let mut c = Car::new(0, spec, 0, 512.0 + 180.0, 512.0, 0.0);
c.place(&track);
// Flat out in a straight line: the outer wall turns the car back.
c.controls.throttle = 1.0;
for _ in 0..500 {
c.step(&track, 1.0);
let r = (c.x - 512.0).hypot(c.y - 512.0);
assert!(r > 140.0 && r < 215.0, "left the road at radius {r}");
}
}
} }
+35 -8
View File
@@ -39,12 +39,12 @@ USAGE:
skids unpack FILE.adf|FILE.lha --out DIR extract every file from a disk image or archive skids unpack FILE.adf|FILE.lha --out DIR extract every file from a disk image or archive
skids music [--data DIR] --out FILE.wav [--seconds S] skids music [--data DIR] --out FILE.wav [--seconds S]
render the tune to a WAV file render the tune to a WAV file
skids render [--data DIR] --out FILE.png [--track N] [--seconds S] [--players 1|2] [--menu] [--caravans] skids render [--data DIR] --out FILE.png [--track N] [--seconds S] [--players 1|2] [--menu] [--caravans] [--handling N]
simulate a race (or the menu) and save a frame simulate a race (or the menu) and save a frame
skids bench [--data DIR] [--seconds S] [--caravans] skids bench [--data DIR] [--seconds S] [--caravans] [--handling N]
race computer cars on every track race computer cars on every track
skids trace [--data DIR] --out FILE.png [--track N] [--seconds S] skids trace [--data DIR] --out FILE.png [--track N] [--seconds S] [--handling N]
draw the computer cars' paths on a track draw the walls, gates and computer cars' paths
The game data is built into the executable, so no disks are needed. --data The game data is built into the executable, so no disks are needed. --data
adds to or overrides it with ADF images, LHA archives, or extracted track/, adds to or overrides it with ADF images, LHA archives, or extracted track/,
@@ -52,6 +52,9 @@ vehicles/ and palettes/ folders, or an assets folder of edits. Without
--data, a folder like that in the current directory or next to the --data, a folder like that in the current directory or next to the
executable is picked up automatically. executable is picked up automatically.
--handling picks the original's handling presets: 0 = each car's own
(CUSTOM), 1-6 = CLASSIC, GRUNTY, ZIPPY, RIPPY, SLIPPERY, PEDAL CAR.
CONTROLS: CONTROLS:
Player 1: arrow keys (or W A S D in one-player games) Player 1: arrow keys (or W A S D in one-player games)
Player 2: W A S D Player 2: W A S D
@@ -68,6 +71,7 @@ struct Args {
players: usize, players: usize,
menu: bool, menu: bool,
caravans: bool, caravans: bool,
handling: usize,
} }
fn parse_args() -> Result<Args, String> { fn parse_args() -> Result<Args, String> {
@@ -82,6 +86,7 @@ fn parse_args() -> Result<Args, String> {
players: 1, players: 1,
menu: false, menu: false,
caravans: false, caravans: false,
handling: 0,
}; };
let mut it = std::env::args().skip(1); let mut it = std::env::args().skip(1);
while let Some(arg) = it.next() { while let Some(arg) = it.next() {
@@ -97,6 +102,12 @@ fn parse_args() -> Result<Args, String> {
"--mute" => a.mute = true, "--mute" => a.mute = true,
"--menu" => a.menu = true, "--menu" => a.menu = true,
"--caravans" => a.caravans = true, "--caravans" => a.caravans = true,
"--handling" => {
a.handling = val("--handling")?.parse().map_err(|_| "bad --handling")?;
if a.handling > data::vehicle::PRESETS.len() {
return Err("--handling must be 0-6".into());
}
}
"info" | "extract" | "render" | "bench" | "trace" | "music" | "play" => a.cmd = arg, "info" | "extract" | "render" | "bench" | "trace" | "music" | "play" => a.cmd = arg,
"unpack" => { "unpack" => {
a.cmd = arg; a.cmd = arg;
@@ -370,6 +381,7 @@ fn render_shot(data: GameData, args: &Args) -> Result<(), String> {
App::new(data, audio) App::new(data, audio)
}; };
app.settings.caravans = args.caravans; app.settings.caravans = args.caravans;
app.settings.handling = args.handling;
if args.menu { if args.menu {
for _ in 0..(args.seconds / game::DT) as usize { for _ in 0..(args.seconds / game::DT) as usize {
app.update(&Input::default()); app.update(&Input::default());
@@ -399,7 +411,7 @@ fn render_shot(data: GameData, args: &Args) -> Result<(), String> {
.entrants .entrants
.iter_mut() .iter_mut()
.filter(|e| matches!(e.driver, game::Driver::Human(_))) .filter(|e| matches!(e.driver, game::Driver::Human(_)))
.map(|e| e.ai.drive(&e.car, &race.track, e.next_gate, game::DT, true)) .map(|e| e.ai.drive(&e.car, &race.track, game::DT, true))
.collect(); .collect();
race.step(&controls); race.step(&controls);
app::draw_race(&mut frame, &app.data, &race, &mut cams, players); app::draw_race(&mut frame, &app.data, &race, &mut cams, players);
@@ -428,6 +440,7 @@ fn bench(data: GameData, args: &Args) -> Result<(), String> {
App::new(data, audio) App::new(data, audio)
}; };
app.settings.caravans = args.caravans; app.settings.caravans = args.caravans;
app.settings.handling = args.handling;
let frames = (args.seconds / game::DT) as usize; let frames = (args.seconds / game::DT) as usize;
out!( out!(
"{:<10} {:>5} {:>8} {:>9} {:>8}", "{:<10} {:>5} {:>8} {:>9} {:>8}",
@@ -537,6 +550,7 @@ fn trace(data: GameData, args: &Args) -> Result<(), String> {
App::new(data, audio) App::new(data, audio)
}; };
app.settings.caravans = args.caravans; app.settings.caravans = args.caravans;
app.settings.handling = args.handling;
// Same seed as `bench` uses for this track, so problems seen there can be traced. // Same seed as `bench` uses for this track, so problems seen there can be traced.
let mut race = app.build_race( let mut race = app.build_race(
args.track.saturating_sub(1), args.track.saturating_sub(1),
@@ -547,18 +561,31 @@ fn trace(data: GameData, args: &Args) -> Result<(), String> {
args.track.max(1) as u32, args.track.max(1) as u32,
)?; )?;
let mut img = race.track.rgb(); let mut img = race.track.rgb();
let plot = |img: &mut Vec<u32>, wx: f32, wy: f32, c: u32| { let plot = |img: &mut Vec<u32>, t: &track::Track, wx: f32, wy: f32, c: u32| {
let (x, y) = track::to_screen(wx, wy); let (x, y) = t.to_screen(wx, wy);
if track::on_screen(x, y, 0.0) { if track::on_screen(x, y, 0.0) {
img[y as usize * track::W + x as usize] = c; img[y as usize * track::W + x as usize] = c;
} }
}; };
// The walls: both road edges.
let edges = &race.track.edges;
for (k, g) in edges.iter().enumerate() {
let h = edges[(k + 1) % edges.len()];
for i in 0..8 {
let f = i as f32 / 8.0;
for (p, q) in [(g.a, h.a), (g.b, h.b)] {
let (x, y) = (p.0 + (q.0 - p.0) * f, p.1 + (q.1 - p.1) * f);
plot(&mut img, &race.track, x, y, 0xc0c0c0);
}
}
}
for (gi, g) in race.track.gates.iter().enumerate() { for (gi, g) in race.track.gates.iter().enumerate() {
let c = if gi == 0 { 0xffff00 } else { 0xff2020 }; let c = if gi == 0 { 0xffff00 } else { 0xff2020 };
for i in 0..=64 { for i in 0..=64 {
let k = i as f32 / 64.0; let k = i as f32 / 64.0;
plot( plot(
&mut img, &mut img,
&race.track,
g.a.0 + (g.b.0 - g.a.0) * k, g.a.0 + (g.b.0 - g.a.0) * k,
g.a.1 + (g.b.1 - g.a.1) * k, g.a.1 + (g.b.1 - g.a.1) * k,
c, c,
@@ -569,7 +596,7 @@ fn trace(data: GameData, args: &Args) -> Result<(), String> {
for _ in 0..(args.seconds / game::DT) as usize { for _ in 0..(args.seconds / game::DT) as usize {
race.step(&[]); race.step(&[]);
for (i, e) in race.entrants.iter().enumerate() { for (i, e) in race.entrants.iter().enumerate() {
plot(&mut img, e.car.x, e.car.y, TRAIL[i % 4]); plot(&mut img, &race.track, e.car.x, e.car.y, TRAIL[i % 4]);
} }
} }
let n = race.track.gates.len(); let n = race.track.gates.len();