Newgrf Airports Documentation
This is a proposed spec only
This is a proposal for a newgrf airports spec. Whereas the current spec being worked on includes a full state machine, this one attempts to cut as much "hard code" out as possible, leaving a lightweight callback system which is, hopefully, both flexible and powerful, while being as simple as possible to implement.
In many ways, you could see this as my attempt to contribute to newgrf airports, since I can't/don't write OTTD patches, but I do write newgrfs. By moving as much codewriting into newgrf as possible, hopefully we can see newgrf airports included sooner than they otherwise would be.
Premise
This spec treats airports in a similar way to industries; it divides them into "airport tiles", which are the graphical component, and an "airport", which contains the logic. The state machine consists of a list of node positions held in the airport, and a callback that is run when an aircraft interacts with them.
Contents |
Airport Tiles
Airport tiles are the graphical component of the airport. They have no direct interaction or effect on the state machine on or aircraft.
Action 0 Airport Tiles
- Land shape flags
- Animation information
- Animation speed
as per Properties for industry tiles 0D, 0F and 10
- Tile Subname string (ie, clicking ? on this tile will give you "AirportName (TileSubname)")
- Hangar #: Clicking on this tile will open the hangar window or create a hangar order.
Action 2 Airport Tiles
as per Action 2 for houses and industry tiles
Var Action 2 Airport Tiles
as per Variational Action 2 Variables for Industry Tiles, except of course "industry" -> "airport".
Callbacks for Airport Tiles
- Next animation frame (1A/26/141)
- Animation control (1B/25/140)
- Custom shape check (2F)
- Decide drawing default foundations (30)
- Disable autosloping for industry tiles (3C)
Airports
Airports are the logical component of the airport. The airport contains the state machine.
Action 0 Airports
- Small/Large/Helipad/Oilrig (note: for backwards compatibility with vehicle var 44)
- Years available
- Catchment area
- Noise level
- Text string to show in the build list
- Cost per new tile when building
- Cost per overbuilt tile when building
- Yearly facility maintanance cost (possibly ?)
- Station name: four text IDs to use as station names if available, otherwise defaults to the old "x Airport" or "x Anystationname".
- Tile array similar to Industry Property 0A
- Node array. Each node has an x,y,z position, and a flag byte (hangar, contact point, loading bay) used for overbuilding, buying/selling aircraft in the hangar and, in the case of the contact point(s), aircraft interception from outside the state machine.
Action 4 Airports
- Name
- Class (?)
Action 2 Airports
The icon to show in the build list.
Var Action 2 Airports
- Airport type ID
- Year built
- Days since an aircraft last arrived or was loading at any node
- Days since an aircraft last arrived or was loading at a specific node (60+)
- Quantity of passengers waiting at the airport
- Quantity of mail waiting at the airport
- Quantity and ID of most common other cargo waiting at the airport
- Variable 7C, 16 4-byte slots of persistent data
Callbacks for Airports
state machine callback
The state machine callback is called when an aircraft wants to leave a node. It returns 15 bits, with the 3rd nibble containing the following flags:
- 0 = fly/taxi to the node specified in the low byte
- 1 = change the movement state to the value specified in the low byte and reiterate
- 2 = hold while taxiing. turn to the heading specified in the low byte
- 8 = begin loading/unloading
- 9 = service the aircraft (at a non-hangar node) and reiterate
- A = play the sound effect specified in the low byte and reiterate
- C = throw the alert message specified in the low byte, change plan to "heading for a hangar" and reiterate
- D = throw the alert message specified in the low byte, skip to next order, change plan to "heading for an exit" and reiterate
- E = throw the alert message specified in the low byte and stop the aircraft (if it's on the ground) or leave the state machine and move to the next order (if it's in the air)
- F = leave the state machine. Skip to next order if plan was not "heading for an exit"
(note: stopping the aircraft is probably not a good idea unless the aircraft is in a hangar!)
Var 10 contains the number of the node that was triggered. Var 11 contains information on the aircraft's current plan:
- 0 = heading for a bay
- 1 = heading for a hangar
- 2 = heading for a hangar (for servicing only)
- 3 = heading for an exit
- 10+ = high nibble contains the reiteration count. Aircraft can be processed up to 6 times per tick(?)
Type 82 within the callback gets the variables of the aircraft which triggered the callback. This callback + Variable 7C completely replaces the hardcoded state machine!!
Aircraft
Aircraft gain Variable 7C, and an additional property to provide information to the airport state machine.
Action 0 Aircraft
Basic aircraft information.
A byte-sized property consisting of:
- 01-0F = runway length required
- 10 = large aircraft/airship (if helicopter)
- 20 = seaplane (only)
- 40 = amphibious
- 80 = special aircraft (airport may want to send it to a special bay/apron).
This property goes into 'uu' in Vehicle Variable 42.
Overbuilding
Airports can be overbuilt like stations, even if aircraft are currently on the airport. Building a new airport tile over an existing airport tile can have a different cost from building one on empty ground (see airport action 0s above). When an airport is overbuilt, the airport's persistent data variables are reset, and the following happens to the aircraft on the airport:
- All aircraft currently loading will abort loading, have their plan changed to "heading for a bay", will be placed in the same bay number of the new airport (if it exists) or in a random bay (if their current bay doesn't exist), and will then run the state machine callback to lock themselves in. Loading aircraft that don't fit will be moved to the first defined hangar.
- All other aircraft on the ground will be moved to the airport's first defined hangar.
- All aircraft in the air will move to the first defined contact point.
If the airport wants to move aircraft to a hangar but doesn't have one, the overbuilding will fail.
Example Airports
Dusty Airpark
This is an early 2x4 small airport. It has 0x12 nodes defined in the airport action 0, including 3 contact points (0 2 5), four bays (C D E F) and one hangar (10). For the number of available bays it is quite compact, although there are no seperate taxiways so congestion is possible with too many aircraft.Airships can land, depart, and load/unload at (B). They do not fit in the hangar. This is not really a suitable airport for airships.
Simple logic description
Landing and taxing in
Aircraft will intercept the airport at the closest contact point. They will then change to the approach pattern state (10) and fly the pattern (1 2 3 4 6) until a landing runway becomes available. Only aircraft with a runway requirement of 4 or less can land.
Aircraft which have landed on Rwy L will taxi via (D F) in order of preference, or (F D) for helicopters. If (D F) are both blocked, aircraft will wait at their current node (8 or 7).
Aircraft which have landed on the Rwy R and want to go to a bay will taxi to bay (C E) in order of preference, or (E C) for helicopters. If they want the hangar, they will taxi directly via (B 10).
From the bay
Aircraft in bay (C) which want to depart will taxi directly onto Rwy R (A 9). If they want the hangar they can taxi directly (A B 10). Aircraft in bay (D) which want to depart or visit the hangar will taxi via (C) if it is clear, or else via Rwy L (8 7 (F)) if there are not too many aircraft waiting to land. Aircraft in bay (E) which want to depart will taxi directly onto Rwy R (9). If they want the hangar they can taxi directly (9 B 10). Aircraft in bay (F) which want to depart will taxi directly onto Rwy R (E 9) if (E) is clear, or else onto Rwy L (7). If they want the hangar they will taxi via (E 9 B 10).
From the hangar
Aircraft who want to move from the hangar to a bay will taxi to (E C) in order of preference via (B). Aircraft which want to take off from the hangar will taxi directly via (B 9).
Example NFO
WARNING: Since this is obviously impossible to test at the moment, the code below may contain numerous errors both logical and typographical. Please ask questions on the talk page or on the forums.
// Use airport 7C 01 to for the statemachine. Bit values: // 01 0 = Left runway occupied // 02 1 = Right runway occupied // 04 2 = Aircraft waiting to move from D or F // 08 3 = Aircraft waiting to move from C, E or 10 // 10 4 = Bay C occupied // 20 5 = Bay D occupied // 40 6 = Bay E occupied // 80 7 = Bay F occupied // 00 01 8 = 01 - 0F = count of aircraft in holding pattern // 00 02 9 = 01 - 0F = count of aircraft in holding pattern // 00 04 a = 01 - 0F = count of aircraft in holding pattern // 00 08 b = 01 - 0F = count of aircraft in holding pattern // 00 10 c = Aircraft from hangar is heading for C // 00 20 d = // 00 40 e = // 00 80 f = // 00 00 01 = // 00 00 02 = // 00 00 04 = // 00 00 08 = // 00 00 10 = // 00 00 20 = // 00 00 40 = // 00 00 80 = // 00 00 00 01 = // 00 00 00 02 = // 00 00 00 04 = // 00 00 00 08 = // 00 00 00 10 = // 00 00 00 20 = // 00 00 00 40 = // 00 00 00 80 = 1 * 1 02 0D FF <build menu graphics> // state machine callback ================================================================================ // runway nodes ========================================= // node 08 ========== end Rwy L // helo: 1 * 1 02 0D A7 81 11 00 F0 01 17 81 00 00 11 80 // set movement state to htakeoff (17) and target to node 11. 1 * 1 02 0D A7 85 7C 01 20 FF FE \2sto 1A 00 01 \w1 A7 00 00 00 A7 00 // taking off: unlock Rwy L, 1 * 1 02 0D 07 81 11 00 F0 01 07 81 00 00 07 80 // set movement state to taxi (07) and target to node 07. 1 * 1 02 0D 07 85 7C 01 20 FF 7F \2+ 1A 20 80 00 \2sto 1A 00 01 \w1 07 00 00 00 07 00 // taxiing to F: lock 0F 1 * 1 02 0D 07 81 11 00 F0 01 07 81 00 00 0D 80 // set movement state to taxi (07) and target to node 0D. 1 * 1 02 0D 07 85 7C 01 20 FF DE \2+ 1A 20 20 00 \2sto 1A 00 01 \w1 07 00 00 00 07 00 // taxiing to D: unlock Rwy L, lock 0D // vv F+D- vvD+ vv wait 1 * 1 02 0D B7 81 7C 01 00 A0 02 8F 00 20 20 8D 00 80 A0 03 82 // taxiing: check availability of nodes (0D) and (0F) 1 * 1 02 0D B7 81 11 00 FF 01 A7 00 03 03 B7 00 // plan is takeoff? // aircraft: 1 * 1 02 0D 07 81 11 00 F0 01 07 81 00 00 07 80 // set movement state to taxi (07) and target to node 07. 1 * 1 02 0D 07 85 7C 01 20 FF 7F \2+ 1A 20 80 00 \2sto 1A 00 01 \w1 07 00 00 00 07 00 // taxiing to F: lock 0F 1 * 1 02 0D 07 81 11 00 F0 01 07 81 00 00 0D 80 // set movement state to taxi (07) and target to node 0D. 1 * 1 02 0D 07 85 7C 01 20 FF DE \2+ 1A 20 20 00 \2sto 1A 00 01 \w1 07 00 00 00 07 00 // taxiing to D: unlock Rwy L, lock 0D // vv F+D- vvD+ vv wait 1 * 1 02 0D B7 81 7C 01 00 A0 02 8F 00 20 20 8D 00 80 A0 03 82 // taxiing: check availability of nodes (0D) and (0F) 1 * 1 02 0D A7 81 11 00 F0 01 0D 81 00 00 08 80 // set movement state to takeoff (0D) and target to node 8. 1 * 1 02 0D 07 81 11 00 FF 01 A7 00 03 03 07 00 // taxiing: plan is takeoff? 1 * 1 02 0D A7 81 11 00 F0 01 07 81 00 00 03 82 // rollout: change mode to taxi, turn to heading 03 1 * 1 02 0D 07 82 E2 00 FF 02 A7 00 16 16 A8 0D 0D 07 00 // check movementstate (rollout 16 or taking off 0D or taxiing), and 1 * 1 02 0D 08 82 81 00 FF 01 B7 00 00 00 07 00 // aircraft or helicopter? // node 07 ========== threshold Rwy L // helo: 1 * 1 02 0D A7 81 11 00 F0 01 17 81 00 00 11 80 // set movement state to htakeoff (17) and target to node 11. 1 * 1 02 0D A7 85 7C 01 20 FF FE \2sto 1A 00 01 \w1 A7 00 00 00 A7 00 // taking off: unlock Rwy L, 1 * 1 02 0D B7 81 11 00 F0 01 07 81 00 00 08 80 // set movement state to taxi (07) and target to node 08. 1 * 1 02 0D 07 81 11 00 F0 01 07 81 00 00 0F 80 // set movement state to taxi (07) and target to node 0F. 1 * 1 02 0D 07 85 7C 01 20 FF 7E \2+ 1A 20 80 00 \2sto 1A 00 01 \w1 07 00 00 00 07 00 // lock Bay F, unlock Rwy L, 1 * 1 02 0D B7 81 7C 01 07 01 01 07 00 00 00 B7 00 // if Bay F is available, 1 * 1 02 0D B7 81 11 00 FF 01 A7 00 03 03 B7 00 // plan is takeoff? // aircraft: 1 * 1 02 0D 07 81 11 00 F0 01 07 81 00 00 0F 80 // set movement state to taxi (07) and target to node 0F. 1 * 1 02 0D 07 85 7C 01 20 FF FE \2sto 1A 00 01 \w1 07 00 00 00 07 00 // taxiing: unlock Rwy L, 1 * 1 02 0D A7 81 11 00 F0 01 0D 81 00 00 08 80 // set movement state to takeoff (0D) and target to node 8. 1 * 1 02 0D 07 81 11 00 FF 01 A7 00 03 03 07 00 // plan is takeoff? 1 * 1 02 0D A7 81 11 00 F0 02 16 81 00 00 <sfx> 8A 10 10 08 80 // landing: rollout (16), play landing sound, set target to node 08 1 * 1 02 0D 07 82 E2 00 FF 01 A7 00 15 15 07 00 // check movementstate (landing 15 or taxiing), and 1 * 1 02 0D 07 82 81 00 FF 01 B7 00 00 00 07 00 // aircraft or helicopter? // end runway nodes ===================================== // approach nodes ======================================= // node 04 ========== final turn Rwy R 1 * 1 02 0D A4 81 11 00 F0 01 15 81 00 00 0B 80 // set state to landing (15) and set target to 0B for airship 1 * 1 02 0D 04 81 11 00 F0 01 15 81 00 00 09 80 // set state to landing (15) and set target to 09, or 1 * 1 02 0D A4 82 42 10 10 01 A4 00 10 10 04 00 // check if airship, and 1 * 1 02 0D 04 85 7C 01 20 FF FF \2- 1A 20 00 \b1 \2sto 1A 00 01 00 01 A4 00 00 00 A4 00 // -1 to the circuit count 1 * 1 02 0A A4 85 7C 01 00 00 0F 01 A4 00 00 00 00 00 04 00 // if circuit count > 0, 1 * 1 02 0D A4 85 7C 01 20 FD FF \2+ 1A 20 02 00 \2sto 1A 00 01 00 01 A4 00 00 00 A4 00 // lock Rwy R, and 1 * 1 02 0D 04 81 7C 01 01 01 01 A4 00 00 00 06 80 // if Rwy R is available, // node 03 ========== final turn Rwy L 1 * 1 02 0D 03 81 7C 01 03 01 01 04 80 00 00 06 80 // if there is no aircraft waiting to take off Rwy R, try our luck at node 4, otherwise, go around to node 6 1 * 1 02 0D A3 81 11 00 F0 01 15 81 00 00 07 80 // set state to landing (15) and set target to 07 1 * 1 02 0D 03 85 7C 01 20 FF FF \2- 1A 20 00 \b1 \2sto 1A 00 01 00 01 A3 00 00 00 A3 00 // -1 to the circuit count 1 * 1 02 0A A3 85 7C 01 00 00 0F 01 A3 00 00 00 00 00 03 00 // if circuit count > 0, 1 * 1 02 0D A3 85 7C 01 20 FE FF \2+ 1A 20 01 00 \2sto 1A 00 01 00 01 A3 00 00 00 A3 00 // lock Rwy L, and 1 * 1 02 0D 03 81 7C 01 00 01 01 A3 00 00 00 03 00 // if Rwy L is available, 1 * 1 02 0D A3 82 42 10 10 01 <runway too short to land> 8D 10 10 03 00 // no runway req, check if "large", else you land at your own risk! 1 * 1 02 0D 03 82 42 10 0F 02 A3 00 00 00 03 00 01 04 <runway too short to land> 8D // runway length check before landing 1 * 1 02 0D 03 82 81 00 FF 01 04 80 00 00 03 00 // head for node 04 if the aircraft is a helicopter // node 02 ========== base leg holding pattern // this is the point where aircraft change state to (10) and slow down for the holding pattern. // if the aircraft has just arrived (which we can tell by checking if it's state is already (10), // we add + to the aircraft hold counter - this counter will be used to help try and streamline // aircraft movements on the ground. 1 * 1 02 0D 02 81 11 00 F0 01 10 81 00 00 03 80 // set state to holding pattern (10) and target to node 03 1 * 1 02 0D A2 85 7C 01 20 FF FF \2+ 1A 20 00 \b1 \2sto 1A 00 01 00 01 02 00 00 00 02 00 // +1 to the circuit count 1 * 1 02 0A A2 85 7C 01 00 00 0F 01 A2 00 00 00 00 0E 02 00 // if circuit count < E, then 1 * 1 02 0D 02 82 E2 00 FF 01 02 00 10 10 A2 00 // if the aircraft is not already holding (E2 movementstate = 10), and // end approach nodes =================================== 1 * 1 02 0D AA 81 10 00 FF 11 // which node is triggered? 00 00 01 80 // node 00 = north contact point, head for node 01 01 00 02 80 // node 01 = downwind leg holding pattern, head for node 02 02 00 02 02 03 00 03 03 04 00 04 04 05 00 01 80 // node 05 = east contact point, head for node 01 06 00 01 80 // node 06 = crosswind leg holding pattern, head for node 01 07 00 07 07 08 00 08 08 09 00 09 09 0A 00 0A 0A 0B 00 0B 0B 0C 00 0C 0C 0D 00 0D 0D 0E 00 0E 0E 0F 00 0F 0F 10 00 10 10 00 8F // leave the state machine and fly to next destination 1 * 1 02 0D AA 85 0C 00 FF FF 01 AA 00 <callback #> FF 00 // statemachine callback // end state machine callback ============================================================================ 1 * 1 03 0D 01 04 01 FF FF 00 AA 00