274 lines
11 KiB
C
274 lines
11 KiB
C
// Aircraft state and flight model. Direct port of the FS2 disassembly:
|
|
// chunk5 IntegratePhysicsStep, ComputeFlightDerivedValues,
|
|
// UpdateAutoTrimAndYaw, IntegrateClimbRate, CheckFlightEnvelope,
|
|
// ResetAircraftSystems, ApplySlewDeltas; chunk2 ApplyWind.
|
|
//
|
|
// State uses the FS2 fixed-point conventions:
|
|
// - position: int32_t (Q16.16) -- high 16 bits = world unit, low 16
|
|
// bits = fraction. FS2 stores 24-bit position cells with the low
|
|
// byte fractional; we extend to 32-bit signed for headroom.
|
|
// - byte angles: uint8_t (256 == full turn).
|
|
// - rates / speeds: int16_t (Q8.8) -- low byte fractional, high byte
|
|
// integer per-frame delta.
|
|
// - pilot inputs: signed int8_t (-127..+127) for yoke / rudder / trim,
|
|
// unsigned uint8_t (0..255) for throttle / flaps / mixture, matching
|
|
// FS2's YokeVertPos / ThrottlePos byte layout.
|
|
|
|
#ifndef AIRCRAFT_H
|
|
#define AIRCRAFT_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include "camera.h"
|
|
#include "wind.h"
|
|
|
|
|
|
// Crash classes mirror FS2 chunk3 `HandleCrashOrSplash` + the
|
|
// `crash_msg_table` indices and `msg_problem` / `msg_splash` cases.
|
|
typedef enum CrashTypeE {
|
|
CRASH_NONE = 0,
|
|
CRASH_GROUND = 1,
|
|
CRASH_MOUNTAIN = 2,
|
|
CRASH_BUILDING = 3,
|
|
CRASH_SPLASH = 4,
|
|
CRASH_PROBLEM = 5
|
|
} CrashTypeE;
|
|
|
|
|
|
// View directions mirror FS2's `ViewDirection` semantics: forward is
|
|
// the default; left/right/back are 90/180/270 degree yaw offsets;
|
|
// down is a fixed pitch-down view.
|
|
typedef enum ViewDirectionE {
|
|
VIEW_FORWARD = 0,
|
|
VIEW_RIGHT = 1,
|
|
VIEW_BACK = 2,
|
|
VIEW_LEFT = 3,
|
|
VIEW_DOWN = 4
|
|
} ViewDirectionE;
|
|
|
|
|
|
// FS2 fixed-point shifts. Position uses the FS2 32-bit position cell
|
|
// convention (low 16 bits fractional). Rates / speeds use 8.8.
|
|
#define AC_POS_FRACT_BITS 16
|
|
#define AC_POS_FRACT_ONE (1 << AC_POS_FRACT_BITS)
|
|
#define AC_RATE_FRACT_BITS 8
|
|
#define AC_RATE_FRACT_ONE (1 << AC_RATE_FRACT_BITS)
|
|
|
|
// Compose a Q16.16 world coordinate from integer world units.
|
|
#define AC_WORLD_UNITS(n) ((int32_t)(n) * AC_POS_FRACT_ONE)
|
|
// Compose a uint8 throttle/flaps/mixture value from a percent 0..100.
|
|
#define AC_BYTE_PCT(p) ((uint8_t)(((int)(p) * 255) / 100))
|
|
|
|
// Reality-mode instrument failure bits. Mirrors chunk3
|
|
// `InstrumentOperationalFlags` (init $FF = all good). The
|
|
// chunk3 `FailureProcTable` clears one of these bits or sets one of
|
|
// the engine-fault bits when the reality-mode roll trips, instead of
|
|
// the immediate crash the port previously triggered.
|
|
#define AC_FAIL_AIRSPEED 0x01 // bit 0 (FailInstrumentBit0)
|
|
#define AC_FAIL_VSI 0x04 // bit 2 (FailInstrumentBit2)
|
|
#define AC_FAIL_ALTIMETER 0x08 // bit 3 (FailInstrumentBit3)
|
|
#define AC_FAIL_TURN_COORD 0x20 // bit 5 (FailInstrumentBit5)
|
|
#define AC_FAIL_ATTITUDE 0x40 // bit 6 (FailInstrumentBit6)
|
|
#define AC_FAIL_HEADING 0x80 // bit 7 (FailInstrumentBit7)
|
|
#define AC_FAIL_ALL_INSTRUMENTS (AC_FAIL_AIRSPEED | AC_FAIL_VSI | AC_FAIL_ALTIMETER | AC_FAIL_TURN_COORD | AC_FAIL_ATTITUDE | AC_FAIL_HEADING)
|
|
|
|
// Engine fault bits. SetEngineFault01 ORs $03 into $0991, SetEngineFault23
|
|
// ORs $0C. Two cylinder banks; either can fail independently.
|
|
#define AC_ENG_FAULT_LEFT 0x03
|
|
#define AC_ENG_FAULT_RIGHT 0x0C
|
|
|
|
// Mapping between aircraft metre-space and FS2 scenery units.
|
|
// FS2 scenery uses ~feet as its base unit; we round to 3 units/metre
|
|
// for clean integer math (the true ratio is 3.28 ft/m, so DME and
|
|
// ground-track come out ~9% short — close enough until we get a
|
|
// known-leg measurement to refine).
|
|
#define AC_SCENERY_UNITS_PER_METRE 3
|
|
|
|
// Nautical mile = 1852 m * 3 units/m. Used by DME so the constant is
|
|
// consistent with AC_SCENERY_UNITS_PER_METRE -- bumping one without
|
|
// the other would make airspeed and DME disagree.
|
|
#define AC_SCENERY_UNITS_PER_NM (1852 * AC_SCENERY_UNITS_PER_METRE)
|
|
|
|
// Recenter threshold: when |worldX| or |worldZ| exceeds this many
|
|
// metres the aircraftStep transparently slides the anchor and
|
|
// shrinks the local coords. Keeps the local Q16.16 well clear of its
|
|
// integer headroom (~32 km) so flight math never sees big numbers.
|
|
#define AC_RECENTER_THRESHOLD_M 20000
|
|
|
|
|
|
typedef struct AircraftT {
|
|
// Anchor: absolute FS2 scenery position of the aircraft's
|
|
// local (worldX, worldY, worldZ) = (0, 0, 0). Same role as
|
|
// FS2's section base. Updated transparently by the recenter
|
|
// logic so the local coords stay small.
|
|
int32_t sceneryOriginX;
|
|
int32_t sceneryOriginY;
|
|
int32_t sceneryOriginZ;
|
|
|
|
// Local position relative to the anchor. Q16.16 metres.
|
|
int32_t worldX;
|
|
int32_t worldY;
|
|
int32_t worldZ;
|
|
|
|
// Orientation (byte angles, 256 == full turn).
|
|
uint8_t pitch;
|
|
uint8_t bank;
|
|
uint8_t yaw;
|
|
|
|
// Body-frame rate accumulators in Q8.8 byte-angles per frame.
|
|
int16_t pitchRate; // +ve = nose up
|
|
int16_t bankRate; // +ve = right wing down
|
|
int16_t yawRate; // +ve = nose right
|
|
|
|
// Linear motion. Q8.8 world units per frame.
|
|
int16_t forwardSpeed; // along body +Z
|
|
int16_t climbRate; // +ve = climbing
|
|
|
|
// Pilot inputs.
|
|
int8_t yokeVert; // -127..+127 (FS2 YokeVertPos)
|
|
int8_t yokeHoriz; // -127..+127
|
|
int8_t rudder; // -127..+127 (FS2 RudderPos)
|
|
uint8_t throttle; // 0..255
|
|
uint8_t flaps; // 0..255 (panel slider; no flight effect yet)
|
|
int8_t trim; // -127..+127
|
|
uint8_t mixture; // 0..255 (rich..lean)
|
|
|
|
// Status.
|
|
bool onGround;
|
|
bool stalled;
|
|
bool envelopeWarning;
|
|
bool crashed;
|
|
CrashTypeE crashType;
|
|
|
|
// Spin state. Set when a stalled aircraft is also yawing
|
|
// significantly; the integrator then forces an autorotation
|
|
// around the spin axis until the pilot applies opposite
|
|
// rudder + nose-down to break it (= FS2 chunk3 SpinHandler).
|
|
bool spinning;
|
|
int8_t spinDirection; // -1 = left, +1 = right
|
|
uint16_t spinFrameCounter;
|
|
|
|
// Load factor (G) for the high-G / VNE bleed. Q8.8 g per
|
|
// frame, sampled by the integrator from yokeVert + bank
|
|
// contributions. FS2's `CheckFlightEnvelope` uses this to
|
|
// gate VNE / airframe damage.
|
|
int16_t loadFactor_q88;
|
|
|
|
// Cumulative airframe damage from over-G or over-VNE events.
|
|
// FS2 chunk3 stores this at $0898 and feeds it into the
|
|
// reliability dispatch. Port mirrors the byte and forces a
|
|
// crash when it saturates.
|
|
uint8_t airframeDamage;
|
|
|
|
// Slip / skid sideslip angle (signed Q8.8 byte-angle). FS2
|
|
// drives this from the lateral acceleration; we recompute it
|
|
// from (yaw rate - turn-coord harmony) so the slip ball reads
|
|
// from a real signal instead of just bankRate.
|
|
int16_t sideslip_q88;
|
|
|
|
// Crash-recovery snapshot armed flag. The actual saved state
|
|
// lives in a static buffer inside aircraft.c (= FS2 $FC00+).
|
|
bool hasRecoverySnapshot;
|
|
|
|
// Slew mode (FS2 `SlewMode`).
|
|
bool slewMode;
|
|
bool showSlewDigits;
|
|
int8_t slewPitchRate;
|
|
int8_t slewRollRate;
|
|
int8_t slewYawRate;
|
|
int8_t slewAltRate;
|
|
|
|
// Demo mode (FS2 chunk2 `DemoMode64K`).
|
|
bool demoMode;
|
|
uint8_t demoState; // mirrors FS2 `DemoModeParam3`
|
|
|
|
// Edit mode (FS2 `EditModeFlag`).
|
|
bool editMode;
|
|
|
|
// Reality mode (FS2 chunk3 `RealityMode`).
|
|
bool realityMode;
|
|
uint8_t reliabilityFactor;
|
|
uint16_t realityTickCounter;
|
|
uint8_t failedInstruments; // see AC_FAIL_* bits
|
|
uint8_t engineFaults; // see AC_ENG_FAULT_* bits
|
|
|
|
// Cockpit toggles overlaid on top of the static panel text.
|
|
bool lightsOn;
|
|
bool carbHeatOn;
|
|
// VOR2/ADF mode toggle. FS2 shares ONE physical instrument bay
|
|
// between VOR2 (= CDI horizontal slider) and ADF (= rotating
|
|
// bearing dial). chunk4 `ADFMode` flag selects which one is
|
|
// active; chunk5 `DrawVOR2IndicatorChanges` and chunk3
|
|
// `UpdateADFIndicator` each early-out when the OTHER mode is
|
|
// selected so only one set of needles/flags/digits paint at a
|
|
// time. Default false = VOR2 mode (matches chunk4's compiled
|
|
// initial value of 0 for ADFMode).
|
|
bool adfMode;
|
|
|
|
// Fuel state. FS2 chunk5 `UpdateFuelTankGauges` shows separate
|
|
// L/R tanks; we model them in 0..255 byte units (=full..empty).
|
|
uint8_t fuelLeft;
|
|
uint8_t fuelRight;
|
|
|
|
// Magneto state. Mirrors FS2 chunk5 `MagnetoState` ($0845):
|
|
// 0 = OFF, 1 = R only, 2 = L only, 3 = BOTH, 4 = START.
|
|
// Set by `ApplyMagnetoState` and the 1/2/3 keys.
|
|
uint8_t magnetos;
|
|
|
|
// Pause flag. FS2's `TogglePause` halts the integrator and
|
|
// input processing until pressed again.
|
|
bool paused;
|
|
|
|
// Color / B&W display mode. FS2 prompts at boot for COLOR or
|
|
// B/W; the choice patches the display kernel via
|
|
// `ColorModePatch` / `BWModePatch`. We track it as a flag and
|
|
// gate colour drawing accordingly. true = monochrome.
|
|
bool monochrome;
|
|
|
|
// Radar view (FS2 chunk4 RadarView).
|
|
bool radarView;
|
|
int16_t radarZoom; // Q8.8 metres per pixel
|
|
|
|
// View direction.
|
|
ViewDirectionE viewDirection;
|
|
} AircraftT;
|
|
|
|
|
|
void aircraftInit(AircraftT *ac);
|
|
|
|
// Per-frame integrator. One call per video frame. `wind` may be NULL
|
|
// to skip the chunk2 wind+turbulence pipeline.
|
|
void aircraftStep(AircraftT *ac, WindStateT *wind);
|
|
|
|
void aircraftToggleSlew(AircraftT *ac);
|
|
void aircraftToggleDemo(AircraftT *ac);
|
|
void aircraftToggleReality(AircraftT *ac);
|
|
void aircraftToggleEdit(AircraftT *ac);
|
|
|
|
// Copy aircraft pose into a camera so the renderer can transform the
|
|
// world from the cockpit point of view.
|
|
void aircraftSyncCamera(const AircraftT *ac, CameraT *cam);
|
|
|
|
// Effective absolute scenery coordinates of the aircraft (anchor +
|
|
// local position scaled into scenery units).
|
|
int32_t aircraftSceneryX(const AircraftT *ac);
|
|
int32_t aircraftSceneryY(const AircraftT *ac);
|
|
int32_t aircraftSceneryZ(const AircraftT *ac);
|
|
|
|
// Teleport: place the aircraft at an absolute scenery coordinate.
|
|
// Used by spawn, region selection, etc. Resets the local coords to
|
|
// zero and stores the absolute coordinate as the anchor.
|
|
void aircraftTeleport(AircraftT *ac, int32_t sx, int32_t sy, int32_t sz);
|
|
|
|
void aircraftAddThrottle(AircraftT *ac, int delta); // unit: 0..255
|
|
|
|
// Crash recovery: arm a snapshot of the current state, then restore it
|
|
// on demand. FS2 $FC00+ keeps a single image so the pilot can resume
|
|
// after a crash overlay.
|
|
void aircraftArmRecovery(AircraftT *ac);
|
|
void aircraftRestoreRecovery(AircraftT *ac);
|
|
void aircraftDecayYokeVert(AircraftT *ac, uint8_t k_q8);
|
|
void aircraftDecayYokeHoriz(AircraftT *ac, uint8_t k_q8);
|
|
void aircraftDecayRudder(AircraftT *ac, uint8_t k_q8);
|
|
|
|
#endif
|