On this page
  1. Related
  2. Games
  3. References
  4. Sources

A flight model decides how a craft moves through the air. The Pilotwings 64 gyrocopter adds five terms each update, by the atlas’s reading gravity, a drag curve, a share of the wind, and rotor and engine lift, and in one state adds the sum straight to its velocity1,2.

  • Force sumPilotwings 64 (1996). The gyrocopter sums gravity, a drag curve of squared airspeed, a share of the wind, rotor lift, and thrust that fades out between heights of 700 and 800.1,2

Games

GameYearHowNote
Pilotwings 641996Force sumthe gyrocopter sums gravity, a drag curve of squared airspeed, a share of the wind, rotor lift, and thrust that fades out between heights of 700 and 800
Every claim about this mechanic, by game (2)
Pilotwings 64 1996In the US version of Pilotwings 64, the gyrocopter's update routine func_8030399C clears a vector (unk31C) and has five routines add terms to it: func_80307A9C, func_80307E2C, func_80308478, func_80307BAC, and func_80307EA8. A routine chosen by state can add to it after them (func_80304B98, for states 1 and 4, does), and func_80309090 copies the result to unk1AC. In state 1 (unkC0), the routine adds unk1AC times the frame time to the craft's velocity (unk190), with no mass dividing it; in state 0 it first combines unk1AC with cross terms of the velocity and the turn rates (unk1B8). func_80307E2C adds (0, 0, −9.8), z being the world's vertical, turned into the craft's frame through the inverse of its matrix (by the atlas's reading, gravity). func_80307A9C adds, on each of the craft's axes, a curve of the square of unk364's component on that axis, signed like the component: 0 at 0, 4.2 at 2661.2188, and 7.56 at 6944.389 and above, linear between (by the atlas's reading, air resistance). Those squares are speeds of about 51.59 and 83.33 (computed). func_80308478 sets unk364 to the vector env_802E1754 returns for the craft's position, in the craft's frame, less the craft's velocity, and adds 0.21 of that vector as a term of its own; by the atlas's reading the vector is the air's velocity and unk364 the airspeed. Below a speed (unk1A8) of 51.587303 the vector is scaled by the square of the speed over 51.587303, and outside state 0 it's taken as zero. The other two routines are in pw64/gyrocopter-lift.Code
In the US version of Pilotwings 64, two of the terms the gyrocopter's func_8030399C sums give it lift, by the atlas's reading. The one the atlas calls the rotor term (func_80307BAC) is 0.3 times the square of the airspeed's component on the z axis of the matrix unk25C, signed like that component and capped at 200 when positive, times the square of the airspeed's y component over 25, held between 0.05 and 1; it's added along the z axis of the matrix unk21C. The one it calls the engine term (func_80307EA8), while usingFuel is set, is 35 × power (unkB4) × a factor of height (spD8, the atlas's altitude factor), split 0.24 onto y and 0.76 onto z, the z part also times a curve of the negated y airspeed: 0 at 0, 0.15 at 10, 0.4 at 20, 1.2 at 50, and 1.5 at 100 and above. It too goes through unk21C, and its y component is cut to 0.9 when unk1F0 is nonzero, unk190.y is positive, and unk7C is 0. That factor is 1 up to a height of 700 and falls by 0.01 per unit above it, held at 0 from 800 up; that hold is under #if !defined(VERSION_JP), so a Japanese build lets the factor go negative. Neither term applies while unkC1 is set or in state 2 (unkC0).Code

