src/airport.c
author rubidium
Tue, 09 Jan 2007 14:48:21 +0000
changeset 5572 a98ffa55b19c
parent 5475 2e6990a8c7c4
permissions -rw-r--r--
(svn r8000) -Codechange: drop UDP packets when their internal size does not match the received size. If that is the case, the packet was not received in one piece (or got somehow mangled with another packet), which will cause us to drop the packet later on because we are (for example) trying to read beyond the end of the packet.
/* $Id$ */

#include "stdafx.h"
#include "openttd.h"
#include "debug.h"
#include "map.h"
#include "airport.h"
#include "macros.h"
#include "variables.h"
#include "airport_movement.h"
#include "date.h"

/* Uncomment this to print out a full report of the airport-structure
 * You should either use
 * - true: full-report, print out every state and choice with string-names
 * OR
 * - false: give a summarized report which only shows current and next position */
//#define DEBUG_AIRPORT false

static AirportFTAClass *CountryAirport;
static AirportFTAClass *CityAirport;
static AirportFTAClass *Oilrig;
static AirportFTAClass *Heliport;
static AirportFTAClass *MetropolitanAirport;
static AirportFTAClass *InternationalAirport;
static AirportFTAClass *CommuterAirport;
static AirportFTAClass *HeliDepot;
static AirportFTAClass *IntercontinentalAirport;
static AirportFTAClass *HeliStation;

static void AirportFTAClass_Constructor(AirportFTAClass *apc,
	const byte *terminals, const byte *helipads,
	const byte entry_point,  const byte acc_planes,
	const AirportFTAbuildup *apFA,
	const TileIndexDiffC *depots, const byte nof_depots,
	uint size_x, uint size_y
);
static void AirportFTAClass_Destructor(AirportFTAClass *apc);

static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA);
static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA);
static byte AirportGetTerminalCount(const byte *terminals, byte *groups);
static byte AirportTestFTA(const AirportFTAClass *apc);

#ifdef DEBUG_AIRPORT
static void AirportPrintOut(const AirportFTAClass *apc, bool full_report);
#endif /* DEBUG_AIRPORT */

void InitializeAirports(void)
{
	// country airport
	CountryAirport = malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		CountryAirport,
		_airport_terminal_country,
		NULL,
		16,
		ALL,
		_airport_fta_country,
		_airport_depots_country,
		lengthof(_airport_depots_country),
		4, 3
	);

	// city airport
	CityAirport = malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		CityAirport,
		_airport_terminal_city,
		NULL,
		19,
		ALL,
		_airport_fta_city,
		_airport_depots_city,
		lengthof(_airport_depots_city),
		6, 6
	);

	// metropolitan airport
	MetropolitanAirport = malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		MetropolitanAirport,
		_airport_terminal_metropolitan,
		NULL,
		20,
		ALL,
		_airport_fta_metropolitan,
		_airport_depots_metropolitan,
		lengthof(_airport_depots_metropolitan),
		6, 6
	);

	// international airport
	InternationalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		InternationalAirport,
		_airport_terminal_international,
		_airport_helipad_international,
		37,
		ALL,
		_airport_fta_international,
		_airport_depots_international,
		lengthof(_airport_depots_international),
		7, 7
	);

	// intercontintental airport
	IntercontinentalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		IntercontinentalAirport,
		_airport_terminal_intercontinental,
		_airport_helipad_intercontinental,
		43,
		ALL,
		_airport_fta_intercontinental,
		_airport_depots_intercontinental,
		lengthof(_airport_depots_intercontinental),
		9,11
	);

	// heliport, oilrig
	Heliport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		Heliport,
		NULL,
		_airport_helipad_heliport_oilrig,
		7,
		HELICOPTERS_ONLY,
		_airport_fta_heliport_oilrig,
		NULL,
		0,
		1, 1
	);

	Oilrig = Heliport;  // exactly the same structure for heliport/oilrig, so share state machine

	// commuter airport
	CommuterAirport = malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		CommuterAirport,
		_airport_terminal_commuter,
		_airport_helipad_commuter,
		22,
		ALL,
		_airport_fta_commuter,
		_airport_depots_commuter,
		lengthof(_airport_depots_commuter),
		5,4
	);

	// helidepot airport
	HeliDepot = malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		HeliDepot,
		NULL,
		_airport_helipad_helidepot,
		4,
		HELICOPTERS_ONLY,
		_airport_fta_helidepot,
		_airport_depots_helidepot,
		lengthof(_airport_depots_helidepot),
		2,2
	);

	// helistation airport
	HeliStation = malloc(sizeof(AirportFTAClass));

	AirportFTAClass_Constructor(
		HeliStation,
		NULL,
		_airport_helipad_helistation,
		25,
		HELICOPTERS_ONLY,
		_airport_fta_helistation,
		_airport_depots_helistation,
		lengthof(_airport_depots_helistation),
		4,2
	);

}

