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 "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(4, 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(4, 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(4, 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.     gProgram_state.prog_status = eProg_opening;
  311. }
  312.  
  313. // IDA: void __cdecl DoProgOpeningAnimation()
  314. void DoProgOpeningAnimation(void) {
  315.     LOG_TRACE("()");
  316.  
  317.     gProgram_state.prog_status = eProg_idling;
  318.     DRS3StopOutletSound(gEffects_outlet);
  319. }
  320.  
  321. // IDA: void __cdecl DoProgramDemo()
  322. void DoProgramDemo(void) {
  323.     LOG_TRACE("()");
  324.  
  325.     DoLogos();
  326.     gProgram_state.prog_status = eProg_idling;
  327.     DRS3StopOutletSound(gEffects_outlet);
  328. }
  329.  
  330. // IDA: int __usercall ChooseOpponent@<EAX>(int pNastiness@<EAX>, int *pHad_scum@<EDX>)
  331. int ChooseOpponent(int pNastiness, int* pHad_scum) {
  332.     int i;
  333.     int count;
  334.     int temp_array[40];
  335.     LOG_TRACE("(%d, %p)", pNastiness, pHad_scum);
  336.  
  337.     count = 0;
  338.     for (i = 0; i < gNumber_of_racers; ++i) {
  339.         if (gOpponents[i].strength_rating == pNastiness
  340.             && gProgram_state.current_car.index != i
  341.             && !gOpponents[i].picked
  342.             && (gOpponents[i].car_number >= 0 || !*pHad_scum)) {
  343.             temp_array[count++] = i;
  344.         }
  345.     }
  346.     i = temp_array[IRandomBetween(0, count - 1)];
  347.     gOpponents[i].picked = 1;
  348.     if (gOpponents[i].car_number < 0) {
  349.         *pHad_scum = 1;
  350.     }
  351.     return i;
  352. }
  353.  
  354. // IDA: void __usercall SelectOpponents(tRace_info *pRace_info@<EAX>)
  355. void SelectOpponents(tRace_info* pRace_info) {
  356.     int i;
  357.     int rank_band;
  358.     int nastiness;
  359.     int had_scum;
  360.     LOG_TRACE("(%p)", pRace_info);
  361.  
  362.     if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  363.         pRace_info->number_of_racers = OPPONENT_COUNT;
  364.         for (i = 0; i < OPPONENT_COUNT; i++) {
  365.             pRace_info->opponent_list[i].index = gDemo_opponents[i];
  366.             pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
  367.         }
  368.         return;
  369.     }
  370.  
  371.     had_scum = 0;
  372.     if (gNet_mode == eNet_mode_none) {
  373.         pRace_info->number_of_racers = OPPONENT_COUNT;
  374.         for (i = 0; i < gNumber_of_racers; ++i) {
  375.             gOpponents[i].picked = 0;
  376.         }
  377.         if (gRace_list[gProgram_state.current_race_index].suggested_rank < 0) {
  378.             rank_band = 0;
  379.         } else {
  380.             rank_band = gRace_list[gProgram_state.current_race_index].suggested_rank / 10;
  381.         }
  382.         for (i = 0; i < OPPONENT_COUNT; i++) {
  383.             nastiness = gOpponent_mix[rank_band][i];
  384.             pRace_info->opponent_list[i].index = ChooseOpponent(nastiness, &had_scum);
  385.             pRace_info->opponent_list[i].ranking = IRandomBetween(gProgram_state.rank - 10, gProgram_state.rank + 10);
  386.         }
  387.     }
  388. }
  389.  
  390. // IDA: int __usercall PickNetRace@<EAX>(int pCurrent_race@<EAX>, tNet_sequence_type pNet_race_sequence@<EDX>)
  391. int PickNetRace(int pCurrent_race, tNet_sequence_type pNet_race_sequence) {
  392.     int i;
  393.     int new_index;
  394.     int races_count;
  395.     int most_seldom_seen;
  396.     int races_to_pick_from[50];
  397.     LOG_TRACE("(%d, %d)", pCurrent_race, pNet_race_sequence);
  398.  
  399.     if (pNet_race_sequence == eNet_sequence_sequential) {
  400.         pCurrent_race++;
  401.         if (pCurrent_race >= gNumber_of_races) {
  402.             pCurrent_race = 0;
  403.         }
  404.     } else {
  405.         most_seldom_seen = 10000;
  406.         for (i = 0; i < gNumber_of_races; i++) {
  407.             if (gRace_list[i].been_there_done_that < most_seldom_seen) {
  408.                 most_seldom_seen = gRace_list[i].been_there_done_that;
  409.             }
  410.         }
  411.         races_count = 0;
  412.         for (i = 0; i < gNumber_of_races; i++) {
  413.             if (gRace_list[i].been_there_done_that == most_seldom_seen && (i != pCurrent_race)) {
  414.                 races_to_pick_from[races_count] = i;
  415.                 races_count++;
  416.             }
  417.         }
  418.         new_index = IRandomBetween(0, races_count - 1);
  419.         pCurrent_race = races_to_pick_from[new_index];
  420.         gRace_list[pCurrent_race].been_there_done_that++;
  421.     }
  422.     return pCurrent_race;
  423. }
  424.  
  425. // IDA: void __cdecl SwapNetCarsLoad()
  426. void SwapNetCarsLoad(void) {
  427.     int i;
  428.     int switched_res;
  429.     LOG_TRACE("()");
  430.  
  431.     DisableNetService();
  432.     AboutToLoadFirstCar();
  433.     switched_res = SwitchToRealResolution();
  434.     for (i = 0; i < gNumber_of_net_players; i++) {
  435.         if (gNet_players[i].next_car_index >= 0) {
  436.             gNet_players[i].car_index = gNet_players[i].next_car_index;
  437.         }
  438.         gNet_players[i].next_car_index = -1;
  439.         LoadCar(gOpponents[gNet_players[i].car_index].car_file_name,
  440.             (gThis_net_player_index == i) ? eDriver_local_human : eDriver_net_human,
  441.             gNet_players[i].car, gNet_players[i].car_index, gNet_players[i].player_name,
  442.             &gNet_cars_storage_space);
  443.     }
  444.     if (switched_res) {
  445.         SwitchToLoresMode();
  446.     }
  447.     ReenableNetService();
  448. }
  449.  
  450. // IDA: void __cdecl SwapNetCarsDispose()
  451. void SwapNetCarsDispose(void) {
  452.     //int i; // Pierre-Marie Baty -- unused variable
  453.     LOG_TRACE("()");
  454.     NOT_IMPLEMENTED();
  455. }
  456.  
  457. // IDA: void __cdecl DoGame()
  458. void DoGame(void) {
  459.     tSO_result options_result;
  460.     tRace_result race_result;
  461.     int second_select_race;
  462.     int first_summary_done;
  463.     int i;
  464.     LOG_TRACE("()");
  465.  
  466.     gAbandon_game = 0;
  467.     gDisallow_abandon_race = 0;
  468.     gCar_to_view = &gProgram_state.current_car;
  469.     StartLoadingScreen();
  470.     gProgram_state.prog_status = eProg_game_ongoing;
  471.     second_select_race = 0;
  472.     if (gNet_mode == gNet_mode_of_last_game) {
  473.         PrintMemoryDump(0, "BEFORE START RACE SCREEN");
  474.         SelectOpponents(&gCurrent_race);
  475.         if (gNet_mode != eNet_mode_none) {
  476.             LoadRaceInfo(gProgram_state.current_race_index, &gCurrent_race);
  477.             FillInRaceInfo(&gCurrent_race);
  478.             DisposeRaceInfo(&gCurrent_race);
  479.         } else {
  480.             do {
  481.                 options_result = DoSelectRace(&second_select_race);
  482.                 if (options_result == eSO_main_menu_invoked) {
  483.                     DoMainMenuScreen(0, 1, 1);
  484.                 }
  485.                 gDisallow_abandon_race = 0;
  486.             } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing && !gAbandon_game);
  487.         }
  488.         if (gProgram_state.prog_status == eProg_game_starting
  489.             || gProgram_state.prog_status == eProg_quit
  490.             || gProgram_state.prog_status == eProg_idling
  491.             || gAbandon_game) {
  492.             PrintMemoryDump(0, "AFTER ABANDONING START RACE SCREEN");
  493.             if (gProgram_state.prog_status == eProg_game_ongoing) {
  494.                 gProgram_state.prog_status = eProg_game_starting;
  495.             }
  496.         } else {
  497.             PrintMemoryDump(0, "AFTER START RACE SCREEN");
  498.             DoNewGameAnimation();
  499.             StartLoadingScreen();
  500.             if (gNet_mode != eNet_mode_none) {
  501.                 if (gCurrent_net_game->options.random_car_choice
  502.                     && (gCurrent_net_game->options.car_choice == eNet_car_all || gCurrent_net_game->options.car_choice == eNet_car_both)
  503.                     && !gNo_races_yet) {
  504.                     SwapNetCarsLoad();
  505.                 }
  506.             } else {
  507.                 LoadOpponentsCars(&gCurrent_race);
  508.             }
  509.             PrintMemoryDump(0, "AFTER LOADING OPPONENTS IN");
  510.             InitRace();
  511.             if (gNet_mode_of_last_game == gNet_mode) {
  512.                 if (gProgram_state.prog_status == eProg_game_starting
  513.                     || gProgram_state.prog_status == eProg_quit
  514.                     || gProgram_state.prog_status == eProg_idling
  515.                     || gAbandon_game) {
  516.                     DisposeRace();
  517.                     if (gNet_mode == eNet_mode_none && gNet_mode_of_last_game == eNet_mode_none) {
  518.                         DisposeOpponentsCars(&gCurrent_race);
  519.                     }
  520.                     DisposeTrack();
  521.                     if (gProgram_state.prog_status == eProg_game_ongoing) {
  522.                         gProgram_state.prog_status = eProg_game_starting;
  523.                     }
  524.                 } else {
  525.                     if (gNet_mode != eNet_mode_none) {
  526.                         do {
  527.                             options_result = NetSynchRaceStart();
  528.                             if (options_result == eSO_main_menu_invoked) {
  529.                                 DoMainMenuScreen(0, 1, 1);
  530.                             }
  531.                         } while (options_result == eSO_main_menu_invoked
  532.                             && gProgram_state.prog_status == eProg_game_ongoing
  533.                             && !gAbandon_game);
  534.                     } else {
  535.                         do {
  536.                             options_result = DoGridPosition();
  537.                             if (options_result == eSO_main_menu_invoked) {
  538.                                 DoMainMenuScreen(0, 1, 1);
  539.                             }
  540.                         } while (options_result == eSO_main_menu_invoked
  541.                             && gProgram_state.prog_status == eProg_game_ongoing
  542.                             && !gAbandon_game);
  543.                         SetInitialPositions(&gCurrent_race);
  544.                     }
  545.                     if (gProgram_state.prog_status == eProg_game_starting
  546.                         || gProgram_state.prog_status == eProg_quit
  547.                         || gProgram_state.prog_status == eProg_idling
  548.                         || gAbandon_game) {
  549.                         DisposeRace();
  550.                         if (!gNet_mode && !gNet_mode_of_last_game) {
  551.                             DisposeOpponentsCars(&gCurrent_race);
  552.                         }
  553.                         DisposeTrack();
  554.                         if (gProgram_state.prog_status == eProg_game_ongoing) {
  555.                             gProgram_state.prog_status = eProg_game_starting;
  556.                         }
  557.                     } else {
  558.                         SwitchToRealResolution();
  559.                         InitOpponents(&gCurrent_race);
  560.                         InitialiseCarsEtc(&gCurrent_race);
  561.                         SetInitialCopPositions();
  562.                         InitSoundSources();
  563.                         InitLastDamageArrayEtc();
  564.                         race_result = DoRace();
  565.                         SwitchToLoresMode();
  566.                         DisposeRace();
  567.                         if (gNet_mode != eNet_mode_none) {
  568.                             gProgram_state.current_race_index = gPending_race;
  569.                             gCurrent_net_game->start_race = gPending_race;
  570.                             gPending_race = -1;
  571.                         }
  572.                         if (race_result == eRace_completed || race_result == eRace_timed_out) {
  573.                             DoEndRaceAnimation();
  574.                             first_summary_done = 0;
  575.                             do {
  576.                                 options_result = DoEndRaceSummary(&first_summary_done, race_result);
  577.                                 if (options_result == eSO_main_menu_invoked) {
  578.                                     DoMainMenuScreen(0, 1, 1);
  579.                                 }
  580.                             } while (options_result == eSO_main_menu_invoked && gProgram_state.prog_status == eProg_game_ongoing);
  581.                         }
  582.                         if (gNet_mode) {
  583.                             for (i = 0; i < gNumber_of_net_players; i++) {
  584.                                 TotallyRepairACar(gNet_players[i].car);
  585.                             }
  586.                         } else {
  587.                             TotallyRepairCar();
  588.                         }
  589.                         if (gNet_mode) {
  590.                             if (gCurrent_net_game->options.random_car_choice
  591.                                 && (gCurrent_net_game->options.car_choice == eNet_car_all
  592.                                     || gCurrent_net_game->options.car_choice == eNet_car_both)
  593.                                 && !gNo_races_yet) {
  594.                                 SwapNetCarsDispose();
  595.                             }
  596.                         } else {
  597.                             DisposeOpponentsCars(&gCurrent_race);
  598.                         }
  599.                         DisposeTrack();
  600.                         if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  601.                             DoFullVersionPowerpoint();
  602.                         }
  603.                         gProgram_state.loaded = 0;
  604.                         if (gProgram_state.prog_status == eProg_game_ongoing) {
  605.                             gProgram_state.prog_status = eProg_game_starting;
  606.                         }
  607.                     }
  608.                 }
  609.             } else {
  610.                 gProgram_state.prog_status = eProg_idling;
  611.             }
  612.         }
  613.     } else {
  614.         gProgram_state.prog_status = eProg_idling;
  615.     }
  616. }
  617.  
  618. // IDA: void __cdecl InitialiseProgramState()
  619. void InitialiseProgramState(void) {
  620.     gProgram_state.loaded = 0;
  621.     gProgram_state.last_slot = 0;
  622.     gProgram_state.frank_or_anniness = eFrankie;
  623.     gProgram_state.skill_level = 1;
  624.     gProgram_state.view_type = eVT_Scene;
  625.     gProgram_state.auto_parts_reply = eAP_auto;
  626.     gProgram_state.racing = 0;
  627.     gProgram_state.cut_scene = 0;
  628.     gProgram_state.saving = 0;
  629.     gProgram_state.loading = 0;
  630.     gProgram_state.dont_save_or_load = 0;
  631.     gProgram_state.dont_load = 0;
  632.     gProgram_state.mirror_on = gMirror_on__structur;
  633.     gProgram_state.prog_status = eProg_intro;
  634.     if (gAusterity_mode) {
  635.         gProgram_state.prat_cam_on = 0;
  636.         gPratcam_on = 0;
  637.     } else {
  638.         gProgram_state.prat_cam_on = gPratcam_on;
  639.     }
  640.     gProgram_state.cockpit_on = gCockpit_on;
  641.     gProgram_state.car_name[0] = 0;
  642.     SetSoundVolumes();
  643.     AllocateRearviewPixelmap();
  644. }
  645.  
  646. // IDA: void __cdecl DoProgram()
  647. void DoProgram(void) {
  648.     InitialiseProgramState();
  649.     while (gProgram_state.prog_status != eProg_quit) {
  650.         switch (gProgram_state.prog_status) {
  651.         case eProg_intro:
  652.             DisposeGameIfNecessary();
  653.             DoLogos();
  654.             break;
  655.         case eProg_opening:
  656.             DisposeGameIfNecessary();
  657.             DoProgOpeningAnimation();
  658.             break;
  659.         case eProg_idling:
  660.             DisposeGameIfNecessary();
  661.             if (gGame_to_load < 0) {
  662.                 DoMainMenuScreen(30000u, 0, 0);
  663.             } else {
  664.                 DoLoadGame();
  665.             }
  666.             break;
  667.         case eProg_demo:
  668.             DoProgramDemo();
  669.             break;
  670.         case eProg_game_starting:
  671.             DoGame();
  672.             break;
  673.         default:
  674.             break;
  675.         }
  676.     }
  677. }
  678.  
  679. // IDA: void __cdecl JumpTheStart()
  680. void JumpTheStart(void) {
  681.     char s[256];
  682.     LOG_TRACE("()");
  683.  
  684.     if (gNet_mode == eNet_mode_none
  685.         || gProgram_state.credits_earned - gProgram_state.credits_lost >= gJump_start_fine[gProgram_state.skill_level]) {
  686.         WakeUpOpponentsToTheFactThatTheStartHasBeenJumped(gCountdown);
  687.         gCountdown = 0;
  688.         DRS3StopOutletSound(gPedestrians_outlet);
  689.         DRS3StartSound(gPedestrians_outlet, 8016);
  690.         SpendCredits(gJump_start_fine[gProgram_state.skill_level]);
  691.         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));
  692.         NewTextHeadupSlot(4, 0, 1000, -4, s);
  693.     }
  694. }
  695.  
  696. // IDA: void __cdecl GoingToInterfaceFromRace()
  697. void GoingToInterfaceFromRace(void) {
  698.     LOG_TRACE("()");
  699.  
  700.     gInterface_within_race_mode = 1;
  701.     PlayFlicsFromDisk();
  702.     SwitchToLoresMode();
  703.     if (gNet_mode == eNet_mode_host) {
  704.         SendGameplayToAllPlayers(eNet_gameplay_host_paused, 0, 0, 0, 0);
  705.     }
  706. }
  707.  
  708. // IDA: void __cdecl GoingBackToRaceFromInterface()
  709. void GoingBackToRaceFromInterface(void) {
  710.     LOG_TRACE("()");
  711.  
  712.     gInterface_within_race_mode = 0;
  713.     PlayFlicsFromMemory();
  714.     SwitchToRealResolution();
  715.     if (gNet_mode == eNet_mode_host) {
  716.         SendGameplayToAllPlayers(eNet_gameplay_host_unpaused, 0, 0, 0, 0);
  717.     }
  718. }
  719.