tron@2186: /* $Id$ */ tron@2186: rubidium@10429: /** @file airport.cpp Functions related to airports. */ belugas@6443: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@1299: #include "debug.h" truelight@0: #include "airport.h" peter1138@3701: #include "airport_movement.h" rubidium@8609: #include "core/bitmath_func.hpp" rubidium@8626: #include "core/alloc_func.hpp" rubidium@8636: #include "date_func.h" rubidium@8766: #include "settings_type.h" truelight@0: Darkvater@4843: /* Uncomment this to print out a full report of the airport-structure Darkvater@4843: * You should either use Darkvater@4843: * - true: full-report, print out every state and choice with string-names Darkvater@4843: * OR Darkvater@4843: * - false: give a summarized report which only shows current and next position */ Darkvater@4843: //#define DEBUG_AIRPORT false Darkvater@4843: celestar@6890: static AirportFTAClass *DummyAirport; belugas@4171: static AirportFTAClass *CountryAirport; belugas@4171: static AirportFTAClass *CityAirport; belugas@4171: static AirportFTAClass *Oilrig; belugas@4171: static AirportFTAClass *Heliport; belugas@4171: static AirportFTAClass *MetropolitanAirport; belugas@4171: static AirportFTAClass *InternationalAirport; belugas@4171: static AirportFTAClass *CommuterAirport; belugas@4171: static AirportFTAClass *HeliDepot; belugas@4171: static AirportFTAClass *IntercontinentalAirport; belugas@4171: static AirportFTAClass *HeliStation; truelight@0: truelight@0: rubidium@6573: void InitializeAirports() truelight@0: { celestar@6890: DummyAirport = new AirportFTAClass( celestar@6890: _airport_moving_data_dummy, celestar@6890: NULL, celestar@6890: NULL, celestar@6890: _airport_entries_dummy, celestar@6890: AirportFTAClass::ALL, celestar@6890: _airport_fta_dummy, celestar@6890: NULL, celestar@6890: 0, belugas@10682: 0, 0, 0, celestar@6890: 0, celestar@6890: 0 celestar@6890: ); celestar@6890: tron@6183: CountryAirport = new AirportFTAClass( tron@6175: _airport_moving_data_country, tron@1019: _airport_terminal_country, tron@1019: NULL, celestar@6362: _airport_entries_country, tron@6351: AirportFTAClass::ALL | AirportFTAClass::SHORT_STRIP, tron@1019: _airport_fta_country, tron@1019: _airport_depots_country, tron@3876: lengthof(_airport_depots_country), belugas@10682: 4, 3, 3, tron@6367: 0, tron@6367: 4 celestar@950: ); truelight@0: tron@6183: CityAirport = new AirportFTAClass( tron@6175: _airport_moving_data_town, tron@1019: _airport_terminal_city, tron@1019: NULL, celestar@6362: _airport_entries_city, tron@6350: AirportFTAClass::ALL, tron@1019: _airport_fta_city, tron@1019: _airport_depots_city, tron@3876: lengthof(_airport_depots_city), belugas@10682: 6, 6, 5, tron@6367: 0, tron@6367: 5 celestar@950: ); truelight@0: tron@6183: MetropolitanAirport = new AirportFTAClass( tron@6175: _airport_moving_data_metropolitan, tron@1019: _airport_terminal_metropolitan, tron@1019: NULL, celestar@6362: _airport_entries_metropolitan, tron@6350: AirportFTAClass::ALL, tron@1019: _airport_fta_metropolitan, tron@1019: _airport_depots_metropolitan, tron@3876: lengthof(_airport_depots_metropolitan), belugas@10682: 6, 6, 8, tron@6367: 0, tron@6367: 6 celestar@950: ); truelight@0: tron@6183: InternationalAirport = new AirportFTAClass( tron@6175: _airport_moving_data_international, tron@1019: _airport_terminal_international, tron@1019: _airport_helipad_international, celestar@6362: _airport_entries_international, tron@6350: AirportFTAClass::ALL, tron@1019: _airport_fta_international, tron@1019: _airport_depots_international, tron@3876: lengthof(_airport_depots_international), belugas@10682: 7, 7, 17, tron@6367: 0, tron@6367: 8 celestar@950: ); truelight@0: tron@6183: IntercontinentalAirport = new AirportFTAClass( tron@6175: _airport_moving_data_intercontinental, richk@4059: _airport_terminal_intercontinental, richk@4059: _airport_helipad_intercontinental, celestar@6362: _airport_entries_intercontinental, tron@6350: AirportFTAClass::ALL, richk@4059: _airport_fta_intercontinental, richk@4059: _airport_depots_intercontinental, richk@4059: lengthof(_airport_depots_intercontinental), belugas@10682: 9, 11, 25, tron@6367: 0, tron@6367: 10 richk@4059: ); richk@4059: tron@6183: Heliport = new AirportFTAClass( tron@6175: _airport_moving_data_heliport, tron@1019: NULL, tron@1019: _airport_helipad_heliport_oilrig, celestar@6362: _airport_entries_heliport_oilrig, tron@6350: AirportFTAClass::HELICOPTERS, tron@1019: _airport_fta_heliport_oilrig, tron@1019: NULL, tron@3876: 0, belugas@10682: 1, 1, 1, tron@6367: 60, tron@6367: 4 celestar@950: ); tron@1019: tron@6183: Oilrig = new AirportFTAClass( tron@6175: _airport_moving_data_oilrig, tron@6175: NULL, tron@6175: _airport_helipad_heliport_oilrig, celestar@6362: _airport_entries_heliport_oilrig, tron@6350: AirportFTAClass::HELICOPTERS, tron@6175: _airport_fta_heliport_oilrig, tron@6175: NULL, tron@6175: 0, belugas@10682: 1, 1, 0, tron@6367: 54, tron@6367: 3 tron@6175: ); richk@4059: tron@6183: CommuterAirport = new AirportFTAClass( tron@6175: _airport_moving_data_commuter, richk@4059: _airport_terminal_commuter, richk@4059: _airport_helipad_commuter, celestar@6362: _airport_entries_commuter, tron@6351: AirportFTAClass::ALL | AirportFTAClass::SHORT_STRIP, richk@4059: _airport_fta_commuter, richk@4059: _airport_depots_commuter, richk@4059: lengthof(_airport_depots_commuter), belugas@10682: 5, 4, 4, tron@6367: 0, tron@6367: 4 richk@4059: ); richk@4059: tron@6183: HeliDepot = new AirportFTAClass( tron@6175: _airport_moving_data_helidepot, richk@4059: NULL, richk@4059: _airport_helipad_helidepot, celestar@6362: _airport_entries_helidepot, tron@6350: AirportFTAClass::HELICOPTERS, richk@4059: _airport_fta_helidepot, richk@4059: _airport_depots_helidepot, richk@4059: lengthof(_airport_depots_helidepot), belugas@10682: 2, 2, 2, tron@6367: 0, tron@6367: 4 richk@4059: ); richk@4059: tron@6183: HeliStation = new AirportFTAClass( tron@6175: _airport_moving_data_helistation, richk@4059: NULL, richk@4059: _airport_helipad_helistation, celestar@6362: _airport_entries_helistation, tron@6350: AirportFTAClass::HELICOPTERS, richk@4059: _airport_fta_helistation, richk@4059: _airport_depots_helistation, richk@4059: lengthof(_airport_depots_helistation), belugas@10682: 4, 2, 3, tron@6367: 0, tron@6367: 4 richk@4059: ); truelight@0: } truelight@0: rubidium@6573: void UnInitializeAirports() truelight@0: { rubidium@8300: delete DummyAirport; tron@6183: delete CountryAirport; tron@6183: delete CityAirport; tron@6183: delete Heliport; tron@6183: delete MetropolitanAirport; tron@6183: delete InternationalAirport; tron@6183: delete CommuterAirport; tron@6183: delete HeliDepot; tron@6183: delete IntercontinentalAirport; tron@6183: delete HeliStation; truelight@0: } truelight@0: tron@6183: tron@6183: static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA); tron@6183: static AirportFTA* AirportBuildAutomata(uint nofelements, const AirportFTAbuildup *apFA); tron@6183: static byte AirportGetTerminalCount(const byte *terminals, byte *groups); tron@6183: static byte AirportTestFTA(uint nofelements, const AirportFTA *layout, const byte *terminals); tron@6183: tron@6183: #ifdef DEBUG_AIRPORT tron@6183: static void AirportPrintOut(uint nofelements, const AirportFTA *layout, bool full_report); tron@6183: #endif tron@6183: tron@6183: tron@6183: AirportFTAClass::AirportFTAClass( tron@6183: const AirportMovingData *moving_data_, tron@6183: const byte *terminals_, tron@6183: const byte *helipads_, celestar@6362: const byte *entry_points_, tron@6350: Flags flags_, Darkvater@4842: const AirportFTAbuildup *apFA, tron@6183: const TileIndexDiffC *depots_, tron@6183: const byte nof_depots_, tron@6183: uint size_x_, tron@6346: uint size_y_, belugas@10682: byte noise_level_, tron@6367: byte delta_z_, tron@6367: byte catchment_ tron@6183: ) : tron@6183: moving_data(moving_data_), tron@6183: terminals(terminals_), tron@6183: helipads(helipads_), tron@6183: airport_depots(depots_), tron@6350: flags(flags_), tron@6183: nof_depots(nof_depots_), tron@6189: nofelements(AirportGetNofElements(apFA)), celestar@6362: entry_points(entry_points_), tron@6183: size_x(size_x_), tron@6346: size_y(size_y_), belugas@10682: noise_level(noise_level_), tron@6367: delta_z(delta_z_), tron@6367: catchment(catchment_) truelight@0: { Darkvater@4843: byte nofterminalgroups, nofhelipadgroups; tron@1019: Darkvater@4843: /* Set up the terminal and helipad count for an airport. Darkvater@4843: * TODO: If there are more than 10 terminals or 4 helipads, internal variables Darkvater@4843: * need to be changed, so don't allow that for now */ tron@6183: uint nofterminals = AirportGetTerminalCount(terminals, &nofterminalgroups); Darkvater@4843: if (nofterminals > MAX_TERMINALS) { Darkvater@5568: DEBUG(misc, 0, "[Ap] only a maximum of %d terminals are supported (requested %d)", MAX_TERMINALS, nofterminals); Darkvater@4843: assert(nofterminals <= MAX_TERMINALS); celestar@950: } celestar@950: tron@6183: uint nofhelipads = AirportGetTerminalCount(helipads, &nofhelipadgroups); Darkvater@4843: if (nofhelipads > MAX_HELIPADS) { Darkvater@5568: DEBUG(misc, 0, "[Ap] only a maximum of %d helipads are supported (requested %d)", MAX_HELIPADS, nofhelipads); Darkvater@4843: assert(nofhelipads <= MAX_HELIPADS); celestar@950: } celestar@950: Darkvater@4843: /* Get the number of elements from the source table. We also double check this Darkvater@4843: * with the entry point which must be within bounds and use this information Darkvater@4843: * later on to build and validate the state machine */ celestar@6362: for (DiagDirection i = DIAGDIR_BEGIN; i < DIAGDIR_END; i++) { celestar@6362: if (entry_points[i] >= nofelements) { celestar@6362: DEBUG(misc, 0, "[Ap] entry (%d) must be within the airport (maximum %d)", entry_points[i], nofelements); celestar@6362: assert(entry_points[i] < nofelements); celestar@6362: } Darkvater@4843: } truelight@0: Darkvater@4843: /* Build the state machine itself */ tron@6183: layout = AirportBuildAutomata(nofelements, apFA); celestar@6362: DEBUG(misc, 2, "[Ap] #count %3d; #term %2d (%dgrp); #helipad %2d (%dgrp); entries %3d, %3d, %3d, %3d", celestar@6362: nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, celestar@6362: entry_points[DIAGDIR_NE], entry_points[DIAGDIR_SE], entry_points[DIAGDIR_SW], entry_points[DIAGDIR_NW]); truelight@0: Darkvater@4843: /* Test if everything went allright. This is only a rude static test checking Darkvater@4843: * the symantic correctness. By no means does passing the test mean that the Darkvater@4843: * airport is working correctly or will not deadlock for example */ tron@6183: uint ret = AirportTestFTA(nofelements, layout, terminals); tron@6183: if (ret != MAX_ELEMENTS) DEBUG(misc, 0, "[Ap] problem with element: %d", ret - 1); tron@6183: assert(ret == MAX_ELEMENTS); Darkvater@4843: Darkvater@4843: #ifdef DEBUG_AIRPORT tron@6183: AirportPrintOut(nofelements, layout, DEBUG_AIRPORT); tron@4077: #endif truelight@0: } truelight@0: tron@6183: tron@6183: AirportFTAClass::~AirportFTAClass() truelight@0: { tron@6183: for (uint i = 0; i < nofelements; i++) { tron@6183: AirportFTA *current = layout[i].next; truelight@0: while (current != NULL) { tron@6183: AirportFTA *next = current->next; truelight@0: free(current); truelight@0: current = next; truelight@0: }; truelight@0: } tron@6183: free(layout); truelight@0: } truelight@0: Darkvater@4843: /** Get the number of elements of a source Airport state automata Darkvater@4843: * Since it is actually just a big array of AirportFTA types, we only Darkvater@4843: * know one element from the other by differing 'position' identifiers */ Darkvater@4842: static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA) truelight@0: { truelight@0: uint16 nofelements = 0; Darkvater@4842: int temp = apFA[0].position; tron@2549: tron@6413: for (uint i = 0; i < MAX_ELEMENTS; i++) { Darkvater@4842: if (temp != apFA[i].position) { truelight@0: nofelements++; Darkvater@4842: temp = apFA[i].position; truelight@0: } Darkvater@4842: if (apFA[i].position == MAX_ELEMENTS) break; truelight@0: } truelight@0: return nofelements; truelight@0: } truelight@0: Darkvater@4843: /* We calculate the terminal/helipod count based on the data passed to us Darkvater@4843: * This data (terminals) contains an index as a first element as to how many Darkvater@4843: * groups there are, and then the number of terminals for each group */ Darkvater@4843: static byte AirportGetTerminalCount(const byte *terminals, byte *groups) Darkvater@4843: { Darkvater@4843: byte nof_terminals = 0; Darkvater@4843: *groups = 0; Darkvater@4843: Darkvater@4843: if (terminals != NULL) { tron@6413: uint i = terminals[0]; Darkvater@4843: *groups = i; Darkvater@4843: while (i-- > 0) { Darkvater@4843: terminals++; Darkvater@4843: assert(*terminals != 0); // no empty groups please Darkvater@4843: nof_terminals += *terminals; Darkvater@4843: } Darkvater@4843: } Darkvater@4843: return nof_terminals; Darkvater@4843: } Darkvater@4843: tron@6183: tron@6183: static AirportFTA* AirportBuildAutomata(uint nofelements, const AirportFTAbuildup *apFA) truelight@0: { tron@6183: AirportFTA *FAutomata = MallocT(nofelements); Darkvater@4843: uint16 internalcounter = 0; Darkvater@4843: tron@6183: for (uint i = 0; i < nofelements; i++) { tron@6413: AirportFTA *current = &FAutomata[i]; Darkvater@4843: current->position = apFA[internalcounter].position; Darkvater@4843: current->heading = apFA[internalcounter].heading; Darkvater@4843: current->block = apFA[internalcounter].block; Darkvater@4842: current->next_position = apFA[internalcounter].next; truelight@0: truelight@0: // outgoing nodes from the same position, create linked list Darkvater@4842: while (current->position == apFA[internalcounter + 1].position) { KUDr@5860: AirportFTA *newNode = MallocT(1); tron@2549: Darkvater@4843: newNode->position = apFA[internalcounter + 1].position; Darkvater@4843: newNode->heading = apFA[internalcounter + 1].heading; Darkvater@4843: newNode->block = apFA[internalcounter + 1].block; Darkvater@4842: newNode->next_position = apFA[internalcounter + 1].next; truelight@0: // create link Darkvater@4842: current->next = newNode; Darkvater@4842: current = current->next; truelight@0: internalcounter++; tron@6413: } Darkvater@4842: current->next = NULL; truelight@0: internalcounter++; truelight@0: } tron@6183: return FAutomata; truelight@0: } truelight@0: tron@6183: tron@6183: static byte AirportTestFTA(uint nofelements, const AirportFTA *layout, const byte *terminals) truelight@0: { tron@6183: uint next_position = 0; truelight@0: tron@6183: for (uint i = 0; i < nofelements; i++) { tron@6183: uint position = layout[i].position; Darkvater@4843: if (position != next_position) return i; tron@6183: const AirportFTA *first = &layout[i]; truelight@0: tron@6413: for (const AirportFTA *current = first; current != NULL; current = current->next) { Darkvater@4844: /* A heading must always be valid. The only exceptions are Darkvater@4844: * - multiple choices as start, identified by a special value of 255 Darkvater@4844: * - terminal group which is identified by a special value of 255 */ Darkvater@4844: if (current->heading > MAX_HEADINGS) { Darkvater@4844: if (current->heading != 255) return i; Darkvater@4844: if (current == first && current->next == NULL) return i; tron@6183: if (current != first && current->next_position > terminals[0]) return i; Darkvater@4844: } Darkvater@4844: Darkvater@4844: /* If there is only one choice, it must be at the end */ Darkvater@4844: if (current->heading == 0 && current->next != NULL) return i; Darkvater@4844: /* Obviously the elements of the linked list must have the same identifier */ Darkvater@4843: if (position != current->position) return i; Darkvater@4844: /* A next position must be within bounds */ tron@6183: if (current->next_position >= nofelements) return i; Darkvater@4844: } Darkvater@4843: next_position++; truelight@0: } truelight@0: return MAX_ELEMENTS; truelight@0: } truelight@0: Darkvater@4843: #ifdef DEBUG_AIRPORT tron@2549: static const char* const _airport_heading_strings[] = { truelight@0: "TO_ALL", truelight@0: "HANGAR", truelight@0: "TERM1", truelight@0: "TERM2", truelight@0: "TERM3", truelight@0: "TERM4", truelight@0: "TERM5", truelight@0: "TERM6", truelight@0: "HELIPAD1", truelight@0: "HELIPAD2", truelight@0: "TAKEOFF", truelight@0: "STARTTAKEOFF", truelight@0: "ENDTAKEOFF", truelight@0: "HELITAKEOFF", truelight@0: "FLYING", truelight@0: "LANDING", truelight@0: "ENDLANDING", truelight@0: "HELILANDING", truelight@0: "HELIENDLANDING", richk@4059: "TERM7", richk@4059: "TERM8", richk@4059: "HELIPAD3", richk@4059: "HELIPAD4", rubidium@4434: "DUMMY" // extra heading for 255 truelight@0: }; truelight@0: tron@6183: static void AirportPrintOut(uint nofelements, const AirportFTA *layout, bool full_report) truelight@0: { Darkvater@4843: if (!full_report) printf("(P = Current Position; NP = Next Position)\n"); Darkvater@4843: tron@6183: for (uint i = 0; i < nofelements; i++) { tron@6413: for (const AirportFTA *current = &layout[i]; current != NULL; current = current->next) { truelight@0: if (full_report) { Darkvater@4843: byte heading = (current->heading == 255) ? MAX_HEADINGS + 1 : current->heading; Darkvater@4843: printf("\tPos:%2d NPos:%2d Heading:%15s Block:%2d\n", current->position, Darkvater@4843: current->next_position, _airport_heading_strings[heading], rubidium@8497: FindLastBit(current->block)); tron@2549: } else { Darkvater@4843: printf("P:%2d NP:%2d", current->position, current->next_position); truelight@0: } truelight@0: } truelight@0: printf("\n"); truelight@0: } truelight@0: } tron@2549: #endif truelight@0: belugas@4171: const AirportFTAClass *GetAirport(const byte airport_type) truelight@0: { truelight@0: //FIXME -- AircraftNextAirportPos_and_Order -> Needs something nicer, don't like this code truelight@0: // needs constant change if more airports are added truelight@0: switch (airport_type) { tron@4077: default: NOT_REACHED(); tron@4077: case AT_SMALL: return CountryAirport; tron@4077: case AT_LARGE: return CityAirport; tron@4077: case AT_METROPOLITAN: return MetropolitanAirport; tron@4077: case AT_HELIPORT: return Heliport; tron@4077: case AT_OILRIG: return Oilrig; tron@4077: case AT_INTERNATIONAL: return InternationalAirport; tron@4077: case AT_COMMUTER: return CommuterAirport; tron@4077: case AT_HELIDEPOT: return HeliDepot; tron@4077: case AT_INTERCON: return IntercontinentalAirport; tron@4077: case AT_HELISTATION: return HeliStation; celestar@6890: case AT_DUMMY: return DummyAirport; truelight@0: } truelight@0: } tron@2159: peter1138@3701: rubidium@6573: uint32 GetValidAirports() tron@2159: { tron@6222: uint32 mask = 0; tron@2159: rubidium@10775: if (_cur_year < 1960 || _settings_game.station.always_small_airport) SetBit(mask, 0); // small airport skidd13@8427: if (_cur_year >= 1955) SetBit(mask, 1); // city airport skidd13@8427: if (_cur_year >= 1963) SetBit(mask, 2); // heliport skidd13@8427: if (_cur_year >= 1980) SetBit(mask, 3); // metropolitan airport skidd13@8427: if (_cur_year >= 1990) SetBit(mask, 4); // international airport skidd13@8427: if (_cur_year >= 1983) SetBit(mask, 5); // commuter airport skidd13@8427: if (_cur_year >= 1976) SetBit(mask, 6); // helidepot skidd13@8427: if (_cur_year >= 2002) SetBit(mask, 7); // intercontinental airport skidd13@8427: if (_cur_year >= 1980) SetBit(mask, 8); // helistation tron@6222: return mask; tron@2159: }