void UnInitializeAirports(void)
{
	AirportFTAClass_Destructor(CountryAirport);
	AirportFTAClass_Destructor(CityAirport);
	AirportFTAClass_Destructor(Heliport);
	AirportFTAClass_Destructor(MetropolitanAirport);
	AirportFTAClass_Destructor(InternationalAirport);
	AirportFTAClass_Destructor(CommuterAirport);
	AirportFTAClass_Destructor(HeliDepot);
	AirportFTAClass_Destructor(IntercontinentalAirport);
	AirportFTAClass_Destructor(HeliStation);
}

static void AirportFTAClass_Constructor(AirportFTAClass *apc,
	const byte *terminals, const byte *helipads,
	const byte entry_point, const byte acc_planes,
	const AirportFTAbuildup *apFA,
	const TileIndexDiffC *depots, const byte nof_depots,
	uint size_x, uint size_y
)
{
	byte nofterminals, nofhelipads;
	byte nofterminalgroups, nofhelipadgroups;

	apc->size_x = size_x;
	apc->size_y = size_y;

	/* Set up the terminal and helipad count for an airport.
	 * TODO: If there are more than 10 terminals or 4 helipads, internal variables
	 * need to be changed, so don't allow that for now */
	nofterminals = AirportGetTerminalCount(terminals, &nofterminalgroups);
	if (nofterminals > MAX_TERMINALS) {
		DEBUG(misc, 0, "[Ap] only a maximum of %d terminals are supported (requested %d)", MAX_TERMINALS, nofterminals);
		assert(nofterminals <= MAX_TERMINALS);
	}
	apc->terminals = terminals;

	nofhelipads = AirportGetTerminalCount(helipads, &nofhelipadgroups);
	if (nofhelipads > MAX_HELIPADS) {
		DEBUG(misc, 0, "[Ap] only a maximum of %d helipads are supported (requested %d)", MAX_HELIPADS, nofhelipads);
		assert(nofhelipads <= MAX_HELIPADS);
	}
	apc->helipads = helipads;

	/* Get the number of elements from the source table. We also double check this
	 * with the entry point which must be within bounds and use this information
	 * later on to build and validate the state machine */
	apc->nofelements = AirportGetNofElements(apFA);
	if (entry_point >= apc->nofelements) {
		DEBUG(misc, 0, "[Ap] entry (%d) must be within the airport (maximum %d)", entry_point, apc->nofelements);
		assert(entry_point < apc->nofelements);
	}

	apc->acc_planes     = acc_planes;
	apc->entry_point    = entry_point;
	apc->airport_depots = depots;
	apc->nof_depots     = nof_depots;

	/* Build the state machine itself */
	AirportBuildAutomata(apc, apFA);
	DEBUG(misc, 2, "[Ap] #count %3d; #term %2d (%dgrp); #helipad %2d (%dgrp); entry %3d",
		apc->nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, apc->entry_point);

	/* Test if everything went allright. This is only a rude static test checking
	 * the symantic correctness. By no means does passing the test mean that the
	 * airport is working correctly or will not deadlock for example */
	{ byte ret = AirportTestFTA(apc);
		if (ret != MAX_ELEMENTS) DEBUG(misc, 0, "[Ap] problem with element: %d", ret - 1);
		assert(ret == MAX_ELEMENTS);
	}

#ifdef DEBUG_AIRPORT
	AirportPrintOut(apc, DEBUG_AIRPORT);
#endif
}

