Subversion Repositories Games.Carmageddon

Rev

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