Airport/Source/Aircraft.c

937 lines
29 KiB
C

/* *************************************
* Includes
* *************************************/
#include "Aircraft.h"
#include "System.h"
#include "Game.h"
#include "Camera.h"
#include "LoadMenu.h"
/* *************************************
* Defines
* *************************************/
#define AIRCRAFT_SIZE 16
#define AIRCRAFT_SIZE_FIX16 fix16_from_int(AIRCRAFT_SIZE)
#define AIRCRAFT_INVALID_IDX 0xFF
/* *************************************
* Structs and enums
* *************************************/
enum
{
AIRCRAFT_SPRITE_W = 24,
AIRCRAFT_SPRITE_H = 16,
AIRCRAFT_SPRITE_VRAM_X = 800,
AIRCRAFT_SPRITE_VRAM_Y = 256,
};
enum
{
PHX_LIVERY_CLUT_X = 384,
PHX_LIVERY_CLUT_Y = 497,
};
typedef enum t_aircraftSpeeds
{
AIRCRAFT_SPEED_IDLE = 0,
AIRCRAFT_SPEED_GROUND,
AIRCRAFT_SPEED_APPROACH,
AIRCRAFT_SPEED_TAKEOFF,
AIRCRAFT_SPEED_FINAL,
AIRCRAFT_SPEED_FINAL_Z,
}AIRCRAFT_SPEEDS;
/* *************************************
* Local variables
* *************************************/
static TYPE_AIRCRAFT_DATA AircraftData[GAME_MAX_AIRCRAFT];
static uint8_t aircraftIndex;
static GsSprite AircraftSpr;
static GsSprite UpDownArrowSpr;
static GsSprite LeftRightArrowSpr;
static TYPE_ISOMETRIC_POS AircraftCenterIsoPos;
static TYPE_CARTESIAN_POS AircraftCenterPos;
static AIRCRAFT_LIVERY AircraftLiveryTable[] = {AIRCRAFT_LIVERY_0, AIRCRAFT_LIVERY_UNKNOWN};
// Used to quickly link FlightData indexes against AircraftData indexes.
static uint8_t flightDataIdxTable[GAME_MAX_AIRCRAFT];
static const fix16_t AircraftSpeedsTable[] =
{
[AIRCRAFT_SPEED_IDLE] = 0,
[AIRCRAFT_SPEED_GROUND] = 0x9999,
[AIRCRAFT_SPEED_TAKEOFF] = 0x20000,
[AIRCRAFT_SPEED_FINAL] = 0x10000,
[AIRCRAFT_SPEED_FINAL_Z] = 0x3000
};
/* *************************************
* Local prototypes
* *************************************/
static void AircraftDirection(TYPE_AIRCRAFT_DATA* const ptrAircraft);
static AIRCRAFT_LIVERY AircraftLiveryFromFlightNumber(char* strFlightNumber);
static void AircraftAttitude(TYPE_AIRCRAFT_DATA* const ptrAircraft);
static void AircraftUpdateSpriteFromData(TYPE_AIRCRAFT_DATA* const ptrAircraft);
static void AircraftSpeed(TYPE_AIRCRAFT_DATA* const ptrAircraft);
static bool AircraftCheckCollision(const TYPE_AIRCRAFT_DATA* const ptrRefAircraft, const TYPE_AIRCRAFT_DATA* const ptrOtherAircraft);
static bool AircraftCheckPath(TYPE_AIRCRAFT_DATA* ptrAicraft, TYPE_AIRCRAFT_DATA* ptrOtherAircraft);
void AircraftInit(void)
{
static bool initialised;
bzero(AircraftData, GAME_MAX_AIRCRAFT * sizeof (TYPE_AIRCRAFT_DATA));
aircraftIndex = 0;
AircraftSpr.x = 0;
AircraftSpr.y = 0;
AircraftSpr.attribute = COLORMODE(COLORMODE_8BPP);
AircraftSpr.cx = PHX_LIVERY_CLUT_X;
AircraftSpr.cy = PHX_LIVERY_CLUT_Y;
AircraftSpr.w = AIRCRAFT_SPRITE_W;
AircraftSpr.h = AIRCRAFT_SPRITE_H;
GfxTPageOffsetFromVRAMPosition(&AircraftSpr, AIRCRAFT_SPRITE_VRAM_X, AIRCRAFT_SPRITE_VRAM_Y);
AircraftCenterIsoPos.x = AIRCRAFT_SIZE >> 1;
AircraftCenterIsoPos.y = AIRCRAFT_SIZE >> 1;
AircraftCenterIsoPos.z = 0;
AircraftCenterPos = GfxIsometricToCartesian(&AircraftCenterIsoPos);
memset(flightDataIdxTable, AIRCRAFT_INVALID_IDX, sizeof (flightDataIdxTable));
if (initialised == false)
{
static const char* const GameFileList[] =
{
"DATA\\SPRITES\\UDNARROW.TIM",
"DATA\\SPRITES\\LFRARROW.TIM"
};
static void* const GameFileDest[] =
{
&UpDownArrowSpr,
&LeftRightArrowSpr
};
initialised = true;
LOAD_FILES(GameFileList, GameFileDest);
}
}
bool AircraftAddNew( TYPE_FLIGHT_DATA* const ptrFlightData,
uint8_t FlightDataIndex,
uint16_t* targets,
DIRECTION direction )
{
if (aircraftIndex < GAME_MAX_AIRCRAFT)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = &AircraftData[aircraftIndex];
memmove(ptrAircraft->Target, targets, sizeof (uint16_t) * AIRCRAFT_MAX_TARGETS);
ptrAircraft->TargetIdx = 0;
ptrAircraft->Livery = AircraftLiveryFromFlightNumber(ptrFlightData->strFlightNumber[FlightDataIndex]);
ptrAircraft->FlightDataIdx = FlightDataIndex;
Serial_printf("ptrAircraft->FlightDataIdx = %d, FlightDataIndex = %d\n", ptrAircraft->FlightDataIdx, FlightDataIndex);
if (ptrFlightData->FlightDirection[FlightDataIndex] == ARRIVAL)
{
const uint8_t level_columns = GameGetLevelColumns();
switch (direction)
{
case DIR_EAST:
ptrAircraft->IsoPos.x = 0;
ptrAircraft->IsoPos.y = targets[0] / level_columns;
ptrAircraft->IsoPos.y <<= TILE_SIZE_BIT_SHIFT;
ptrAircraft->IsoPos.y += TILE_SIZE >> 1; // Adjust to tile center
ptrAircraft->IsoPos.y = fix16_from_int(ptrAircraft->IsoPos.y);
ptrAircraft->IsoPos.z = targets[0] % level_columns;
ptrAircraft->IsoPos.z <<= TILE_SIZE_BIT_SHIFT - 2;
ptrAircraft->IsoPos.z += 8;
ptrAircraft->IsoPos.z = fix16_from_int(ptrAircraft->IsoPos.z);
break;
case DIR_SOUTH:
ptrAircraft->IsoPos.x = targets[0] % level_columns;
ptrAircraft->IsoPos.x <<= TILE_SIZE_BIT_SHIFT;
ptrAircraft->IsoPos.x += TILE_SIZE >> 1; // Adjust to tile center
ptrAircraft->IsoPos.x = fix16_from_int(ptrAircraft->IsoPos.x);
ptrAircraft->IsoPos.y = 0;
ptrAircraft->IsoPos.z = targets[0] / level_columns;
ptrAircraft->IsoPos.z <<= TILE_SIZE_BIT_SHIFT - 2;
ptrAircraft->IsoPos.z += 8;
ptrAircraft->IsoPos.z = fix16_from_int(ptrAircraft->IsoPos.z);
break;
case NO_DIRECTION:
// Fall through
default:
Serial_printf("Invalid runway direction %d for inbound flight.\n", direction);
return false;
}
}
else if (ptrFlightData->FlightDirection[FlightDataIndex] == DEPARTURE)
{
if (direction == NO_DIRECTION)
{
Serial_printf("Invalid direction for outbound flight.\n");
return false;
}
ptrAircraft->IsoPos.x = GameGetXFromTile(ptrFlightData->Parking[FlightDataIndex]);
ptrAircraft->IsoPos.y = GameGetYFromTile(ptrFlightData->Parking[FlightDataIndex]);
ptrAircraft->IsoPos.z = 0;
}
ptrAircraft->Direction = direction;
ptrAircraft->State = ptrFlightData->State[FlightDataIndex];
flightDataIdxTable[FlightDataIndex] = aircraftIndex;
Serial_printf("\nAircraft Data:\n");
Serial_printf("\tTargets:");
{
uint8_t i;
for (i = 0; i < AIRCRAFT_MAX_TARGETS; i++)
{
if (ptrAircraft->Target[i] == 0)
{
break;
}
Serial_printf(" %d", ptrAircraft->Target[i]);
}
}
Serial_printf("\n\tDirection: %d\n", ptrAircraft->Direction);
Serial_printf("\nLivery: %d\n", ptrAircraft->Livery );
Serial_printf("Aircraft position: {%d, %d, %d}\n",
fix16_to_int(ptrAircraft->IsoPos.x),
fix16_to_int(ptrAircraft->IsoPos.y),
fix16_to_int(ptrAircraft->IsoPos.z) );
aircraftIndex++;
return true;
}
else
{
Serial_printf("Exceeded maximum aircraft capacity!\n");
}
return false;
}
static AIRCRAFT_LIVERY AircraftLiveryFromFlightNumber(char* strFlightNumber)
{
static const char* const AircraftLiveryNamesTable[] =
{
"PHX",
NULL
};
int32_t liveryIndex;
char strLivery[4];
memset(strLivery, 0, ARRAY_SIZE(strLivery) );
strncpy(strLivery, strFlightNumber, 3);
liveryIndex = SystemIndexOfStringArray(strLivery, AircraftLiveryNamesTable);
if (liveryIndex == -1)
{
return AIRCRAFT_LIVERY_UNKNOWN;
}
return AircraftLiveryTable[liveryIndex];
}
bool AircraftRemove(uint8_t aircraftIdx)
{
if (aircraftIdx != AIRCRAFT_INVALID_IDX)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(aircraftIdx);
if (ptrAircraft->State != STATE_IDLE)
{
if (ptrAircraft->FlightDataIdx == aircraftIdx)
{
ptrAircraft->State = STATE_IDLE;
Serial_printf("Flight %d removed\n", ptrAircraft->FlightDataIdx);
return true;
}
}
}
return false;
}
void AircraftHandler(void)
{
uint8_t i;
for (i = 0; i < GAME_MAX_AIRCRAFT; i++)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = &AircraftData[i];
bool collision_warning = false;
if (ptrAircraft->State != STATE_IDLE)
{
uint8_t j;
AircraftDirection(ptrAircraft);
AircraftAttitude(ptrAircraft);
AircraftSpeed(ptrAircraft);
// Check collision against all other aircraft.
for (j = 0; j < GAME_MAX_AIRCRAFT; j++)
{
TYPE_AIRCRAFT_DATA* ptrOtherAircraft = &AircraftData[j];
if (ptrOtherAircraft->State == STATE_IDLE)
{
continue;
}
if (i == j)
{
continue;
}
else
{
// Check whether aircraft should stop in order to avoid collision against
// other aircraft.
// WARNING: only STATE_TAXIING can be used to automatically stop an aircraft
// when calling GameStopFlight() or GameResumeFlightFromAutoStop().
collision_warning |= AircraftCheckPath(ptrAircraft, ptrOtherAircraft);
}
if (j > i)
{
if (AircraftCheckCollision(ptrAircraft, ptrOtherAircraft))
{
GameAircraftCollision(ptrAircraft->FlightDataIdx);
break;
}
}
else
{
// Collision already calculated.
}
}
if (collision_warning)
{
GameStopFlight(ptrAircraft->FlightDataIdx);
}
else
{
GameResumeFlightFromAutoStop(ptrAircraft->FlightDataIdx);
}
ptrAircraft->State = GameGetFlightDataStateFromIdx(ptrAircraft->FlightDataIdx);
}
}
}
static bool AircraftCheckPath(TYPE_AIRCRAFT_DATA* const ptrAircraft, TYPE_AIRCRAFT_DATA* ptrOtherAircraft)
{
const uint16_t currentTile = AircraftGetTileFromFlightDataIndex(ptrAircraft->FlightDataIdx);
uint16_t nextTile; // Keep compiler happy
switch (ptrAircraft->Direction)
{
case DIR_EAST:
nextTile = currentTile + 1;
break;
case DIR_WEST:
nextTile = currentTile - 1;
break;
case DIR_NORTH:
nextTile = currentTile - GameGetLevelColumns();
break;
case DIR_SOUTH:
nextTile = currentTile + GameGetLevelColumns();
break;
case NO_DIRECTION:
// Fall through
default:
Serial_printf("AircraftCheckPath: Undefined direction\n");
return false;
}
{
const uint16_t otherAircraft_currentTile = AircraftGetTileFromFlightDataIndex(ptrOtherAircraft->FlightDataIdx);
if ( (otherAircraft_currentTile == nextTile)
||
(otherAircraft_currentTile == currentTile) )
{
if (ptrOtherAircraft->Speed == 0)
{
// Make ptrAircraft stop if ptrOtherAircraft is nearby and not moving!
return true;
}
}
}
return false;
}
static void AircraftSpeed(TYPE_AIRCRAFT_DATA* const ptrAircraft)
{
switch(ptrAircraft->State)
{
case STATE_FINAL:
ptrAircraft->Speed = AircraftSpeedsTable[AIRCRAFT_SPEED_FINAL];
break;
case STATE_TAKEOFF:
// Fall through
case STATE_CLIMBING:
ptrAircraft->Speed = AircraftSpeedsTable[AIRCRAFT_SPEED_TAKEOFF];
break;
case STATE_TAXIING:
// Fall through
case STATE_ENTERING_RWY:
ptrAircraft->Speed = AircraftSpeedsTable[AIRCRAFT_SPEED_GROUND];
break;
case STATE_USER_STOPPED:
// Fall through
case STATE_AUTO_STOPPED:
// Fall through
case STATE_READY_FOR_TAKEOFF:
// Fall through
case STATE_UNBOARDING:
// Fall through
case STATE_IDLE:
// Fall through
case STATE_LANDED:
// Fall through
case STATE_HOLDING_RWY:
// Fall through
default:
ptrAircraft->Speed = 0;
break;
}
}
void AircraftRender(TYPE_PLAYER* const ptrPlayer, uint8_t aircraftIdx)
{
if (aircraftIdx != AIRCRAFT_INVALID_IDX)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(aircraftIdx);
if (ptrAircraft != NULL)
{
if (ptrAircraft->State != STATE_IDLE)
{
AircraftUpdateSpriteFromData(ptrAircraft);
if (ptrAircraft->IsoPos.z > 0)
{
// Draw aircraft shadow
TYPE_ISOMETRIC_FIX16_POS shadowIsoPos;
shadowIsoPos.x = ptrAircraft->IsoPos.x;
shadowIsoPos.y = ptrAircraft->IsoPos.y;
shadowIsoPos.z = 0;
const TYPE_CARTESIAN_POS shadowCartPos = GfxIsometricFix16ToCartesian(&shadowIsoPos);
// Aircraft position is referred to aircraft center
AircraftSpr.x = shadowCartPos.x - (AircraftSpr.w >> 1);
AircraftSpr.y = shadowCartPos.y - (AircraftSpr.h >> 1);
CameraApplyCoordinatesToSprite(ptrPlayer, &AircraftSpr);
AircraftSpr.r = 0;
AircraftSpr.g = 0;
AircraftSpr.b = 0;
GfxSortSprite(&AircraftSpr);
}
const TYPE_CARTESIAN_POS cartPos = GfxIsometricFix16ToCartesian(&ptrAircraft->IsoPos);
// Aircraft position is referred to aircraft center
AircraftSpr.x = cartPos.x - (AircraftSpr.w >> 1);
AircraftSpr.y = cartPos.y - (AircraftSpr.h >> 1);
CameraApplyCoordinatesToSprite(ptrPlayer, &AircraftSpr);
if ((ptrPlayer->FlightDataSelectedAircraft == aircraftIdx)
&&
(ptrPlayer->ShowAircraftData))
{
static uint8_t aircraft_sine;
static bool aircraft_sine_decrease;
if (aircraft_sine_decrease == false)
{
if (aircraft_sine < 240)
{
aircraft_sine += 24;
}
else
{
aircraft_sine_decrease = true;
}
}
else
{
if (aircraft_sine > 24)
{
aircraft_sine -= 24;
}
else
{
aircraft_sine_decrease = false;
}
}
AircraftSpr.r = NORMAL_LUMINANCE >> 2;
AircraftSpr.g = NORMAL_LUMINANCE >> 2;
AircraftSpr.b = aircraft_sine;
if (GfxIsSpriteInsideScreenArea(&AircraftSpr) == false)
{
bool showLRArrow = false;
bool showUPDNArrow = false;
// When aircraft can't be shown on screen,
// show an arrow indicating its position.
if (AircraftSpr.x < 0)
{
LeftRightArrowSpr.x = 0;
LeftRightArrowSpr.attribute |= H_FLIP;
showLRArrow = true;
}
else if (AircraftSpr.x > X_SCREEN_RESOLUTION)
{
LeftRightArrowSpr.x = X_SCREEN_RESOLUTION - (LeftRightArrowSpr.w << 1);
LeftRightArrowSpr.attribute &= ~(H_FLIP);
showLRArrow = true;
}
else if (AircraftSpr.y < 0)
{
UpDownArrowSpr.y = 0;
UpDownArrowSpr.attribute &= ~(V_FLIP);
showUPDNArrow = true;
}
else if (AircraftSpr.y > Y_SCREEN_RESOLUTION)
{
UpDownArrowSpr.y = Y_SCREEN_RESOLUTION - (UpDownArrowSpr.h);
UpDownArrowSpr.attribute |= V_FLIP;
showUPDNArrow = true;
}
if (showLRArrow)
{
LeftRightArrowSpr.y = AircraftSpr.y;
// First, saturate calculated Y values to {0, Y_SCREEN_RESOLUTION - LeftRightArrowSpr.h}.
if (LeftRightArrowSpr.y < 0)
{
LeftRightArrowSpr.y = 0;
}
else if (LeftRightArrowSpr.y > (Y_SCREEN_RESOLUTION - LeftRightArrowSpr.h) )
{
LeftRightArrowSpr.y = (Y_SCREEN_RESOLUTION - LeftRightArrowSpr.h);
}
GfxSortSprite(&LeftRightArrowSpr);
}
else if (showUPDNArrow)
{
UpDownArrowSpr.x = AircraftSpr.x;
// First, saturate calculated Y values to {0, Y_SCREEN_RESOLUTION - UpDownArrowSpr.h}.
if (UpDownArrowSpr.x < 0)
{
UpDownArrowSpr.x = 0;
}
else if (UpDownArrowSpr.x > (X_SCREEN_RESOLUTION - (UpDownArrowSpr.w << 1) ) )
{
UpDownArrowSpr.x = (UpDownArrowSpr.w << 1);
}
GfxSortSprite(&UpDownArrowSpr);
}
}
}
else
{
AircraftSpr.r = NORMAL_LUMINANCE;
AircraftSpr.g = NORMAL_LUMINANCE;
AircraftSpr.b = NORMAL_LUMINANCE;
}
GfxSortSprite(&AircraftSpr);
}
}
}
}
static void AircraftDirection(TYPE_AIRCRAFT_DATA* const ptrAircraft)
{
TYPE_ISOMETRIC_FIX16_POS targetPos;
if (ptrAircraft->State != STATE_CLIMBING)
{
if (ptrAircraft->Target[ptrAircraft->TargetIdx] == 0)
{
return;
}
targetPos.x = GameGetXFromTile(ptrAircraft->Target[ptrAircraft->TargetIdx]);
targetPos.y = GameGetYFromTile(ptrAircraft->Target[ptrAircraft->TargetIdx]);
targetPos.z = 0;
ptrAircraft->TargetReached = false;
if (targetPos.y == ptrAircraft->IsoPos.y)
{
if (targetPos.x > ptrAircraft->IsoPos.x)
{
if (targetPos.x <= (ptrAircraft->IsoPos.x + ptrAircraft->Speed) )
{
ptrAircraft->TargetReached = true;
}
else
{
ptrAircraft->Direction = DIR_EAST;
ptrAircraft->IsoPos.x += ptrAircraft->Speed;
}
}
else if (targetPos.x < ptrAircraft->IsoPos.x)
{
if (targetPos.x >= (ptrAircraft->IsoPos.x - ptrAircraft->Speed) )
{
ptrAircraft->TargetReached = true;
}
else
{
ptrAircraft->Direction = DIR_WEST;
ptrAircraft->IsoPos.x -= ptrAircraft->Speed;
}
}
else
{
ptrAircraft->TargetReached = true;
}
}
else if (targetPos.x == ptrAircraft->IsoPos.x)
{
if (targetPos.y > ptrAircraft->IsoPos.y)
{
if (targetPos.y <= (ptrAircraft->IsoPos.y + ptrAircraft->Speed) )
{
ptrAircraft->TargetReached = true;
}
else
{
ptrAircraft->Direction = DIR_SOUTH;
ptrAircraft->IsoPos.y += ptrAircraft->Speed;
}
}
else if (targetPos.y < ptrAircraft->IsoPos.y)
{
if (targetPos.y >= (ptrAircraft->IsoPos.y - ptrAircraft->Speed) )
{
ptrAircraft->TargetReached = true;
}
else
{
ptrAircraft->Direction = DIR_NORTH;
ptrAircraft->IsoPos.y -= ptrAircraft->Speed;
}
}
else
{
ptrAircraft->TargetReached = true;
}
}
if (ptrAircraft->TargetReached)
{
ptrAircraft->IsoPos.x = targetPos.x;
ptrAircraft->IsoPos.y = targetPos.y;
if (ptrAircraft->Target[++ptrAircraft->TargetIdx] == 0)
{
Serial_printf("All targets reached!\n");
ptrAircraft->State = GameTargetsReached(ptrAircraft->Target[0], ptrAircraft->FlightDataIdx);
memset(ptrAircraft->Target, 0, AIRCRAFT_MAX_TARGETS);
}
}
}
else
{
// STATE_CLIMBING
switch(ptrAircraft->Direction)
{
case DIR_EAST:
ptrAircraft->IsoPos.x += ptrAircraft->Speed;
break;
case DIR_WEST:
ptrAircraft->IsoPos.x -= ptrAircraft->Speed;
break;
case DIR_NORTH:
ptrAircraft->IsoPos.y -= ptrAircraft->Speed;
break;
case DIR_SOUTH:
ptrAircraft->IsoPos.y += ptrAircraft->Speed;
break;
case NO_DIRECTION:
// Fall through
default:
return;
}
ptrAircraft->IsoPos.z += AircraftSpeedsTable[AIRCRAFT_SPEED_FINAL_Z];
if (GameInsideLevelFromIsoPos(&ptrAircraft->IsoPos) == false)
{
GameRemoveFlight(ptrAircraft->FlightDataIdx, true);
// Deactivate TYPE_AIRCRAFT instance.
ptrAircraft->State = STATE_IDLE;
}
}
}
static void AircraftUpdateSpriteFromData(TYPE_AIRCRAFT_DATA* const ptrAircraft)
{
switch(ptrAircraft->Livery)
{
case AIRCRAFT_LIVERY_0:
AircraftSpr.cx = PHX_LIVERY_CLUT_X;
AircraftSpr.cy = PHX_LIVERY_CLUT_Y;
break;
case AIRCRAFT_LIVERY_UNKNOWN:
// Fall through
default:
Serial_printf("Unknown livery %d!\n", ptrAircraft->Livery);
break;
}
// Reset TPAGE and {U, V} offset first.
GfxTPageOffsetFromVRAMPosition(&AircraftSpr, AIRCRAFT_SPRITE_VRAM_X, AIRCRAFT_SPRITE_VRAM_Y);
switch(ptrAircraft->Direction)
{
case DIR_NORTH:
AircraftSpr.v += AircraftSpr.h;
AircraftSpr.attribute |= H_FLIP;
break;
case DIR_SOUTH:
AircraftSpr.v += 0;
AircraftSpr.attribute |= H_FLIP;
break;
case DIR_EAST:
AircraftSpr.v += 0;
AircraftSpr.attribute &= ~(H_FLIP);
break;
case DIR_WEST:
AircraftSpr.v += AircraftSpr.h;
AircraftSpr.attribute &= ~(H_FLIP);
break;
case NO_DIRECTION:
// Fall through
default:
break;
}
}
static void AircraftAttitude(TYPE_AIRCRAFT_DATA* const ptrAircraft)
{
if (ptrAircraft->State == STATE_FINAL)
{
if (ptrAircraft->IsoPos.z > 0)
{
ptrAircraft->IsoPos.z -= AircraftSpeedsTable[AIRCRAFT_SPEED_FINAL_Z];
}
}
}
TYPE_ISOMETRIC_POS AircraftGetIsoPos(const uint8_t FlightDataIdx)
{
TYPE_ISOMETRIC_POS retIsoPos = {0};
if (FlightDataIdx != AIRCRAFT_INVALID_IDX)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(FlightDataIdx);
if (ptrAircraft != NULL)
{
// Aircraft position data is stored in fix16_t data type instead of "short" data type.
// So we must perform a conversion first for convenience.
const TYPE_ISOMETRIC_FIX16_POS fix16IsoPos = ptrAircraft->IsoPos;
retIsoPos.x = (short)fix16_to_int(fix16IsoPos.x);
retIsoPos.y = (short)fix16_to_int(fix16IsoPos.y);
retIsoPos.z = (short)fix16_to_int(fix16IsoPos.z);
}
}
return retIsoPos;
}
void AircraftAddTargets(TYPE_AIRCRAFT_DATA* const ptrAircraft, const uint16_t* const targets)
{
memmove(ptrAircraft->Target, targets, sizeof (uint16_t) * AIRCRAFT_MAX_TARGETS);
ptrAircraft->TargetIdx = 0;
}
uint16_t AircraftGetTileFromFlightDataIndex(const uint8_t index)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(index);
if (ptrAircraft != NULL)
{
if (ptrAircraft->State != STATE_IDLE)
{
TYPE_ISOMETRIC_POS isoPos = AircraftGetIsoPos(index);
return GameGetTileFromIsoPosition(&isoPos);
}
}
return 0;
}
TYPE_AIRCRAFT_DATA* AircraftFromFlightDataIndex(const uint8_t index)
{
if ((index != AIRCRAFT_INVALID_IDX)
&&
(index < GAME_MAX_AIRCRAFT))
{
const uint8_t idx = flightDataIdxTable[index];
if (idx != AIRCRAFT_INVALID_IDX)
{
return &AircraftData[idx];
}
}
return NULL;
}
void AircraftFromFlightDataIndexAddTargets(const uint8_t index, const uint16_t* const targets)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(index);
if (ptrAircraft != NULL)
{
AircraftAddTargets(ptrAircraft, targets);
}
}
DIRECTION AircraftGetDirection(TYPE_AIRCRAFT_DATA* const ptrAircraft)
{
return ptrAircraft->Direction;
}
const uint16_t* AircraftGetTargets(uint8_t index)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(index);
if (ptrAircraft != NULL)
{
return ptrAircraft->Target;
}
return NULL;
}
uint8_t AircraftGetTargetIdx(uint8_t index)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(index);
if (ptrAircraft != NULL)
{
return ptrAircraft->TargetIdx;
}
return 0;
}
bool AircraftMoving(uint8_t index)
{
TYPE_AIRCRAFT_DATA* const ptrAircraft = AircraftFromFlightDataIndex(index);
if (ptrAircraft != NULL)
{
return (bool)ptrAircraft->Speed;
}
return false;
}
static bool AircraftCheckCollision(const TYPE_AIRCRAFT_DATA* const ptrRefAircraft, const TYPE_AIRCRAFT_DATA* const ptrOtherAircraft)
{
// Here I have used an old macro that I found on nextvolume's source code for "A Small Journey", IIRC.
// Totally fool-proof, so I dint' want to complicate things!
#define check_bb_collision(x1,y1,w1,h1,x2,y2,w2,h2) (!( ((x1)>=(x2)+(w2)) || ((x2)>=(x1)+(w1)) || \
((y1)>=(y2)+(h2)) || ((y2)>=(y1)+(h1)) ))
if (check_bb_collision( ptrRefAircraft->IsoPos.x,
ptrRefAircraft->IsoPos.y,
AIRCRAFT_SIZE_FIX16,
AIRCRAFT_SIZE_FIX16,
ptrOtherAircraft->IsoPos.x,
ptrOtherAircraft->IsoPos.y,
AIRCRAFT_SIZE_FIX16,
AIRCRAFT_SIZE_FIX16 ) != 0)
{
if (ptrRefAircraft->IsoPos.z == ptrOtherAircraft->IsoPos.z)
{
return true;
}
}
return false;
#undef check_bb_collision
}