static void AirportFTAClass_Destructor(AirportFTAClass *apc)
{
	int i;
	AirportFTA *current, *next;

	for (i = 0; i < apc->nofelements; i++) {
		current = apc->layout[i].next;
		while (current != NULL) {
			next = current->next;
			free(current);
			current = next;
		};
	}
	free(apc->layout);
	free(apc);
}

/** Get the number of elements of a source Airport state automata
 * Since it is actually just a big array of AirportFTA types, we only
 * know one element from the other by differing 'position' identifiers */
static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA)
{
	int i;
	uint16 nofelements = 0;
	int temp = apFA[0].position;

	for (i = 0; i < MAX_ELEMENTS; i++) {
		if (temp != apFA[i].position) {
			nofelements++;
			temp = apFA[i].position;
		}
		if (apFA[i].position == MAX_ELEMENTS) break;
	}
	return nofelements;
}

/* We calculate the terminal/helipod count based on the data passed to us
 * This data (terminals) contains an index as a first element as to how many
 * groups there are, and then the number of terminals for each group */
static byte AirportGetTerminalCount(const byte *terminals, byte *groups)
{
	byte i;
	byte nof_terminals = 0;
	*groups = 0;

	if (terminals != NULL) {
		i = terminals[0];
		*groups = i;
		while (i-- > 0) {
			terminals++;
			assert(*terminals != 0); // no empty groups please
			nof_terminals += *terminals;
		}
	}
	return nof_terminals;
}

static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA)
{
	AirportFTA *current;
	AirportFTA *FAutomata = malloc(sizeof(AirportFTA) * apc->nofelements);
	uint16 internalcounter = 0;
	uint16 i;

	apc->layout = FAutomata;
	for (i = 0; i < apc->nofelements; i++) {
		current = &apc->layout[i];
		current->position      = apFA[internalcounter].position;
		current->heading       = apFA[internalcounter].heading;
		current->block         = apFA[internalcounter].block;
		current->next_position = apFA[internalcounter].next;

		// outgoing nodes from the same position, create linked list
		while (current->position == apFA[internalcounter + 1].position) {
			AirportFTA *newNode = malloc(sizeof(AirportFTA));

			newNode->position      = apFA[internalcounter + 1].position;
			newNode->heading       = apFA[internalcounter + 1].heading;
			newNode->block         = apFA[internalcounter + 1].block;
			newNode->next_position = apFA[internalcounter + 1].next;
			// create link
			current->next = newNode;
			current = current->next;
			internalcounter++;
		} // while
		current->next = NULL;
		internalcounter++;
	}
}

static byte AirportTestFTA(const AirportFTAClass *apc)
{
	byte position, i, next_position;
	AirportFTA *current, *first;
	next_position = 0;

	for (i = 0; i < apc->nofelements; i++) {
		position = apc->layout[i].position;
		if (position != next_position) return i;
		current = first = &apc->layout[i];

		for (; current != NULL; current = current->next) {
			/* A heading must always be valid. The only exceptions are
			 * - multiple choices as start, identified by a special value of 255
			 * - terminal group which is identified by a special value of 255 */
			if (current->heading > MAX_HEADINGS) {
				if (current->heading != 255) return i;
				if (current == first && current->next == NULL) return i;
				if (current != first && current->next_position > apc->terminals[0]) return i;
			}

			/* If there is only one choice, it must be at the end */
			if (current->heading == 0 && current->next != NULL) return i;
			/* Obviously the elements of the linked list must have the same identifier */
			if (position != current->position) return i;
			/* A next position must be within bounds */
			if (current->next_position >= apc->nofelements) return i;
		}
		next_position++;
	}
	return MAX_ELEMENTS;
}