References

  1. ^ abIn the US version of Pilotwings 64, the gyrocopter's update routine func_8030399C clears a vector (unk31C) and has five routines add terms to it: func_80307A9C, func_80307E2C, func_80308478, func_80307BAC, and func_80307EA8. A routine chosen by state can add to it after them (func_80304B98, for states 1 and 4, does), and func_80309090 copies the result to unk1AC. In state 1 (unkC0), the routine adds unk1AC times the frame time to the craft's velocity (unk190), with no mass dividing it; in state 0 it first combines unk1AC with cross terms of the velocity and the turn rates (unk1B8). func_80307E2C adds (0, 0, −9.8), z being the world's vertical, turned into the craft's frame through the inverse of its matrix (by the atlas's reading, gravity). func_80307A9C adds, on each of the craft's axes, a curve of the square of unk364's component on that axis, signed like the component: 0 at 0, 4.2 at 2661.2188, and 7.56 at 6944.389 and above, linear between (by the atlas's reading, air resistance). Those squares are speeds of about 51.59 and 83.33 (computed). func_80308478 sets unk364 to the vector env_802E1754 returns for the craft's position, in the craft's frame, less the craft's velocity, and adds 0.21 of that vector as a term of its own; by the atlas's reading the vector is the air's velocity and unk364 the airspeed. Below a speed (unk1A8) of 51.587303 the vector is scaled by the square of the speed over 51.587303, and outside state 0 it's taken as zero. The other two routines are in pw64/gyrocopter-lift.Coden64decomp/pw64revised
    How we know
    251void func_8030399C(GyrocopterData* gcData) {
    ⋮
    265    gcData->unk31C.x = 0.0f;
    ⋮
    274    func_80307A9C(gcData);
    275    func_80307E2C(gcData);
    276    func_80308478(gcData);
    277    func_80307BAC(gcData);
    278    func_80307EA8(gcData);
    
    src/app/gyrocopter.c lines 251–279 at cb66cb2 · upstream ↗
    349    uvMat4Copy(&gcData->unk150, &gcData->unk10);
    350    uvMat4InvertTranslationRotation(&gcData->unk110, &gcData->unk150);
    351    if (gcData->unkC0 == 1) {
    352        gcData->unk190.x += D_8034F854 * gcData->unk1AC.x;
    ⋮
    355        uvMat4LocalToWorld(&gcData->unk150, &gcData->unk19C, &gcData->unk190);
    356    } else {
    357        gcData->unk19C.x += D_8034F854 * gcData->unk1B8.x;
    ⋮
    360        uvMat4LocalToWorld(&gcData->unk110, &gcData->unk190, &gcData->unk19C);
    ⋮
    363    gcData->unk10.m[3][0] = (gcData->unk19C.x * D_8034F854) + spF4;
    
    src/app/gyrocopter.c lines 349–365 at cb66cb2 · upstream ↗
    280    switch (gcData->unkC0) {
    281    case 0:
    282        func_80304738(gcData);
    283        break;
    284    case 1:
    285    case 4:
    286        func_80304B98(gcData);
    287        break;
    ⋮
    290        break;
    ⋮
    294        break;
    ⋮
    296    func_80309090(gcData);
    297    if ((gcData->unkC0 == 0) && (gcData->unk190.z < gcData->unk190.y) && (gcData->unk6D == 0)) {
    ⋮
    
    src/app/gyrocopter.c lines 280–310 at cb66cb2 · upstream ↗
    1818void func_80309090(GyrocopterData* gcData) {
    ⋮
    1826        gcData->unk1AC.x = gcData->unk31C.x;
    ⋮
    1835    gcData->unk1AC.x = gcData->unk31C.x;
    
    src/app/gyrocopter.c lines 1818–1837 at cb66cb2 · upstream ↗
    566void func_80304B98(GyrocopterData* gcData) {
    ⋮
    623        uvVec3Add(&gcData->unk31C, &gcData->unk31C, &sp11C);
    
    src/app/gyrocopter.c lines 566–623 at cb66cb2 · upstream ↗
    1442void func_80307E2C(GyrocopterData* gcData) {
    ⋮
    1447    sp1C.z = -9.8f;
    1448    uvMat4LocalToWorld(&gcData->unk110, &gcData->unk358, &sp1C);
    1449    gcData->unk31C.x += gcData->unk358.x;
    
    src/app/gyrocopter.c lines 1442–1452 at cb66cb2 · upstream ↗
    122    D_80369528.count = 3;
    123    D_80369528.unk4[0].x = 0.f, D_80369528.unk4[0].y = 0.0f;
    124    D_80369528.unk4[1].x = 2661.2188f, D_80369528.unk4[1].y = 4.2f;
    125    D_80369528.unk4[2].x = 6944.389f, D_80369528.unk4[2].y = 7.5599995f;
    
    src/app/gyrocopter.c lines 122–125 at cb66cb2 · upstream ↗
    1360void func_80307A9C(GyrocopterData* gcData) {
    ⋮
    1363    sp24.x = func_80313F08(&D_80369528, SQ(gcData->unk364.x));
    1364    if (gcData->unk364.x < 0.0f) {
    1365        sp24.x = -sp24.x;
    ⋮
    1377    gcData->unk31C.x += sp24.x;
    
    src/app/gyrocopter.c lines 1360–1380 at cb66cb2 · upstream ↗
    257f32 func_80313F08(Unk803599D0* arg0, f32 arg1) {
    ⋮
    263    if (arg1 <= arg0->unk4[0].f[0]) {
    264        return arg0->unk4[0].f[1];
    ⋮
    267    if (arg0->unk4[arg0->count - 1].f[0] <= arg1) {
    268        return arg0->unk4[arg0->count - 1].f[1];
    ⋮
    278    temp_fv1 = (arg1 - prev->f[0]) / (cur->f[0] - prev->f[0]);
    279    return (cur->f[1] * temp_fv1) + (prev->f[1] * (1.0f - temp_fv1));
    
    src/app/code_9A960.c lines 257–280 at cb66cb2 · upstream ↗
    1563void func_80308478(GyrocopterData* gcData) {
    ⋮
    1568    if (gcData->unkC0 == 0) {
    1569        env_802E1754(gcData->unk10.m[3][0], gcData->unk10.m[3][1], gcData->unk10.m[3][2], &sp38);
    1570        uvMat4LocalToWorld(&gcData->unk110, &sp44, &sp38);
    ⋮
    1577        } else {
    ⋮
    1580    } else {
    1581        sp44.x = sp44.y = sp44.z = 0.0f;
    ⋮
    1586    if (gcData->unk1A8 < 51.587303f) {
    1587        temp_fv0 = gcData->unk1A8 / 51.587303f;
    1588        temp_fv0 = SQ(temp_fv0);
    1589        sp44.x *= temp_fv0;
    ⋮
    1596    gcData->unk364.x = sp44.x - gcData->unk190.x;
    ⋮
    
    src/app/gyrocopter.c lines 1563–1605 at cb66cb2 · upstream ↗
    410    gcData->unk1A8 = uvVec3Len(&gcData->unk19C);
    
    src/app/gyrocopter.c line 410 at cb66cb2 · upstream ↗
    471            hud->altSeaLevel = gcData->unk10.m[3][2] * 0.7f;
    
    src/app/code_7CF30.c line 471 at cb66cb2 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  2. ^ abIn the US version of Pilotwings 64, two of the terms the gyrocopter's func_8030399C sums give it lift, by the atlas's reading. The one the atlas calls the rotor term (func_80307BAC) is 0.3 times the square of the airspeed's component on the z axis of the matrix unk25C, signed like that component and capped at 200 when positive, times the square of the airspeed's y component over 25, held between 0.05 and 1; it's added along the z axis of the matrix unk21C. The one it calls the engine term (func_80307EA8), while usingFuel is set, is 35 × power (unkB4) × a factor of height (spD8, the atlas's altitude factor), split 0.24 onto y and 0.76 onto z, the z part also times a curve of the negated y airspeed: 0 at 0, 0.15 at 10, 0.4 at 20, 1.2 at 50, and 1.5 at 100 and above. It too goes through unk21C, and its y component is cut to 0.9 when unk1F0 is nonzero, unk190.y is positive, and unk7C is 0. That factor is 1 up to a height of 700 and falls by 0.01 per unit above it, held at 0 from 800 up; that hold is under #if !defined(VERSION_JP), so a Japanese build lets the factor go negative. Neither term applies while unkC1 is set or in state 2 (unkC0).Coden64decomp/pw64revised
    How we know
    1382void func_80307BAC(GyrocopterData* gcData) {
    ⋮
    1403    if ((gcData->unkC1 != 0) || (gcData->unkC0 == 2)) {
    1404        gcData->unkE8 = func_80313AF4(0.0f, gcData->unkE8, 0.5f);
    1405        return;
    ⋮
    1407    uvMat4LocalToWorld(&gcData->unk25C, &sp34, &gcData->unk364);
    1408    sp54 = -(0.3f * sp34.z * sp34.z);
    1409    if (-sp34.z < 0.0f) {
    1410        sp54 = -sp54;
    1411        if (sp54 < -10.0f) {
    1412            sp54 = -10.0f;
    ⋮
    1414        if (sp54 > 200.0f) {
    1415            sp54 = 200.0f;
    ⋮
    
    src/app/gyrocopter.c lines 1382–1440 at cb66cb2 · upstream ↗
    1454void func_80307EA8(GyrocopterData* gcData) {
    ⋮
    1460    if ((gcData->unkC1 != 0) || (gcData->unkC0 == 2)) {
    1461        gcData->unkF0 = func_80313AF4(0.0f, gcData->unkF0, 0.5f);
    1462        return;
    
    src/app/gyrocopter.c lines 1454–1463 at cb66cb2 · upstream ↗
    1490    if (gcData->unk10.m[3][2] > 700.0f) {
    1491        spD8 = 1.0f - ((gcData->unk10.m[3][2] - 700.0f) / 100.0f);
    1492#if !defined(VERSION_JP)
    1493        if (spD8 < 0.0f) {
    1494            spD8 = 0.0f;
    ⋮
    1496#endif
    1497    } else {
    1498        spD8 = 1.0f;
    ⋮
    1500    if (gcData->usingFuel) {
    1501        temp_fv0 = func_80313F08(&D_803692C0, -gcData->unk364.y);
    1502        spDC.x = 0.0f;
    1503        spDC.y = gcData->unkB4 * 0.24f * spD8 * 35.0f;
    ⋮
    
    src/app/gyrocopter.c lines 1490–1516 at cb66cb2 · upstream ↗
    74    D_803692C0.count = 5;
    75    D_803692C0.unk4[0].x = 0.0f, D_803692C0.unk4[0].y = 0.f;
    76    D_803692C0.unk4[1].x = 10.0f, D_803692C0.unk4[1].y = 0.15f;
    77    D_803692C0.unk4[2].x = 20.0f, D_803692C0.unk4[2].y = 0.4f;
    78    D_803692C0.unk4[3].x = 50.0f, D_803692C0.unk4[3].y = 1.2f;
    79    D_803692C0.unk4[4].x = 100.0f, D_803692C0.unk4[4].y = 1.5f;
    
    src/app/gyrocopter.c lines 74–79 at cb66cb2 · upstream ↗
    471            hud->altSeaLevel = gcData->unk10.m[3][2] * 0.7f;
    ⋮
    473            if (hud->fuel > 0.0f) {
    474                hud->power = gcData->unkB4;
    
    src/app/code_7CF30.c lines 470–474 at cb66cb2 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

Sources

Code