Subversion Repositories Games.Carmageddon

Rev

Rev 20 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "structur.h"
  2. #include "car.h"
  3. #include "controls.h"
  4. #include "crush.h"
  5. #include "cutscene.h"
  6. #include "displays.h"
  7. #include "drmem.h"
  8. #include "finteray.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/trace.h"
  16. #include "init.h"
  17. #include "loading.h"
  18. #include "loadsave.h"
  19. #include "mainloop.h"
  20. #include "mainmenu.h"
  21. #include "netgame.h"
  22. #include "network.h"
  23. #include "opponent.h"
  24. #include "piping.h"
  25. #include "pratcam.h"
  26. #include "racestrt.h"
  27. #include "racesumm.h"
  28. #include "sound.h"
  29. #include "utility.h"
  30. #include <stdlib.h>
  31.  
  32. int gLast_wrong_checkpoint;
  33. int gMirror_on__structur = 1; // suffix added to avoid duplicate symbol
  34. int gPratcam_on = 1;
  35. int gCockpit_on = 1;
  36. int gOpponent_mix[10][5] = {
  37.     { 3, 4, 4, 5, 5 },
  38.     { 2, 3, 4, 5, 5 },
  39.     { 2, 3, 4, 4, 5 },
  40.     { 2, 2, 4, 4, 5 },
  41.     { 2, 2, 3, 4, 5 },
  42.     { 1, 2, 3, 4, 4 },
  43.     { 1, 2, 3, 3, 4 },
  44.     { 1, 2, 2, 3, 4 },
  45.     { 1, 1, 2, 3, 3 },
  46.     { 1, 1, 2, 2, 3 }
  47. };
  48. tU32 gLast_checkpoint_time;
  49. tRace_over_reason gRace_over_reason;
  50.  
  51. // IDA: int __cdecl NumberOfOpponentsLeft()
  52. int NumberOfOpponentsLeft(void) {
  53.     int i;
  54.     int car_count;
  55.     int result;
  56.     tCar_spec* the_car;
  57.     LOG_TRACE("()");
  58.  
  59.     result = 0;
  60.     car_count = GetCarCount(eVehicle_opponent);
  61.     for (i = 0; car_count > i; ++i) {
  62.         the_car = GetCarSpec(eVehicle_opponent, i);
  63.         if (!the_car->knackered) {
  64.             result++;
  65.         }
  66.     }
  67.     return result;
  68. }
  69.  
  70. // IDA: void __usercall RaceCompleted(tRace_over_reason pReason@<EAX>)
  71. void RaceCompleted(tRace_over_reason pReason) {
  72.     LOG_TRACE("(%d)", pReason);
  73.  
  74.     if (!gRace_finished) {
  75.         if (gNet_mode == eNet_mode_host && pReason < eRace_over_network_victory) {
  76.             NetFinishRace(gCurrent_net_game, pReason);
  77.         }
  78.         if (pReason == eRace_over_out_of_time || pReason == eRace_over_demo) {
  79.             ChangeAmbientPratcam(35);
  80.         } else if (pReason < eRace_over_abandoned) {
  81.             ChangeAmbientPratcam(34);
  82.         }
  83.         gRace_over_reason = pReason;
  84.         if (gMap_mode) {
  85.             ToggleMap();
  86.         }
  87.         switch (gRace_over_reason) {
  88.         case eRace_over_laps:
  89.         case eRace_over_peds:
  90.         case eRace_over_opponents:
  91.             ChangeAmbientPratcam(34);
  92.             DoFancyHeadup(kFancyHeadupRaceCompleted);
  93.             DRS3StartSound(gPedestrians_outlet, 8011);
  94.             break;
  95.         case eRace_over_abandoned:
  96.             if (gNet_mode == eNet_mode_client) {
  97.                 gHost_abandon_game = 1;
  98.                 NetFullScreenMessage(87, 0);
  99.             }
  100.             break;
  101.         case eRace_over_out_of_time:
  102.             ChangeAmbientPratcam(35);
  103.             DoFancyHeadup(kFancyHeadupOutOfTime);
  104.             DRS3StartSound(gPedestrians_outlet, 8010);
  105.             break;
  106.         case eRace_over_demo:
  107.             ChangeAmbientPratcam(35);
  108.             DoFancyHeadup(kFancyHeadupDemoTimeout);
  109.             break;
  110.         case eRace_over_network_victory:
  111.             ChangeAmbientPratcam(34);
  112.             DoFancyHeadup(kFancyHeadupNetworkVictory);
  113.             break;
  114.         case eRace_over_network_loss:
  115.             ChangeAmbientPratcam(36);
  116.             DoFancyHeadup(kFancyHeadupNetworkRaceOverNetworkLoss);
  117.             break;
  118.         default:
  119.             break;
  120.         }
  121.         if (gNet_mode != eNet_mode_none) {
  122.             gRace_finished = 8000;
  123.         } else {
  124.             gRace_finished = 4000;
  125.         }
  126.     }
  127. }
  128.  
  129. // IDA: void __usercall Checkpoint(int pCheckpoint_index@<EAX>, int pDo_sound@<EDX>)
  130. void Checkpoint(int pCheckpoint_index, int pDo_sound) {
  131.     LOG_TRACE("(%d, %d)", pCheckpoint_index, pDo_sound);
  132.  
  133.     PratcamEvent(33);
  134.     DoFancyHeadup(kFancyHeadupCheckpoint);
  135.     if (pDo_sound) {
  136.         DRS3StartSound(gPedestrians_outlet, 8012);
  137.     }
  138. }
  139.  
  140. // IDA: void __cdecl IncrementCheckpoint()
  141. void IncrementCheckpoint(void) {
  142.     int done_voice;
  143.     LOG_TRACE("()");
  144.  
  145.     done_voice = 0;
  146.     if (gRace_finished) {
  147.         return;
  148.     }
  149.     gLast_checkpoint_time = GetTotalTime();
  150.     if (gCheckpoint < gCheckpoint_count) {
  151.         gCheckpoint++;
  152.     } else {
  153.         gCheckpoint = 1;
  154.         gLap++;
  155.         if (gLap == gTotal_laps) {
  156.             PratcamEvent(33); // FIXME: or PratcamEventNow
  157.             NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, GetMiscString(kMiscString_FinalLap));
  158.             DRS3StartSound(gPedestrians_outlet, 8014);
  159.             done_voice = 1;
  160.         } else if (gLap > gTotal_laps) {
  161.             gLap = gTotal_laps;
  162.             gCheckpoint = gCheckpoint_count;
  163.             RaceCompleted(eRace_over_laps);
  164.         }
  165.     }
  166.     if (!gRace_finished) {
  167.         Checkpoint(gCheckpoint, !done_voice);
  168.         if (gCheck_point_cash[gProgram_state.skill_level] != 0) {
  169.             EarnCredits(gCheck_point_cash[gProgram_state.skill_level]);
  170.         }
  171.     }
  172. }
  173.  
  174. // IDA: void __cdecl IncrementLap()
  175. void IncrementLap(void) {
  176.     int i;
  177.     LOG_TRACE("()");
  178.  
  179.     for (i = gCheckpoint; i <= gCheckpoint_count; i++) {
  180.         IncrementCheckpoint();
  181.     }
  182. }
  183.  
  184. // IDA: int __usercall RayHitFace@<EAX>(br_vector3 *pV0@<EAX>, br_vector3 *pV1@<EDX>, br_vector3 *pV2@<EBX>, br_vector3 *pNormal@<ECX>, br_vector3 *pStart, br_vector3 *pDir)
  185. int RayHitFace(br_vector3* pV0, br_vector3* pV1, br_vector3* pV2, br_vector3* pNormal, br_vector3* pStart, br_vector3* pDir) {
  186.     tFace_ref the_face;
  187.     br_scalar rt;
  188.     LOG_TRACE("(%p, %p, %p, %p, %p, %p)", pV0, pV1, pV2, pNormal, pStart, pDir);
  189.  
  190.     the_face.material = NULL;
  191.     BrVector3Copy(&the_face.v[0], pV0);
  192.     BrVector3Copy(&the_face.v[1], pV1);
  193.     BrVector3Copy(&the_face.v[2], pV2);
  194.     BrVector3Copy(&the_face.normal, pNormal);
  195.     CheckSingleFace(&the_face, pStart, pDir, &the_face.normal, &rt);
  196.     return rt >= 0.f && rt <= 1.f;
  197. }
  198.  
  199. // IDA: void __usercall WrongCheckpoint(int pCheckpoint_index@<EAX>)
  200. void WrongCheckpoint(int pCheckpoint_index) {
  201.     LOG_TRACE("(%d)", pCheckpoint_index);
  202.  
  203.     if ((pCheckpoint_index == gLast_wrong_checkpoint && GetTotalTime() - gLast_checkpoint_time > 20000) || (pCheckpoint_index != gLast_wrong_checkpoint && GetTotalTime() - gLast_checkpoint_time > 2000)) {
  204.         if (gNet_mode == eNet_mode_none) {
  205.             if (gCheckpoint == ((gCurrent_race.check_point_count < pCheckpoint_index + 2) ? ((gLap == 1) ? -1 : 1) : (pCheckpoint_index + 2))) {
  206.                 return;
  207.             }
  208.         }
  209.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, GetMiscString(kMiscString_WrongCheckpoint));
  210.         DRS3StartSound(gPedestrians_outlet, 8013);
  211.         gLast_checkpoint_time = GetTotalTime();
  212.         gLast_wrong_checkpoint = pCheckpoint_index;
  213.     }
  214. }
  215.  
  216. // IDA: void __cdecl CheckCheckpoints()
  217. void CheckCheckpoints(void) {
  218.     tCar_spec* car;
  219.     br_vector3 orig;
  220.     br_vector3 dir;
  221.     int i;
  222.     int j;
  223.     int cat;
  224.     int car_count;
  225.     int car_index;
  226.     tNet_game_player_info* net_player;
  227.     LOG_TRACE("()");
  228.  
  229.     if (gNet_mode == eNet_mode_client) {
  230.         return;
  231.     }
  232.     if (gNet_mode == eNet_mode_host && gCurrent_net_game->type != eNet_game_type_checkpoint && gCurrent_net_game->type != eNet_game_type_sudden_death) {
  233.         return;
  234.     }
  235.     // in single-player mode (=eNet_mode_none), only the player will be checked,
  236.     // in multi-player, the host + clients will be tested, there are no drone opponents there
  237.     for (cat = 0; cat <= gNet_mode; cat++) {
  238.         if (cat == eVehicle_self) {
  239.             car_count = 1;
  240.         } else {
  241.             car_count = GetCarCount(cat);
  242.         }
  243.         for (car_index = 0; car_index < car_count; car_index++) {
  244.             if (cat == eVehicle_self) {
  245.                 car = &gProgram_state.current_car;
  246.             } else {
  247.                 car = GetCarSpec(cat, car_index);
  248.             }
  249.             BrVector3Copy(&orig, (br_vector3*)car->old_frame_mat.m[3]);
  250.             BrVector3Sub(&dir, &car->car_master_actor->t.t.translate.t, &orig);
  251.             for (i = 0; i < gCurrent_race.check_point_count; i++) {
  252.                 for (j = 0; j < gCurrent_race.checkpoints[i].quad_count; j++) {
  253.                     if (
  254.                         RayHitFace(&gCurrent_race.checkpoints[i].vertices[j][0],
  255.                             &gCurrent_race.checkpoints[i].vertices[j][1],
  256.                             &gCurrent_race.checkpoints[i].vertices[j][2],
  257.                             &gCurrent_race.checkpoints[i].normal[j],
  258.                             &orig, &dir)
  259.                         || RayHitFace(&gCurrent_race.checkpoints[i].vertices[j][0],
  260.                             &gCurrent_race.checkpoints[i].vertices[j][2],
  261.                             &gCurrent_race.checkpoints[i].vertices[j][3],
  262.                             &gCurrent_race.checkpoints[i].normal[j],
  263.                             &orig,
  264.                             &dir)) {
  265.                         if (gNet_mode == eNet_mode_none) {
  266.                             if (i + 1 == gCheckpoint) {
  267.                                 IncrementCheckpoint();
  268.                             } else {
  269.                                 WrongCheckpoint(i);
  270.                             }
  271.                         } else {
  272.                             net_player = NetPlayerFromCar(car);
  273.                             if (gCurrent_net_game->type == eNet_game_type_checkpoint) {
  274.                                 if (net_player->score & (1 << i)) {
  275.                                     net_player->score &= ~(1 << i);
  276.                                     SendGameplay(net_player->ID, eNet_gameplay_checkpoint, i, 0, 0, 0);
  277.                                 } else {
  278.                                     SendGameplay(net_player->ID, eNet_gameplay_wrong_checkpoint, i, 0, 0, 0);
  279.                                 }
  280.                             } else if (net_player->score % gCurrent_race.check_point_count == i) {
  281.                                 net_player->score++;
  282.                                 SendGameplay(net_player->ID, eNet_gameplay_checkpoint, i, 0, 0, 0);
  283.                             } else {
  284.                                 SendGameplay(net_player->ID, eNet_gameplay_wrong_checkpoint, i, 0, 0, 0);
  285.                             }
  286.                         }
  287.                         break;
  288.                     }
  289.                 }
  290.             }
  291.         }
  292.         car->old_frame_mat = car->car_master_actor->t.t.mat;
  293.     }
  294. }
  295.  
  296. // IDA: void __cdecl TotalRepair()
  297. void TotalRepair(void) {
  298.     LOG_TRACE("()");
  299.  
  300.     TotallyRepairCar();
  301.     NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, GetMiscString(kMiscString_InstantRepair));
  302. }
  303.  
  304. // IDA: void __cdecl DoLogos()
  305. void DoLogos(void) {
  306.     ClearEntireScreen();
  307.     DoSCILogo();
  308.     DoOpeningAnimation();
  309.     DoStainlessLogo();
  310. #ifdef DETHRACE_FIX_BUGS
  311.     /* StartMusic is only called in PlaySmackerFile when sound and cutscenes are enabled */
  312.     if (!gSound_override && gCut_scene_override) {
  313.         if (!harness_game_config.no_music) {
  314.             StartMusic();
  315.         }
  316.     }
  317. #endif
  318.     gProgram_state.prog_status = eProg_opening;
  319. }
  320.  
  321. // IDA: void __cdecl DoProgOpeningAnimation()
  322. void DoProgOpeningAnimation(void) {
  323.     LOG_TRACE("()");
  324.  
  325.     gProgram_state.prog_status = eProg_idling;
  326.     DRS3StopOutletSound(gEffects_outlet);
  327. }
  328.  
  329. // IDA: void __cdecl DoProgramDemo()
  330. void DoProgramDemo(void) {
  331.     LOG_TRACE("()");
  332.  
  333.     DoLogos();
  334.     gProgram_state.prog_status = eProg_idling;
  335.     DRS3StopOutletSound(gEffects_outlet);
  336. }
  337.  
  338. // IDA: int __usercall ChooseOpponent@<EAX>(int pNastiness@<EAX>, int *pHad_scum@<EDX>)
  339. int ChooseOpponent(int pNastiness, int* pHad_scum) {
  340.     int i;
  341.     int count;
  342.     int temp_array[40];
  343.     LOG_TRACE("(%d, %p)", pNastiness, pHad_scum);
  344.  
  345.     count = 0;
  346.     for (i = 0; i < gNumber_of_racers; ++i) {
  347.         if (gOpponents[i].strength_rating == pNastiness
  348.             && gProgram_state.current_car.index != i
  349.             && !gOpponents[i].picked
  350.             && (gOpponents[i].car_number >= 0 || !*pHad_scum)) {
  351.             temp_array[count++] = i;
  352.         }
  353.     }
  354.     i = temp_array[IRandomBetween(0, count - 1)];
  355.     gOpponents[i].picked = 1;
  356.     if (gOpponents[i].car_number < 0) {
  357.         *pHad_scum = 1;
  358.     }
  359.     return i;
  360. }
  361.  
  362. // IDA: void __usercall SelectOpponents(tRace_info *pRace_info@<EAX>)
  363. void SelectOpponents(tRace_info* pRace_info) {
  364.     int i;
  365.     int rank_band;
  366.     int nastiness;
  367.     int had_scum;
  368.     LOG_TRACE("(%p)", pRace_info);
  369.  
  370.     if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  371.         pRace_info->number_of_racers = OPPONENT_COUNT;
  372.         for (i = 0; i < OPPONENT_COUNT; i++) {
  373.             pRace_info->opponent_list[i].index = gDemo_opponents[i];
  374.             pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
  375.         }
  376.         return;
  377.     }
  378.  
  379.     had_scum = 0;
  380.     if (gNet_mode == eNet_mode_none) {
  381.         pRace_info->number_of_racers = OPPONENT_COUNT;
  382.         for (i = 0; i < gNumber_of_racers; ++i) {
  383.             gOpponents[i].picked = 0;
  384.         }
  385.         if (gRace_list[gProgram_state.current_race_index].suggested_rank < 0) {
  386.             rank_band = 0;
  387.         } else {
  388.             rank_band = gRace_list[gProgram_state.current_race_index].suggested_rank / 10;
  389.         }
  390.         for (i = 0; i < OPPONENT_COUNT; i++) {
  391.             nastiness = gOpponent_mix[rank_band][i];
  392.             pRace_info->opponent_list[i].index = ChooseOpponent(nastiness, &had_scum);
  393.             pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
  394.         }
  395.     }
  396. }
  397.  
  398. // IDA: int __usercall PickNetRace@<EAX>(int pCurrent_race@<EAX>, tNet_sequence_type pNet_race_sequence@<EDX>)
  399. int PickNetRace(int pCurrent_race, tNet_sequence_type pNet_race_sequence) {
  400.     int i;
  401.     int new_index;
  402.     int races_count;
  403.     int most_seldom_seen;
  404.     int races_to_pick_from[50];
  405.     LOG_TRACE("(%d, %d)", pCurrent_race, pNet_race_sequence);
  406.  
  407.     if (pNet_race_sequence == eNet_sequence_sequential) {
  408.         pCurrent_race++;
  409.         if (pCurrent_race >= gNumber_of_races) {
  410.             pCurrent_race = 0;
  411.         }
  412.     } else {
  413.         most_seldom_seen = 10000;
  414.         for (i = 0; i < gNumber_of_races; i++) {
  415.             if (gRace_list[i].been_there_done_that < most_seldom_seen) {
  416.                 most_seldom_seen = gRace_list[i].been_there_done_that;
  417.             }
  418.         }
  419.         races_count = 0;
  420.         for (i = 0; i < gNumber_of_races; i++) {
  421.             if (gRace_list[i].been_there_done_that == most_seldom_seen && (i != pCurrent_race)) {
  422.                 races_to_pick_from[races_count] = i;
  423.                 races_count++;
  424.             }
  425.         }
  426.         new_index = IRandomBetween(0, races_count - 1);
  427.         pCurrent_race = races_to_pick_from[new_index];
  428.         gRace_list[pCurrent_race].been_there_done_that++;
  429.     }
  430.     return pCurrent_race;
  431. }
  432.  
  433. // IDA: void __cdecl SwapNetCarsLoad()
  434. void SwapNetCarsLoad(void) {
  435.     int i;
  436.     int switched_res;
  437.     LOG_TRACE("()");
  438.  
  439.     DisableNetService();
  440.     AboutToLoadFirstCar();
  441.     switched_res = SwitchToRealResolution();
  442.     for (i = 0; i < gNumber_of_net_players; i++) {
  443.         if (gNet_players[i].next_car_index >= 0) {
  444.             gNet_players[i].car_index = gNet_players[i].next_car_index;
  445.         }
  446.         gNet_players[i].next_car_index = -1;
  447.         LoadCar(gOpponents[gNet_players[i].car_index].car_file_name,
  448.             (gThis_net_player_index == i) ? eDriver_local_human : eDriver_net_human,
  449.             gNet_players[i].car, gNet_players[i].car_index, gNet_players[i].player_name,
  450.             &gNet_cars_storage_space);
  451.     }
  452.     if (switched_res) {
  453.         SwitchToLoresMode();
  454.     }
  455.     ReenableNetService();
  456. }
  457.  
  458. // IDA: void __cdecl SwapNetCarsDispose()
  459. void SwapNetCarsDispose(void) {
  460.     //int i; // Pierre-Marie Baty -- unused variable
  461.     LOG_TRACE("()");
  462.     NOT_IMPLEMENTED();
  463. }
  464.  
  465. // IDA: void __cdecl DoGame()
  466. void DoGame(void) {
  467.     tSO_result options_result;
  468.     tRace_result race_result;
  469.     int second_select_race;
  470.     int first_summary_done;
  471.     int i;
  472.     LOG_TRACE("()");
  473.  
  474.     gAbandon_game = 0;
  475.     gDisallow_abandon_race = 0;
  476.     gCar_to_view = &gProgram_state.current_car;
  477.     StartLoadingScreen();
  478.     gProgram_state.prog_status = eProg_game_ongoing;
  479.     second_select_race = 0;
  480.     if (gNet_mode == gNet_mode_of_last_game) {
  481.         PrintMemoryDump(0, "BEFORE START RACE SCREEN");
  482.         SelectOpponents(&gCurrent_race);
  483.         if (gNet_mode != eNet_mode_none) {
  484.             LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
  485.             FillInRaceInfo(&gCurrent_race);
  486.             DisposeRaceInfo(&gCurrent_race);
  487.         } else {
  488.             do {
  489.                 options_result = DoSelectRace(&second_select_race);
  490.                 if (options_result == eSO_main_menu_invoked) {
  491.                     DoMainMenuScreen(0, 1, 1);
  492.                 }
  493.                 gDisallow_abandon_race = 0;
  494.             } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing && !gAbandon_game);
  495.         }
  496.         if (gProgram_state.prog_status == eProg_game_starting
  497.             || gProgram_state.prog_status == eProg_quit
  498.             || gProgram_state.prog_status == eProg_idling
  499.             || gAbandon_game) {
  500.             PrintMemoryDump(0, "AFTER ABANDONING START RACE SCREEN");
  501.             if (gProgram_state.prog_status == eProg_game_ongoing) {
  502.                 gProgram_state.prog_status = eProg_game_starting;
  503.             }
  504.         } else {
  505.             PrintMemoryDump(0, "AFTER START RACE SCREEN");
  506.             DoNewGameAnimation();
  507.             StartLoadingScreen();
  508.             if (gNet_mode != eNet_mode_none) {
  509.                 if (gCurrent_net_game->options.random_car_choice
  510.                     && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)
  511.                     && !gNo_races_yet) {
  512.                     SwapNetCarsLoad();
  513.                 }
  514.             } else {
  515.                 LoadOpponentsCars(&gCurrent_race);
  516.             }
  517.             PrintMemoryDump(0, "AFTER LOADING OPPONENTS IN");
  518.             InitRace();
  519.             if (gNet_mode_of_last_game == gNet_mode) {
  520.                 if (gProgram_state.prog_status == eProg_game_starting
  521.                     || gProgram_state.prog_status == eProg_quit
  522.                     || gProgram_state.prog_status == eProg_idling
  523.                     || gAbandon_game) {
  524.                     DisposeRace();
  525.                     if (gNet_mode == eNet_mode_none && gNet_mode_of_last_game == eNet_mode_none) {
  526.                         DisposeOpponentsCars(&gCurrent_race);
  527.                     }
  528.                     DisposeTrack();
  529.                     if (gProgram_state.prog_status == eProg_game_ongoing) {
  530.                         gProgram_state.prog_status = eProg_game_starting;
  531.                     }
  532.                 } else {
  533.                     if (gNet_mode != eNet_mode_none) {
  534.                         do {
  535.                             options_result = NetSynchRaceStart();
  536.                             if (options_result == eSO_main_menu_invoked) {
  537.                                 DoMainMenuScreen(0, 1, 1);
  538.                             }
  539.                         } while (options_result == eSO_main_menu_invoked
  540.                             && gProgram_state.prog_status == eProg_game_ongoing
  541.                             && !gAbandon_game);
  542.                     } else {
  543.                         do {
  544.                             options_result = DoGridPosition();
  545.                             if (options_result == eSO_main_menu_invoked) {
  546.                                 DoMainMenuScreen(0, 1, 1);
  547.                             }
  548.                         } while (options_result == eSO_main_menu_invoked
  549.                             && gProgram_state.prog_status == eProg_game_ongoing
  550.                             && !gAbandon_game);
  551.                         SetInitialPositions(&gCurrent_race);
  552.                     }
  553.                     if (gProgram_state.prog_status == eProg_game_starting
  554.                         || gProgram_state.prog_status == eProg_quit
  555.                         || gProgram_state.prog_status == eProg_idling
  556.                         || gAbandon_game) {
  557.                         DisposeRace();
  558.                         if (!gNet_mode && !gNet_mode_of_last_game) {
  559.                             DisposeOpponentsCars(&gCurrent_race);
  560.                         }
  561.                         DisposeTrack();
  562.                         if (gProgram_state.prog_status == eProg_game_ongoing) {
  563.                             gProgram_state.prog_status = eProg_game_starting;
  564.                         }
  565.                     } else {
  566.                         SwitchToRealResolution();
  567.                         InitOpponents(&gCurrent_race);
  568.                         InitialiseCarsEtc(&gCurrent_race);
  569.                         SetInitialCopPositions();
  570.                         InitSoundSources();
  571.                         InitLastDamageArrayEtc();
  572.                         race_result = DoRace();
  573.                         SwitchToLoresMode();
  574.                         DisposeRace();
  575.                         if (gNet_mode != eNet_mode_none) {
  576.                             gProgram_state.current_race_index = gPending_race;
  577.                             gCurrent_net_game->start_race = gPending_race;
  578.                             gPending_race = -1;
  579.                         }
  580.                         if (race_result == eRace_completed || race_result == eRace_timed_out) {
  581.                             DoEndRaceAnimation();
  582.                             first_summary_done = 0;
  583.                             do {
  584.                                 options_result = DoEndRaceSummary(&first_summary_done, race_result);
  585.                                 if (options_result == eSO_main_menu_invoked) {
  586.                                     DoMainMenuScreen(0, 1, 1);
  587.                                 }
  588.                             } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing);
  589.                         }
  590.                         if (gNet_mode) {
  591.                             for (i = 0; i < gNumber_of_net_players; i++) {
  592.                                 TotallyRepairACar(gNet_players[i].car);
  593.                             }
  594.                         } else {
  595.                             TotallyRepairCar();
  596.                         }
  597.                         if (gNet_mode) {
  598.                             if (gCurrent_net_game->options.random_car_choice
  599.                                 && (gCurrent_net_game->options.car_choice == eNet_car_all
  600.                                     || gCurrent_net_game->options.car_choice == eNet_car_both)
  601.                                 && !gNo_races_yet) {
  602.                                 SwapNetCarsDispose();
  603.                             }
  604.                         } else {
  605.                             DisposeOpponentsCars(&gCurrent_race);
  606.                         }
  607.                         DisposeTrack();
  608.                         if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  609.                             DoFullVersionPowerpoint();
  610.                         }
  611.                         gProgram_state.loaded = 0;
  612.                         if (gProgram_state.prog_status == eProg_game_ongoing) {
  613.                             gProgram_state.prog_status = eProg_game_starting;
  614.                         }
  615.                     }
  616.                 }
  617.             } else {
  618.                 gProgram_state.prog_status = eProg_idling;
  619.             }
  620.         }
  621.     } else {
  622.         gProgram_state.prog_status = eProg_idling;
  623.     }
  624. }
  625.  
  626. // IDA: void __cdecl InitialiseProgramState()
  627. void InitialiseProgramState(void) {
  628.     gProgram_state.loaded = 0;
  629.     gProgram_state.last_slot = 0;
  630.     gProgram_state.frank_or_anniness = eFrankie;
  631.     gProgram_state.skill_level = 1;
  632.     gProgram_state.view_type = eVT_Scene;
  633.     gProgram_state.auto_parts_reply = eAP_auto;
  634.     gProgram_state.racing = 0;
  635.     gProgram_state.cut_scene = 0;
  636.     gProgram_state.saving = 0;
  637.     gProgram_state.loading = 0;
  638.     gProgram_state.dont_save_or_load = 0;
  639.     gProgram_state.dont_load = 0;
  640.     gProgram_state.mirror_on = gMirror_on__structur;
  641.     gProgram_state.prog_status = eProg_intro;
  642.     if (gAusterity_mode) {
  643.         gProgram_state.prat_cam_on = 0;
  644.         gPratcam_on = 0;
  645.     } else {
  646.         gProgram_state.prat_cam_on = gPratcam_on;
  647.     }
  648.     gProgram_state.cockpit_on = gCockpit_on;
  649.     gProgram_state.car_name[0] = 0;
  650.     SetSoundVolumes();
  651.     AllocateRearviewPixelmap();
  652. }
  653.  
  654. // IDA: void __cdecl DoProgram()
  655. void DoProgram(void) {
  656.     InitialiseProgramState();
  657.     while (gProgram_state.prog_status != eProg_quit) {
  658.         switch (gProgram_state.prog_status) {
  659.         case eProg_intro:
  660.             DisposeGameIfNecessary();
  661.             DoLogos();
  662.             break;
  663.         case eProg_opening:
  664.             DisposeGameIfNecessary();
  665.             DoProgOpeningAnimation();
  666.             break;
  667.         case eProg_idling:
  668.             DisposeGameIfNecessary();
  669.             if (gGame_to_load < 0) {
  670.                 DoMainMenuScreen(30000u, 0, 0);
  671.             } else {
  672.                 DoLoadGame();
  673.             }
  674.             break;
  675.         case eProg_demo:
  676.             DoProgramDemo();
  677.             break;
  678.         case eProg_game_starting:
  679.             DoGame();
  680.             break;
  681.         default:
  682.             break;
  683.         }
  684.     }
  685. }
  686.  
  687. // IDA: void __cdecl JumpTheStart()
  688. void JumpTheStart(void) {
  689.     char s[256];
  690.     LOG_TRACE("()");
  691.  
  692.     if (gNet_mode == eNet_mode_none
  693.         || gProgram_state.credits_earned - gProgram_state.credits_lost >= gJump_start_fine[gProgram_state.skill_level]) {
  694.         WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(gCountdown);
  695.         gCountdown = 0;
  696.         DRS3StopOutletSound(gPedestrians_outlet);
  697.         DRS3StartSound(gPedestrians_outlet, 8016);
  698.         SpendCredits(gJump_start_fine[gProgram_state.skill_level]);
  699.         sprintf(s, "%s %d %s", GetMiscString(gProgram_state.frank_or_anniness == eFrankie ? kMiscString_BadBoy : kMiscString_BadGirl), gJump_start_fine[gProgram_state.skill_level], GetMiscString(kMiscString_CreditFine));
  700.         NewTextHeadupSlot(eHeadupSlot_misc, 0, 1000, -4, s);
  701.     }
  702. }
  703.  
  704. // IDA: void __cdecl GoingToInterfaceFromRace()
  705. void GoingToInterfaceFromRace(void) {
  706.     LOG_TRACE("()");
  707.  
  708.     gInterface_within_race_mode = 1;
  709.     PlayFlicsFromDisk();
  710.     SwitchToLoresMode();
  711.     if (gNet_mode == eNet_mode_host) {
  712.         SendGameplayToAllPlayers(eNet_gameplay_host_paused, 0, 0, 0, 0);
  713.     }
  714. }
  715.  
  716. // IDA: void __cdecl GoingBackToRaceFromInterface()
  717. void GoingBackToRaceFromInterface(void) {
  718.     LOG_TRACE("()");
  719.  
  720.     gInterface_within_race_mode = 0;
  721.     PlayFlicsFromMemory();
  722.     SwitchToRealResolution();
  723.     if (gNet_mode == eNet_mode_host) {
  724.         SendGameplayToAllPlayers(eNet_gameplay_host_unpaused, 0, 0, 0, 0);
  725.     }
  726. }
  727.