#ifdef DEBUG_AIRPORT
static const char* const _airport_heading_strings[] = {
	"TO_ALL",
	"HANGAR",
	"TERM1",
	"TERM2",
	"TERM3",
	"TERM4",
	"TERM5",
	"TERM6",
	"HELIPAD1",
	"HELIPAD2",
	"TAKEOFF",
	"STARTTAKEOFF",
	"ENDTAKEOFF",
	"HELITAKEOFF",
	"FLYING",
	"LANDING",
	"ENDLANDING",
	"HELILANDING",
	"HELIENDLANDING",
	"TERM7",
	"TERM8",
	"HELIPAD3",
	"HELIPAD4",
	"DUMMY" // extra heading for 255
};

static uint AirportBlockToString(uint32 block)
{
	uint i = 0;
	if (block & 0xffff0000) { block >>= 16; i += 16; }
	if (block & 0x0000ff00) { block >>=  8; i +=  8; }
	if (block & 0x000000f0) { block >>=  4; i +=  4; }
	if (block & 0x0000000c) { block >>=  2; i +=  2; }
	if (block & 0x00000002) { i += 1; }
	return i;
}

static void AirportPrintOut(const AirportFTAClass *apc, bool full_report)
{
	uint16 i;

	if (!full_report) printf("(P = Current Position; NP = Next Position)\n");

	for (i = 0; i < apc->nofelements; i++) {
		AirportFTA *current = &apc->layout[i];

		for (; current != NULL; current = current->next) {
			if (full_report) {
				byte heading = (current->heading == 255) ? MAX_HEADINGS + 1 : current->heading;
				printf("\tPos:%2d NPos:%2d Heading:%15s Block:%2d\n", current->position,
					    current->next_position, _airport_heading_strings[heading],
							AirportBlockToString(current->block));
			} else {
				printf("P:%2d NP:%2d", current->position, current->next_position);
			}
		}
		printf("\n");
	}
}
#endif

const AirportFTAClass *GetAirport(const byte airport_type)
{
	//FIXME -- AircraftNextAirportPos_and_Order -> Needs something nicer, don't like this code
	// needs constant change if more airports are added
	switch (airport_type) {
		default:               NOT_REACHED();
		case AT_SMALL:         return CountryAirport;
		case AT_LARGE:         return CityAirport;
		case AT_METROPOLITAN:  return MetropolitanAirport;
		case AT_HELIPORT:      return Heliport;
		case AT_OILRIG:        return Oilrig;
		case AT_INTERNATIONAL: return InternationalAirport;
		case AT_COMMUTER:      return CommuterAirport;
		case AT_HELIDEPOT:     return HeliDepot;
		case AT_INTERCON:      return IntercontinentalAirport;
		case AT_HELISTATION:   return HeliStation;
	}
}

const AirportMovingData *GetAirportMovingData(byte airport_type, byte position)
{
	assert(airport_type < lengthof(_airport_moving_datas));
	assert(position < GetAirport(airport_type)->nofelements);
	return &_airport_moving_datas[airport_type][position];
}

uint32 GetValidAirports(void)
{
	uint32 bytemask = _avail_aircraft; /// sets the first 3 bytes, 0 - 2, @see AdjustAvailAircraft()

	if (_cur_year >= 1980) SETBIT(bytemask, 3); // metropolitan airport
	if (_cur_year >= 1990) SETBIT(bytemask, 4); // international airport
	if (_cur_year >= 1983) SETBIT(bytemask, 5); // commuter airport
	if (_cur_year >= 1976) SETBIT(bytemask, 6); // helidepot
	if (_cur_year >= 2002) SETBIT(bytemask, 7); // intercontinental airport
	if (_cur_year >= 1980) SETBIT(bytemask, 8); // helistation
	return bytemask;
}