Subversion Repositories Games.Carmageddon

Rev

Rev 18 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "mainloop.h"
  2. #include "brender/brender.h"
  3. #include "car.h"
  4. #include "controls.h"
  5. #include "crush.h"
  6. #include "depth.h"
  7. #include "displays.h"
  8. #include "drmem.h"
  9. #include "flicplay.h"
  10. #include "globvars.h"
  11. #include "globvrkm.h"
  12. #include "globvrpb.h"
  13. #include "graphics.h"
  14. #include "harness/config.h"
  15. #include "harness/hooks.h"
  16. #include "harness/trace.h"
  17. #include "input.h"
  18. #include "main.h"
  19. #include "mainmenu.h"
  20. #include "netgame.h"
  21. #include "network.h"
  22. #include "oil.h"
  23. #include "opponent.h"
  24. #include "pd/sys.h"
  25. #include "pedestrn.h"
  26. #include "piping.h"
  27. #include "powerup.h"
  28. #include "pratcam.h"
  29. #include "replay.h"
  30. #include "s3/s3.h"
  31. #include "skidmark.h"
  32. #include "sound.h"
  33. #include "spark.h"
  34. #include "structur.h"
  35. #include "trig.h"
  36. #include "utility.h"
  37. #include "world.h"
  38. #include <stdlib.h>
  39.  
  40. int gNasty_kludgey_cockpit_variable;
  41. tInfo_mode gInfo_mode;
  42. tU32 gLast_tick_count;
  43. tU32 gActual_last_tick_count;
  44. tU32 gAverage_frame_period;
  45. tU32 gOld_camera_time;
  46. tU32 gLast_wasted_massage_start;
  47. float gMr_odo;
  48. tU32 gWasted_last_flash;
  49. tTime_bonus_state gTime_bonus_state;
  50. int gQueued_wasted_massages_count;
  51. int gTime_bonus;
  52. int gRace_bonus_headup;
  53. int gWasted_flash_state;
  54. int gLast_time_headup;
  55. int gTime_bonus_headup;
  56. int gQueued_wasted_massages[5];
  57. tU32 gTime_bonus_start;
  58. int gLast_credit_headup__mainloop; // suffix added to avoid duplicate symbol
  59.  
  60. // IDA: void __cdecl ToggleInfo()
  61. void ToggleInfo(void) {
  62.     LOG_TRACE("()");
  63.  
  64.     if (gProgram_state.game_completed) {
  65.         if (KeyIsDown(KEYMAP_CONTROL_ANY)) {
  66.             gAR_fudge_headups = !gAR_fudge_headups;
  67.         } else {
  68.             gInfo_on = !gInfo_on;
  69.             if (gInfo_on) {
  70.                 gInfo_mode = PDKeyDown(KEY_SHIFT_ANY);
  71.             }
  72.         }
  73.     }
  74. }
  75.  
  76. // IDA: void __cdecl CalculateFrameRate()
  77. void CalculateFrameRate(void) {
  78.     static tU32 last_time;
  79.     tU32 new_time;
  80.     static int last_rates[30];
  81.     int new_rate;
  82.     int i;
  83.     LOG_TRACE("()");
  84.  
  85.     new_time = PDGetTotalTime();
  86.     if (new_time != last_time) {
  87.         new_rate = 10000u / (new_time - last_time);
  88.         gFrame_rate = new_rate;
  89.         for (i = 0; i < COUNT_OF(last_rates); ++i) {
  90.             gFrame_rate += last_rates[i];
  91.         }
  92.         gFrame_rate /= COUNT_OF(last_rates) + 1;
  93.         for (i = 0; i < COUNT_OF(last_rates) - 1; i++) {
  94.             last_rates[i] = last_rates[i + 1];
  95.         }
  96.         last_rates[29] = new_rate;
  97.         last_time = new_time;
  98.     }
  99. }
  100.  
  101. // IDA: void __cdecl LoseOldestWastedMassage()
  102. void LoseOldestWastedMassage(void) {
  103.     int i;
  104.     LOG_TRACE("()");
  105.  
  106.     for (i = 1; i < gQueued_wasted_massages_count; i++) {
  107.         gQueued_wasted_massages[i - 1] = gQueued_wasted_massages[i];
  108.     }
  109.     gQueued_wasted_massages_count--;
  110.     gLast_wasted_massage_start = GetTotalTime();
  111. }
  112.  
  113. // IDA: void __usercall QueueWastedMassage(int pIndex@<EAX>)
  114. void QueueWastedMassage(int pIndex) {
  115.     LOG_TRACE("(%d)", pIndex);
  116.  
  117.     if (gQueued_wasted_massages_count == COUNT_OF(gQueued_wasted_massages)) {
  118.         LoseOldestWastedMassage();
  119.     }
  120.     if (gQueued_wasted_massages_count == 0) {
  121.         gLast_wasted_massage_start = GetTotalTime();
  122.     }
  123.     gQueued_wasted_massages[gQueued_wasted_massages_count] = pIndex;
  124.     gQueued_wasted_massages_count++;
  125. }
  126.  
  127. // IDA: void __cdecl MungeHeadups()
  128. void MungeHeadups(void) {
  129.     char the_text[256];
  130.     //int flash_rate; // Pierre-Marie Baty -- unused variable
  131.     int new_countdown;
  132.     int net_credits;
  133.     int previous_gtimer;
  134.     //int previous_time_bonus; // Pierre-Marie Baty -- unused variable
  135.     int effective_timer;
  136.     int bonus;
  137.     int oppo_count;
  138.     tU32 the_time;
  139.     float bearing;
  140.     //br_material* nearby; // Pierre-Marie Baty -- unused variable
  141.     //tPixelmap_user_data* user; // Pierre-Marie Baty -- unused variable
  142.     static tU32 last_rattle_time;
  143.     LOG_TRACE("()");
  144.  
  145.     ClearHeadupSlot(3);
  146.     gMr_odo = (double)gFrame_period * gProgram_state.current_car.speedo_speed * WORLD_SCALE / 1600.0 + gMr_odo;
  147.     if (gInfo_on) {
  148.         bearing = 360.0 - FastScalarArcTan2(gCamera_to_world.m[0][2], gCamera_to_world.m[2][2]);
  149.         if (gInfo_mode) {
  150.             sprintf(
  151.                 the_text,
  152.                 "P'cam: curr=%d, ambi=%d, pend=%d Car: c=%+.3f, a=%+.3f, b=%+.3f",
  153.                 PratcamGetCurrent(),
  154.                 PratcamGetAmbient(),
  155.                 PratcamGetPending(),
  156.                 gCar_to_view->curvature,
  157.                 gCar_to_view->acc_force,
  158.                 gCar_to_view->brake_force);
  159.         } else {
  160.             sprintf(
  161.                 the_text,
  162.                 "%d.%d (%.3f, %.3f, %.3f) %.0f, %.0f, MILES=%.2f",
  163.                 gFrame_rate / 10,
  164.                 gFrame_rate % 10,
  165.                 gSelf->t.t.translate.t.v[0],
  166.                 gSelf->t.t.translate.t.v[1],
  167.                 gSelf->t.t.translate.t.v[2],
  168.                 gCamera_to_horiz_angle,
  169.                 bearing,
  170.                 gMr_odo);
  171.         }
  172.         ChangeHeadupText(gProgram_state.frame_rate_headup, the_text);
  173.     } else {
  174.         ChangeHeadupText(gProgram_state.frame_rate_headup, "");
  175.     }
  176.     net_credits = gProgram_state.credits_earned - gProgram_state.credits_lost;
  177.     if (fabs((double)(gProgram_state.credits_earned - gProgram_state.credits_lost) - (double)gLast_credit_headup__mainloop) / (double)gFrame_period > 1.2) {
  178.         if (net_credits - gLast_credit_headup__mainloop <= 0) {
  179.             net_credits = (double)gLast_credit_headup__mainloop
  180.                 - ((double)(gLast_credit_headup__mainloop - net_credits) + 1000.0)
  181.                     * (double)gFrame_period
  182.                     * 1.2
  183.                     / 1000.0;
  184.         } else {
  185.             net_credits = (net_credits - gLast_credit_headup__mainloop) + 1000.0 * (double)gFrame_period * 1.2 / 1000.0
  186.                 + (double)gLast_credit_headup__mainloop;
  187.         }
  188.     }
  189.     gLast_credit_headup__mainloop = net_credits;
  190.     if (gCountdown) {
  191.         new_countdown = 7.5 - (double)GetRaceTime() / 1000.0;
  192.         if (new_countdown < 0) {
  193.             new_countdown = 0;
  194.         }
  195.         if (gCountdown != new_countdown && new_countdown <= 5) {
  196.             gCountdown = new_countdown;
  197.             NewImageHeadupSlot(5, 0, 800, new_countdown + 4);
  198.             DRS3StartSound(gPedestrians_outlet, gCountdown + 8000);
  199.             if (!new_countdown) {
  200.                 MakeFlagWavingBastardWaveHisFlagWhichIsTheProbablyTheLastThingHeWillEverDo();
  201.             }
  202.         }
  203.     }
  204.     if (fabs((double)gTimer - (double)gLast_time_headup) / (double)gFrame_period <= 10.0) {
  205.         effective_timer = gTimer;
  206.     } else if (gTimer - gLast_time_headup <= 0) {
  207.         effective_timer = gTimer;
  208.     } else {
  209.         effective_timer = gFrame_period * 10.0 + gLast_time_headup;
  210.     }
  211.     gLast_time_headup = effective_timer;
  212.     if (gNet_mode != eNet_mode_none) {
  213.         DoNetworkHeadups(net_credits);
  214.     } else {
  215.         if (net_credits < 0) {
  216.             sprintf(the_text, "\xF8%d\xFA %s", -net_credits, GetMiscString(kMiscString_LOSS));
  217.         } else {
  218.             sprintf(the_text, "\xF8%d\xFA %s", net_credits, GetMiscString(net_credits < 100000 ? kMiscString_PROFIT : kMiscString_PRFT));
  219.         }
  220.         ChangeHeadupText(gCredits_won_headup, the_text);
  221.         if (gPedestrians_on) {
  222.             sprintf(the_text, "\xF8%d\xFA/%d %s", gProgram_state.peds_killed, gTotal_peds, GetMiscString(kMiscString_KILLS));
  223.             ChangeHeadupText(gPed_kill_count_headup, the_text);
  224.         } else {
  225.             ChangeHeadupText(gPed_kill_count_headup, "");
  226.         }
  227.         if (gQueued_wasted_massages_count && GetTotalTime() > gLast_wasted_massage_start + 5000) {
  228.             LoseOldestWastedMassage();
  229.         }
  230.         if (gQueued_wasted_massages_count) {
  231.             if (Flash(150, &gWasted_last_flash, &gWasted_flash_state)) {
  232.                 sprintf(the_text, "\xF9%s %s", gOpponents[gQueued_wasted_massages[0]].abbrev_name, GetMiscString(kMiscString_WASTED_46));
  233.             } else {
  234.                 sprintf(the_text, " ");
  235.             }
  236.         } else {
  237.             oppo_count = GetCarCount(eVehicle_opponent);
  238.             sprintf(the_text, "%s \xF8%d\xFA/%d", GetMiscString(kMiscString_WASTED_19), oppo_count - NumberOfOpponentsLeft(), oppo_count);
  239.         }
  240.         ChangeHeadupText(gCar_kill_count_headup, the_text);
  241.         if (effective_timer > 1199000) {
  242.             effective_timer = 1199000;
  243.         }
  244.         TimerString(effective_timer, the_text, 1, 0);
  245.         ChangeHeadupText(gTimer_headup, the_text);
  246.         sprintf(the_text, "%s \xF8%d\xFA/%d %s \xF8%d\xFA/%d", GetMiscString(kMiscString_CP), gCheckpoint, gCheckpoint_count, GetMiscString(kMiscString_LAP), gLap, gTotal_laps);
  247.         ChangeHeadupText(gLaps_headup, the_text);
  248.         the_time = GetTotalTime() - gTime_bonus_start;
  249.         switch (gTime_bonus_state) {
  250.         case eTime_bonus_initial_pause:
  251.             if (the_time >= 500) {
  252.                 bonus = gCurrent_race.bonus_score[gRace_over_reason][gProgram_state.skill_level];
  253.                 sprintf(the_text, "%s %d", GetMiscString(kMiscString_TimeBonus), bonus);
  254.                 DRS3StartSound(gPedestrians_outlet, 8015);
  255.                 ChangeHeadupText(gRace_bonus_headup, the_text);
  256.                 gProgram_state.credits_earned += bonus;
  257.                 gTime_bonus_state = eTime_bonus_race_bonus;
  258.                 gTime_bonus_start = GetTotalTime();
  259.             }
  260.             gRace_finished = 10000;
  261.             break;
  262.         case eTime_bonus_race_bonus:
  263.             if (the_time >= 2000) {
  264.                 gTime_bonus_state = eTime_bonus_tb_up;
  265.                 gTime_bonus_start = GetTotalTime();
  266.                 last_rattle_time = 0;
  267.             }
  268.             gRace_finished = 10000;
  269.             break;
  270.         case eTime_bonus_tb_up:
  271.             if (gTimer) {
  272.                 if (the_time - last_rattle_time > 15) {
  273.                     previous_gtimer = gTimer;
  274.                     gTimer -= 1000 * ((the_time - last_rattle_time) / 15);
  275.                     if (gTimer < 0) {
  276.                         gTimer = 0;
  277.                     }
  278.                     gTime_bonus += (previous_gtimer - gTimer) / 1000 * gPoints_per_second[gProgram_state.skill_level];
  279.                     last_rattle_time += 15 * ((the_time - last_rattle_time) / 15);
  280.                 }
  281.                 sprintf(the_text, "%s %d", GetMiscString(kMiscString_TimeBonus), gTime_bonus);
  282.                 ChangeHeadupText(gTime_bonus_headup, the_text);
  283.             } else {
  284.                 gTime_bonus_state = eTime_bonus_tb_pause;
  285.                 gTime_bonus_start = GetTotalTime();
  286.                 last_rattle_time = 0;
  287.             }
  288.             gRace_finished = 10000;
  289.             break;
  290.         case eTime_bonus_tb_pause:
  291.             if (the_time >= 1000) {
  292.                 gTime_bonus_state = eTime_bonus_tb_down;
  293.                 gTime_bonus_start = GetTotalTime();
  294.                 last_rattle_time = 0;
  295.             }
  296.             gRace_finished = 10000;
  297.             break;
  298.         case eTime_bonus_tb_down:
  299.             if (gTime_bonus) {
  300.                 if (the_time - last_rattle_time > 15) {
  301.                     bonus = gTime_bonus;
  302.                     gTime_bonus -= (the_time - last_rattle_time) * gPoints_per_second[gProgram_state.skill_level] / 15;
  303.                     if (gTime_bonus < 0) {
  304.                         gTime_bonus = 0;
  305.                     }
  306.                     gProgram_state.credits_earned += bonus - gTime_bonus;
  307.                     last_rattle_time += 15 * ((the_time - last_rattle_time) / 15);
  308.                 }
  309.                 sprintf(the_text, "%s %d", GetMiscString(kMiscString_TimeBonus), gTime_bonus);
  310.                 ChangeHeadupText(gTime_bonus_headup, the_text);
  311.             } else {
  312.                 gTime_bonus_state = eTime_bonus_end_pause;
  313.                 gTime_bonus_start = GetTotalTime();
  314.             }
  315.             gRace_finished = 10000;
  316.             break;
  317.         case eTime_bonus_end_pause:
  318.             if (the_time >= 2000 && gRace_finished > 1) {
  319.                 gRace_finished = 1;
  320.             }
  321.             break;
  322.         default:
  323.             return;
  324.         }
  325.     }
  326. }
  327.  
  328. // IDA: void __usercall UpdateFramePeriod(tU32 *pCamera_period@<EAX>)
  329. void UpdateFramePeriod(tU32* pCamera_period) {
  330.     tU32 new_tick_count;
  331.     tU32 new_camera_tick_count;
  332.     int error;
  333.     static int last_AR_mode;
  334.     LOG_TRACE("(%p)", pCamera_period);
  335.  
  336.     if (gAction_replay_mode != last_AR_mode) {
  337.         if (gAction_replay_mode) {
  338.             gLast_replay_frame_time = GetTotalTime();
  339.         } else {
  340.             gLast_tick_count = GetTotalTime();
  341.         }
  342.         last_AR_mode = gAction_replay_mode;
  343.     }
  344.     if (gAction_replay_mode) {
  345.         gFrame_period = abs((int)(gLast_replay_frame_time - gLast_tick_count));
  346.         gLast_tick_count = gLast_replay_frame_time;
  347.         new_camera_tick_count = PDGetTotalTime();
  348.         new_tick_count = GetTotalTime();
  349.         if (gOld_camera_time) {
  350.             *pCamera_period = new_camera_tick_count - gOld_camera_time;
  351.         } else {
  352.             *pCamera_period = 0;
  353.         }
  354.         gOld_camera_time = new_camera_tick_count;
  355.         if (gFrame_period) {
  356.             *pCamera_period = gFrame_period;
  357.         }
  358.     } else {
  359.         new_tick_count = GetTotalTime();
  360.         gLast_tick_count += gFrame_period;
  361.         error = new_tick_count - gLast_tick_count;
  362.         gFrame_period = new_tick_count - gActual_last_tick_count;
  363.         if ((new_tick_count - gActual_last_tick_count) > 500 && new_tick_count - gRace_start > 2000) {
  364.             gFrame_period = gAverage_frame_period / 10;
  365.             gLast_tick_count = new_tick_count;
  366.             if (gNet_mode) {
  367.                 if (gNet_mode == eNet_mode_client) {
  368.                     gProgram_state.current_car.last_car_car_collision = 0;
  369.                 }
  370.             }
  371.         }
  372.         gAverage_frame_period = 9 * gAverage_frame_period / 10 + gFrame_period;
  373.         if ((new_tick_count - gRace_start) > 2000) {
  374.             gFrame_period = gAverage_frame_period / 10;
  375.         }
  376.         if ((int)(error + gFrame_period) > 0) {
  377.             gFrame_period += error;
  378.         } else {
  379.             gLast_tick_count = new_tick_count;
  380.         }
  381.         *pCamera_period = gFrame_period;
  382.         gActual_last_tick_count = new_tick_count;
  383.     }
  384.     if (gFrame_period >= 10) {
  385.         if (gFrame_period > 1000) {
  386.             gFrame_period = 1000;
  387.             gLast_tick_count = new_tick_count;
  388.         }
  389.     } else {
  390.         // The following makes the timer go too fast when the real frame rate is high (=low frame period)
  391. #ifndef DETHRACE_FIX_BUGS
  392.         gFrame_period = 10;
  393. #endif
  394.         gLast_tick_count = new_tick_count;
  395.     }
  396.     if (*pCamera_period >= 10) {
  397.         if (*pCamera_period > 1000) {
  398.             *pCamera_period = 1000;
  399.         }
  400.     } else {
  401.         *pCamera_period = 10;
  402.     }
  403. }
  404.  
  405. // IDA: tU32 __cdecl GetLastTickCount()
  406. tU32 GetLastTickCount(void) {
  407.     LOG_TRACE("()");
  408.  
  409.     return gLast_tick_count;
  410. }
  411.  
  412. // IDA: void __cdecl CheckTimer()
  413. void CheckTimer(void) {
  414.     tS32 time_in_seconds;
  415.     tS32 time_left;
  416.     static tU32 last_time_in_seconds = 0;
  417.     static tU32 last_demo_time_in_seconds = 0;
  418.     LOG_TRACE("()");
  419.  
  420.     if (harness_game_config.freeze_timer) {
  421.         return;
  422.     }
  423.  
  424.     if (!gFreeze_timer && !gCountdown && !gRace_finished) {
  425.         if (gFrame_period < (tU32) gTimer) { // Pierre-Marie Baty -- added type cast
  426.             if (gNet_mode == eNet_mode_none) {
  427.                 gTimer -= gFrame_period;
  428.             }
  429.             time_left = gTimer + 500;
  430.             time_in_seconds = (time_left) / 1000;
  431.             if (time_in_seconds != last_time_in_seconds && time_in_seconds <= 10) {
  432.                 DRS3StartSound(gPedestrians_outlet, 1001);
  433.             }
  434.             last_time_in_seconds = time_in_seconds;
  435.         } else {
  436.             gTimer = 0;
  437.             RaceCompleted(eRace_over_out_of_time);
  438.         }
  439.  
  440.         if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  441.             if (harness_game_config.demo_timeout != 0) {
  442.                 time_left = harness_game_config.demo_timeout - GetRaceTime();
  443.                 time_in_seconds = (time_left + 500) / 1000;
  444.                 if (time_in_seconds != last_demo_time_in_seconds && time_in_seconds <= 10)
  445.                     DRS3StartSound(gPedestrians_outlet, 1001);
  446.                 last_demo_time_in_seconds = time_in_seconds;
  447.                 if (time_left <= 0) {
  448.                     gTimer = 0;
  449.                     RaceCompleted(eRace_over_demo);
  450.                 }
  451.             }
  452.         }
  453.     }
  454. }
  455.  
  456. // IDA: int __cdecl MungeRaceFinished()
  457. int MungeRaceFinished(void) {
  458.     LOG_TRACE("()");
  459.  
  460.     if (!gRace_finished || gAction_replay_mode || (gNet_mode != eNet_mode_none && gRace_over_reason == eRace_not_over_yet)) {
  461.         return 0;
  462.     }
  463.     if (gRace_finished > gFrame_period) {
  464.         gRace_finished -= gFrame_period;
  465.     } else {
  466.         if (!gTimer || gNet_mode != eNet_mode_none) {
  467.             gRace_finished = 0;
  468.             return 1;
  469.         }
  470.         gRace_finished = 15 * gTimer + 4500;
  471.         gRace_bonus_headup = NewTextHeadupSlot(9, 0, 0, -4, "");
  472.         gTime_bonus_headup = NewTextHeadupSlot(10, 0, 0, -4, "");
  473.         gTime_bonus = 0;
  474.         gTime_bonus_start = GetTotalTime();
  475.         gTime_bonus_state = eTime_bonus_initial_pause;
  476.     }
  477.     return PDKeyDown(KEY_RETURN) || PDKeyDown(KEY_KP_ENTER);
  478. }
  479.  
  480. // IDA: tRace_result __cdecl MainGameLoop()
  481. tRace_result MainGameLoop(void) {
  482.     tU32 camera_period;
  483.     tU32 start_menu_time;
  484.     tU32 frame_start_time;
  485.     tRace_result result;
  486.     int tried_to_allocate_AR;
  487.     int i;
  488.     int bonus;
  489.     LOG_TRACE("()");
  490.  
  491.     tried_to_allocate_AR = 0;
  492.     gCar_to_view = &gProgram_state.current_car;
  493.     gOld_camera_time = 0;
  494.     ClearEntireScreen();
  495.     ResetPalette();
  496.     gLast_credit_headup__mainloop = 0;
  497.     gLast_time_headup = gTimer;
  498.     gRace_bonus_headup = -1;
  499.     gTime_bonus_headup = -1;
  500.     gTime_bonus_start = 0;
  501.     gTime_bonus_state = eTime_bonus_none;
  502.     gLast_replay_frame_time = PDGetTotalTime();
  503.     gLast_tick_count = GetTotalTime();
  504.     gActual_last_tick_count = gLast_tick_count;
  505.     gQueued_wasted_massages_count = 0;
  506.     gWasted_flash_state = 0;
  507.     gWasted_last_flash = 0;
  508.     RevertPalette();
  509.     gHost_abandon_game = 0;
  510.     gNo_races_yet = 0;
  511.     gRace_over_reason = eRace_not_over_yet;
  512.     gMr_odo = 0.0;
  513.     gReceived_game_scores = 0;
  514.     ShowSpecialVolumesIfRequ();
  515.     gProgram_state.racing = 1;
  516.     if (gNet_mode == eNet_mode_host) {
  517.         gCurrent_net_game->status.stage = eNet_game_playing;
  518.     }
  519.     NetPlayerStatusChanged(ePlayer_status_racing);
  520.     GetAverageGridPosition(&gCurrent_race);
  521.     ForceRebuildActiveCarList();
  522.     PrintMemoryDump(0, "ABOUT TO ENTER MAINLOOP");
  523.  
  524.     do {
  525.         frame_start_time = GetTotalTime();
  526.         CyclePollKeys();
  527.         CheckSystemKeys(1);
  528.         NetReceiveAndProcessMessages();
  529.         if (gHost_abandon_game || gProgram_state.prog_status == eProg_idling) {
  530.             break;
  531.         }
  532.         if (gNet_mode && gMap_mode
  533.             && ((gCurrent_net_game->type == eNet_game_type_foxy && gThis_net_player_index == gIt_or_fox)
  534.                 || (gCurrent_net_game->type == eNet_game_type_tag && gThis_net_player_index != gIt_or_fox))) {
  535.             ToggleMap();
  536.         }
  537.         ResetGrooveFlags();
  538.         MungeEngineNoise();
  539.         ServiceGameInRace();
  540.         EnterUserMessage();
  541.         UpdateFramePeriod(&camera_period);
  542.         if (!gAction_replay_mode) {
  543.             DoPowerupPeriodics(gFrame_period);
  544.         }
  545.         ResetLollipopQueue();
  546.         if (!gAction_replay_mode) {
  547.             MungeOpponents(gFrame_period);
  548.             PollCarControls(gFrame_period);
  549.         }
  550.         PollCameraControls(camera_period);
  551.         if (gAction_replay_mode) {
  552.             DoActionReplay(gFrame_period);
  553.         } else {
  554.             ControlOurCar(gFrame_period);
  555.             ApplyPhysicsToCars(gLast_tick_count - gRace_start, gFrame_period);
  556.             PipeCarPositions();
  557.             NetSendMessageStacks();
  558.             CheckRecoveryOfCars(gFrame_period + gLast_tick_count - gRace_start);
  559.         }
  560.         if (!gNasty_kludgey_cockpit_variable) {
  561.             gNasty_kludgey_cockpit_variable = 1;
  562.             ToggleCockpit();
  563.         }
  564.         gOur_pos = &gSelf->t.t.translate.t;
  565.         PositionExternalCamera(&gProgram_state.current_car, camera_period);
  566.         BrActorToActorMatrix34(&gCamera_to_world, gCamera, gUniverse_actor);
  567.         BrActorToActorMatrix34(&gRearview_camera_to_world, gRearview_camera, gUniverse_actor);
  568.         gCamera_to_horiz_angle = FastScalarArcTan2(gCamera_to_world.m[2][1], gCamera_to_world.m[1][1]);
  569.         gYon_squared = ((br_camera*)gCamera->type_data)->yon_z * ((br_camera*)gCamera->type_data)->yon_z
  570.             * gYon_multiplier
  571.             * gYon_multiplier;
  572.         if (!gAction_replay_mode) {
  573.             CheckCheckpoints();
  574.         }
  575.         ChangingView();
  576.         MungeCarGraphics(gFrame_period);
  577.         FunkThoseTronics();
  578.         GrooveThoseDelics();
  579.         DoWheelDamage(gFrame_period);
  580.         CalculateFrameRate();
  581.         MungePedestrians(gFrame_period);
  582.         CameraBugFix(&gProgram_state.current_car, camera_period);
  583.         if (!gAction_replay_mode) {
  584.             MungeHeadups();
  585.             ProcessOilSpills(gFrame_period);
  586.         }
  587.         MungeShrapnel(gFrame_period);
  588.         ChangeDepthEffect();
  589.         ServiceGameInRace();
  590.         EnterUserMessage();
  591.         SkidsPerFrame();
  592.         if (!gWait_for_it) {
  593.             RenderAFrame(1);
  594.         }
  595.         CheckReplayTurnOn();
  596.         if (!gRecover_car
  597.             && gProgram_state.prog_status == eProg_game_ongoing
  598.             && !gPalette_fade_time
  599.             && (gNet_mode == eNet_mode_none
  600.                 || !gAction_replay_mode
  601.                 || gProgram_state.current_car.car_master_actor->t.t.mat.m[3][0] < 500.0)) {
  602.  
  603.             EnsureRenderPalette();
  604.             EnsurePaletteUp();
  605.         }
  606.         DoNetGameManagement();
  607.         if (KeyIsDown(KEYMAP_ESCAPE) && !gEntering_message) {
  608.             WaitForNoKeys();
  609.             if (gAction_replay_mode) {
  610.                 ToggleReplay();
  611.             } else {
  612.                 start_menu_time = PDGetTotalTime();
  613.                 FadePaletteDown();
  614.                 ClearEntireScreen();
  615.                 GoingToInterfaceFromRace();
  616.                 DoMainMenuScreen(0, 0, 1);
  617.                 GoingBackToRaceFromInterface();
  618.                 AddLostTime(PDGetTotalTime() - start_menu_time);
  619.             }
  620.         }
  621.         if (gAction_replay_mode) {
  622.             PollActionReplayControls(gFrame_period);
  623.         } else {
  624.             CheckTimer();
  625.         }
  626.         if (!gAction_replay_mode && gKnobbled_frame_period) {
  627.             while (GetTotalTime() - frame_start_time < (tU32) gKnobbled_frame_period) { // Pierre-Marie Baty -- added type cast
  628.                 ;
  629.             }
  630.         }
  631.         if (!tried_to_allocate_AR) {
  632.             tried_to_allocate_AR = 1;
  633.             PrintMemoryDump(0, "JUST RENDERED 1ST STUFF");
  634.             InitialisePiping();
  635.             PrintMemoryDump(0, "JUST ALLOCATED ACTION REPLAY BUFFER");
  636.         }
  637.         if (gNet_mode == eNet_mode_client && gAbandon_game) {
  638.             gProgram_state.prog_status = eProg_idling;
  639.             gAbandon_game = 0;
  640.         }
  641.  
  642.     } while (gProgram_state.prog_status == eProg_game_ongoing
  643.         && !MungeRaceFinished()
  644.         && !gAbandon_game
  645.         && !gHost_abandon_game);
  646.     PrintMemoryDump(0, "JUST EXITED MAINLOOP");
  647.     FadePaletteDown();
  648.     ClearEntireScreen();
  649.     SuspendPendingFlic();
  650.     RevertPalette();
  651.     if (gMap_mode) {
  652.         ToggleMap();
  653.     }
  654.     EnsureSpecialVolumesHidden();
  655.     FadePaletteDown();
  656.     NetPlayerStatusChanged(ePlayer_status_loading);
  657.     if (gProgram_state.prog_status == eProg_game_ongoing) {
  658.         ResetCarScreens();
  659.     }
  660.     if (gAbandon_game && gNet_mode == eNet_mode_host && gRace_over_reason < eRace_over_network_victory) {
  661.         NetFinishRace(gCurrent_net_game, eRace_over_abandoned);
  662.     }
  663.     if (gAction_replay_mode) {
  664.         ToggleReplay();
  665.     }
  666.     // From splatpack x-mas demo
  667.     if (gArrow_mode) {
  668.         ToggleArrow();
  669.     }
  670.  
  671.     if (gHost_abandon_game) {
  672.         result = eRace_game_abandonned;
  673.     } else if (gProgram_state.prog_status == eProg_game_ongoing) {
  674.         if (gAbandon_game) {
  675.             result = eRace_aborted;
  676.         } else if (gRace_over_reason == eRace_over_out_of_time || gRace_over_reason == eRace_over_demo) {
  677.             result = eRace_timed_out;
  678.         } else {
  679.             result = eRace_completed;
  680.         }
  681.     } else {
  682.         result = eRace_game_abandonned;
  683.     }
  684.     if (result >= eRace_completed) {
  685.         gProgram_state.redo_race_index = -1;
  686.     } else {
  687.         gProgram_state.redo_race_index = gProgram_state.current_race_index;
  688.     }
  689.     gAbandon_game = 0;
  690.     gSynch_race_start = 0;
  691.     gInitialised_grid = 0;
  692.     gHost_abandon_game = 0;
  693.     S3StopAllOutletSounds();
  694.     if (gTime_bonus_state > eTime_bonus_none) {
  695.         gTime_bonus += gTimer / 1000 * gPoints_per_second[gProgram_state.skill_level];
  696.         gProgram_state.credits_earned += gTime_bonus;
  697.     }
  698.     if (gTime_bonus_state < eTime_bonus_race_bonus) {
  699.         // FIXME: gRace_over_reason can be -1 eRace_not_over_yet (=-1) when aborting a race
  700.         bonus = gCurrent_race.bonus_score[gRace_over_reason][gProgram_state.skill_level];
  701.         gProgram_state.credits_earned += bonus;
  702.     }
  703.     if (gNet_mode != eNet_mode_none) {
  704.         for (i = 0; i < gNumber_of_net_players; i++) {
  705.             StopCarBeingIt(gNet_players[i].car);
  706.         }
  707.     }
  708.     gProgram_state.racing = 0;
  709.     if (gNet_mode == eNet_mode_host) {
  710.         gCurrent_net_game->status.stage = eNet_game_starting;
  711.     }
  712.     WaitForNoKeys();
  713.     if (gNet_mode && gAbandon_game) {
  714.         gProgram_state.prog_status = eProg_idling;
  715.     }
  716.     return result;
  717. }
  718.  
  719. // IDA: tRace_result __cdecl DoRace()
  720. tRace_result DoRace(void) {
  721.     tRace_result result;
  722.     LOG_TRACE("()");
  723.  
  724.     gRace_start = GetTotalTime();
  725.     result = MainGameLoop();
  726.     return result;
  727. }
  728.