Storing data for each of the ships in the local bubble of universe
Every ship in our local bubble of universe has its own data block, stored in the K% workspace. The ship data block contains information about the ship's status, its location in space, its orientation and so on. Each ship in the local bubble has an entry in the lookup table at UNIV that points to its data block in K%, and along with the ship slots at FRIN and the ship blueprints at XX21, we have everything we need to simulate the world of Elite.
Before going any further, some important notes on how we're going to talk about ships and ship data blocks.
Ship data terminology
Throughout this documentation, we're going to refer to "ships" and "ship data blocks" for all the different object types in the vicinity, not just ships. The same blocks, pointers and data structures are used not only for ships, but also for cargo canisters, missiles, escape pods, asteroids, space stations, planets and suns, but that's a bit of a mouthful compared to "ships", so "ships" it is.
When working with a ship's data - such as when we move a ship in MVEIT, or spawn a child ship in SFS1 - we normally work with the data in the INWK workspace, as INWK is in zero page and is therefore faster and more memory efficient to manipulate. The ship data blocks in the K% workspace are therefore copied into INWK before they are worked on, and new ship blocks are created in INWK before being copied to K%. As a result, the layout of the INWK data block is identical the layout of each ship data block in K%.
It's also important to note that INWK is known as XX1 in some parts of the codebase, namely those parts that were written by David Braben on his Acorn Atom, where he was only allowed to use label names starting with two letters, followed by numbers (this is why the source code is full of catchy labels like TT26 and LL9). Because we might end up talking about ship data in INWK, K% or XX1, this commentary refers to "ship byte #5" for byte #5 of the ship data (y_sign), or "ship byte #32" for byte #32 (the AI flag), and so on. Most of the time we will be working with INWK or XX1, but every now and then the bytes in the K% block are manipulated directly, which we will point out in the comments.
There are 36 bytes of data in each ship's block, and as mentioned above, they all have the same format, though not all bytes are used for all ship types. Planets, for example, don't have AI or missiles, though it would be fun if they did...
Let's take a look at the format of a typical ship data block.
Summary of the ship data block format
The bytes in each ship data block are arranged as follows:
- Bytes #0-2 Ship's x-coordinate in space = (x_sign x_hi x_lo)
- Bytes #3-5 Ship's y-coordinate in space = (y_sign y_hi y_lo)
- Bytes #6-8 Ship's z-coordinate in space = (z_sign z_hi z_lo)
- Bytes #9-14 Orientation vector nosev = [ nosev_x nosev_y nosev_z ]
- Bytes #15-19 Orientation vector roofv = [ roofv_x roofv_y roofv_z ]
- Bytes #21-26 Orientation vector sidev = [ sidev_x sidev_y sidev_z ]
- Byte #27 Speed
- Byte #28 Acceleration
- Byte #29 Roll counter
- Byte #30 Pitch counter
- Byte #31 Exploding state Killed state "Is being drawn on-screen" flag "Is visible on the scanner" flag Missile count
- Byte #32 AI flag Hostility flag Aggression level E.C.M. flag
- Bytes #33-34 Ship line heap address pointer
- Byte #35 Energy level
Let's look at these in more detail.
Ship coordinates (bytes #0-8)
These bytes contain the ship's location in space relative to our ship. The x-axis goes to the right, the y-axis goes up and the z-axis goes into the screen.
- Byte #0 = x_lo
- Byte #1 = x_hi
- Byte #2 = x_sign
- Byte #3 = y_lo
- Byte #4 = y_hi
- Byte #5 = y_sign
- Byte #6 = z_lo
- Byte #7 = z_hi
- Byte #8 = z_sign
The coordinates are stored as 24-bit sign-magnitude numbers, where the sign of the number is stored in bit 7 of the sign byte, and the other 23 bits contain the magnitude of the number without any sign (i.e. the absolute values |x|, |y| and |z|).
We can also write the coordinates like this:
- x-coordinate = (x_sign x_hi x_lo) = INWK(2 1 0)
- y-coordinate = (y_sign y_hi y_lo) = INWK(5 4 3)
- z-coordinate = (z_sign z_hi z_lo) = INWK(8 7 6)
Orientation vectors (bytes #9-26)
The ship's orientation vectors determine its orientation in space. There are three vectors, named according to the direction they point in (i.e. out of the ship's nose, the ship's roof, or the ship's right side):
- Byte #9 = nosev_x_lo
- Byte #10 = nosev_x_hi
- Byte #11 = nosev_y_lo
- Byte #12 = nosev_y_hi
- Byte #13 = nosev_z_lo
- Byte #14 = nosev_z_hi
- Byte #15 = roofv_x_lo
- Byte #16 = roofv_x_hi
- Byte #17 = roofv_y_lo
- Byte #18 = roofv_y_hi
- Byte #19 = roofv_z_lo
- Byte #20 = roofv_z_hi
- Byte #21 = sidev_x_lo
- Byte #22 = sidev_x_hi
- Byte #23 = sidev_y_lo
- Byte #24 = sidev_y_hi
- Byte #25 = sidev_z_lo
- Byte #26 = sidev_z_hi
The vector coordinates are stored as 16-bit sign-magnitude numbers, where the sign of the number is stored in bit 7 of the high byte. See the deep dive on orientation vectors for more information.
Ship movement (bytes #27-30)
This block controls the ship's movement in space.
- Byte #27 = Speed, in the range 1-40
- Byte #28 = Acceleration, which gets added to the speed once, in MVEIT, before being zeroed again
- Byte #29 = Roll counter
- Bits 0-6 = The counter. If this is 127 (%1111111) then damping is disabled and the ship keeps rolling forever, otherwise damping is enabled and the counter reduces by 1 for every iteration of the main flight loop. The ship rolls by a fixed amount (1/16 radians, or 3.6 degrees) for every iteration where the counter is > 0.
- Bit 7 = The direction of roll
- Byte #30 = Pitch counter
- Bits 0-6 = The counter. If this is 127 (%1111111) then damping is disabled and the ship keeps pitching forever, otherwise damping is enabled and the counter reduces by 1 for every iteration of the main flight loop. The ship pitches by a fixed amount (1/16 radians, or 3.6 degrees) for every iteration where the counter is > 0.
- Bit 7 = The direction of pitch
See the deep dive on pitching and rolling by a fixed angle for more details on the pitch and roll that the above counters apply to a ship.
Ship flags (bytes #31-32)
These two flags contain a lot of information about the ship, and they are consulted often.
- Byte #31 = Exploding state, Killed state, "Is being drawn on-screen" flag, "Is visible on the scanner" flag, Missile count
- Bits 0-2: %nnn = number of missiles or Thargons (maximum 7)
- Bit 3: 0 = isn't currently being drawn on-screen 1 = is currently being drawn on-screen
- Bit 4: 0 = don't show on scanner 1 = do show on scanner
- Bit 5: 0 = ship is not exploding 1 = ship is exploding
- Bit 6: 0 = ship is not firing lasers 1 = ship is firing lasers 0 = explosion has not been drawn 1 = explosion has been drawn
- Bit 7: 0 = ship has not been killed 1 = ship has been killed
- Byte #32 = AI flag, Hostility flag, Aggression level, E.C.M. flag
- For ships:
- Bit 0: 0 = no E.C.M. 1 = has E.C.M.
- Bits 1-5: n = aggression level (see TACTICS part 7)
- Bit 6: 0 = friendly 1 = hostile
- Bit 7: 0 = dumb 1 = AI enabled (tactics get applied by the TACTICS routine)
- For the space station:
- Bit 7: 0 = friendly 1 = hostile
- For missiles:
- Bit 0: 0 = no lock/launched 1 = target locked
- Bits 1-4: %nnnn = target's slot number (maximum 15)
- Bit 6: 0 = friendly 1 = hostile
- Bit 7: 0 = dumb 1 = AI enabled (tactics get applied)
Heap pointer and energy (bytes #33-34)
The final two bytes are as follows:
- Byte #33 = low byte of ship line heap address pointer in INWK(34 33)
- Byte #34 = high byte of ship line heap address pointer in INWK(34 33)
- Byte #35 = ship energy