Subversion Repositories Games.Carmageddon

Rev

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

  1. #include "racesumm.h"
  2. #include "brender.h"
  3. #include "crush.h"
  4. #include "cutscene.h"
  5. #include "displays.h"
  6. #include "flicplay.h"
  7. #include "globvars.h"
  8. #include "globvrkm.h"
  9. #include "globvrpb.h"
  10. #include "grafdata.h"
  11. #include "graphics.h"
  12. #include "harness/config.h"
  13. #include "harness/trace.h"
  14. #include "input.h"
  15. #include "intrface.h"
  16. #include "loading.h"
  17. #include "main.h"
  18. #include "network.h"
  19. #include "opponent.h"
  20. #include "pd/sys.h"
  21. #include "raycast.h"
  22. #include "s3/s3.h"
  23. #include "sound.h"
  24. #include "utility.h"
  25. #include <stdlib.h>
  26.  
  27. int gPlayer_lookup[6];
  28. tMouse_area gOld_back_button;
  29. tWreck_info gWreck_array[30];
  30. br_actor* gWreck_root;
  31. br_actor* gWreck_camera;
  32. tU32 gWreck_start_zoom;
  33. tU32 gWreck_gallery_start;
  34. float gTemp_rank_increase;
  35. float gRank_per_ms;
  36. tU32 gLast_wreck_draw;
  37. tS3_sound_tag gSumm_sound;
  38. float gCredits_per_ms;
  39. tMouse_area* gBack_button_ptr;
  40. tU32 gSummary_start;
  41. br_pixelmap* gWreck_z_buffer;
  42. br_pixelmap* gWreck_render_area;
  43. int gWreck_selected;
  44. int gWreck_zoom_out;
  45. br_pixelmap* gChrome_font;
  46. int gWreck_zoom_in;
  47. int gTemp_credits;
  48. int gUser_interacted;
  49. int gWreck_count;
  50. int gRank_etc_munged;
  51. int gRank_increase;
  52. int gTemp_earned;
  53. int gTemp_rank;
  54. int gWreck_zoomed_in;
  55. int gDone_initial;
  56. int gTemp_lost;
  57.  
  58. // IDA: void __usercall MungeRankEtc(tProgram_state *pThe_state@<EAX>)
  59. void MungeRankEtc(tProgram_state* pThe_state) {
  60.     int i;
  61.     int not_done_yet;
  62.     LOG_TRACE("(%p)", pThe_state);
  63.  
  64.     not_done_yet = 0;
  65.     if (!gRank_etc_munged) {
  66.         gRank_etc_munged = 1;
  67.         gRace_list[pThe_state->current_race_index].been_there_done_that++;
  68.         pThe_state->rank -= gRank_increase;
  69.         if (pThe_state->rank <= 0) {
  70.             pThe_state->rank = 1;
  71.             for (i = 0; i < gNumber_of_races; i++) {
  72.                 if (gRace_list[i].best_rank < 2 && !gRace_list[i].been_there_done_that) {
  73.                     not_done_yet = 1;
  74.                     break;
  75.                 }
  76.             }
  77.  
  78.             pThe_state->game_completed = !not_done_yet;
  79.         }
  80.         pThe_state->credits += pThe_state->credits_earned - pThe_state->credits_lost;
  81.         if (pThe_state->credits > 999999) {
  82.             pThe_state->credits = 999999;
  83.         }
  84.         pThe_state->credits_earned = 0;
  85.         pThe_state->credits_lost = 0;
  86.         gRank_increase = 0;
  87.         if (pThe_state->credits < 0) {
  88.             pThe_state->credits = 0;
  89.         }
  90.     }
  91. }
  92.  
  93. // IDA: void __cdecl CalcRankIncrease()
  94. void CalcRankIncrease(void) {
  95.     LOG_TRACE("()");
  96.  
  97.     if (gNet_mode == eNet_mode_none) {
  98.         gRank_increase = gProgram_state.credits_earned / gProgram_state.credits_per_rank;
  99.         if (gRank_increase > 5) {
  100.             gRank_increase = 5;
  101.         }
  102.     } else {
  103.         gRank_increase = 1;
  104.     }
  105. }
  106.  
  107. // IDA: int __usercall RaceSummaryDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  108. int RaceSummaryDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  109.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  110.  
  111.     if (pTimed_out) {
  112.         pCurrent_choice = 0;
  113.     } else if (pEscaped) {
  114.         pCurrent_choice = -1;
  115.     }
  116.     return pCurrent_choice;
  117. }
  118.  
  119. // IDA: void __usercall DrawInBox(int pBox_left@<EAX>, int pText_left@<EDX>, int pTop@<EBX>, int pRight@<ECX>, int pBottom, int pColour, int pAmount)
  120. void DrawInBox(int pBox_left, int pText_left, int pTop, int pRight, int pBottom, int pColour, int pAmount) {
  121.     LOG_TRACE("(%d, %d, %d, %d, %d, %d, %d)", pBox_left, pText_left, pTop, pRight, pBottom, pColour, pAmount);
  122.  
  123.     BrPixelmapRectangleFill(gBack_screen,
  124.         pBox_left,
  125.         pTop,
  126.         pRight - pBox_left,
  127.         pBottom - pTop,
  128.         0);
  129.     if (pAmount >= 0) {
  130.         BrPixelmapTextF(gBack_screen, pText_left, pTop - (TranslationMode() ? 2 : 0), pColour, gFont_7, "%d", pAmount);
  131.     }
  132. }
  133.  
  134. // IDA: void __usercall DrawChromeNumber(int pLeft_1@<EAX>, int pLeft_2@<EDX>, int pPitch@<EBX>, int pTop@<ECX>, int pAmount)
  135. void DrawChromeNumber(int pLeft_1, int pLeft_2, int pPitch, int pTop, int pAmount) {
  136.     LOG_TRACE("(%d, %d, %d, %d, %d)", pLeft_1, pLeft_2, pPitch, pTop, pAmount);
  137.  
  138.     if (pAmount < 10) {
  139.         DRPixelmapRectangleMaskedCopy(gBack_screen,
  140.             pLeft_1,
  141.             pTop,
  142.             gChrome_font,
  143.             0,
  144.             gChrome_font->height / 10 * pAmount,
  145.             gChrome_font->width,
  146.             gChrome_font->height / 10);
  147.     } else {
  148.         DrawChromeNumber(pLeft_2, 0, 0, pTop, pAmount / 10);
  149.         DrawChromeNumber(pPitch + pLeft_2, 0, 0, pTop, pAmount % 10);
  150.     }
  151. }
  152.  
  153. // IDA: void __cdecl DrawSummaryItems()
  154. void DrawSummaryItems(void) {
  155.     LOG_TRACE("()");
  156.  
  157.     DrawInBox(
  158.         gCurrent_graf_data->summ1_credits_box_left,
  159.         gCurrent_graf_data->summ1_credits_left,
  160.         gCurrent_graf_data->summ1_earned_top,
  161.         gCurrent_graf_data->summ1_credits_right,
  162.         gCurrent_graf_data->summ1_earned_bottom,
  163.         2,
  164.         gTemp_earned);
  165.     DrawInBox(
  166.         gCurrent_graf_data->summ1_credits_box_left,
  167.         gCurrent_graf_data->summ1_credits_left,
  168.         gCurrent_graf_data->summ1_lost_top,
  169.         gCurrent_graf_data->summ1_credits_right,
  170.         gCurrent_graf_data->summ1_lost_bottom,
  171.         2,
  172.         gTemp_lost);
  173.     DrawInBox(
  174.         gCurrent_graf_data->summ1_credits_box_left,
  175.         gCurrent_graf_data->summ1_credits_left,
  176.         gCurrent_graf_data->summ1_total_top,
  177.         gCurrent_graf_data->summ1_credits_right,
  178.         gCurrent_graf_data->summ1_total_bottom,
  179.         2,
  180.         (gTemp_credits <= 0) ? 0 : ((gTemp_credits >= 999999) ? 999999 : gTemp_credits));
  181.     BrPixelmapRectangleFill(
  182.         gBack_screen,
  183.         gCurrent_graf_data->summ1_rank_inc_left,
  184.         gCurrent_graf_data->summ1_rank_top,
  185.         gCurrent_graf_data->summ1_rank_inc_right - gCurrent_graf_data->summ1_rank_inc_left,
  186.         gCurrent_graf_data->summ1_rank_bot - gCurrent_graf_data->summ1_rank_top,
  187.         0);
  188.     BrPixelmapRectangleFill(
  189.         gBack_screen,
  190.         gCurrent_graf_data->summ1_rank_total_left,
  191.         gCurrent_graf_data->summ1_rank_top,
  192.         gCurrent_graf_data->summ1_rank_total_right - gCurrent_graf_data->summ1_rank_total_left,
  193.         gCurrent_graf_data->summ1_rank_bot - gCurrent_graf_data->summ1_rank_top,
  194.         0);
  195.     DrawChromeNumber(
  196.         gCurrent_graf_data->summ1_rank_inc_c,
  197.         gCurrent_graf_data->summ1_rank_inc_l,
  198.         gCurrent_graf_data->summ1_rank_x_pitch,
  199.         gCurrent_graf_data->summ1_rank_y,
  200.         (int)(gTemp_rank_increase + .5f));
  201.     DrawChromeNumber(
  202.         gCurrent_graf_data->summ1_rank_total_c,
  203.         gCurrent_graf_data->summ1_rank_total_l,
  204.         gCurrent_graf_data->summ1_rank_x_pitch,
  205.         gCurrent_graf_data->summ1_rank_y,
  206.         gTemp_rank);
  207. }
  208.  
  209. // IDA: void __usercall RampUpRate(float *pRate@<EAX>, tU32 pTime@<EDX>)
  210. void RampUpRate(float* pRate, tU32 pTime) {
  211.     LOG_TRACE("(%p, %d)", pRate, pTime);
  212.  
  213.     if (pTime >= 6000) {
  214.         *pRate = 10.f;
  215.     } else if (pTime >= 4000) {
  216.         *pRate = 5.f;
  217.     } else if (pTime >= 3000) {
  218.         *pRate = 1.f;
  219.     } else if (pTime >= 2000) {
  220.         *pRate = 0.5f;
  221.     } else {
  222.         *pRate = 0.1f;
  223.     }
  224. }
  225.  
  226. // IDA: void __usercall DrawSummary(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  227. void DrawSummary(int pCurrent_choice, int pCurrent_mode) {
  228.     tU32 the_time;
  229.     static tU32 last_time;
  230.     static tU32 last_change_time;
  231.     int credit_delta;
  232.     float old_temp_increase;
  233.     float rank_delta;
  234.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  235.  
  236.     the_time = PDGetTotalTime();
  237.     if (the_time - gSummary_start > 3000) {
  238.         if (gTemp_earned != 0) {
  239.             ResetInterfaceTimeout();
  240.             if (gSumm_sound == 0) {
  241.                 gSumm_sound = DRS3StartSound(gEffects_outlet, 3200);
  242.             }
  243.             credit_delta = (the_time - last_time) * gCredits_per_ms;
  244.             gTemp_earned -= credit_delta;
  245.             gTemp_credits += credit_delta;
  246.             RampUpRate(&gCredits_per_ms, the_time - last_change_time + 1000);
  247.             if (gTemp_earned <= 0) {
  248.                 gTemp_credits += gTemp_earned;
  249.                 S3StopOutletSound(gEffects_outlet);
  250.                 gSumm_sound = 0;
  251.                 gTemp_earned = 0;
  252.                 gCredits_per_ms = 0.1f;
  253.                 last_change_time = the_time;
  254.             }
  255.         } else if (gTemp_lost != 0) {
  256.             ResetInterfaceTimeout();
  257.             if (the_time - last_change_time > 1000) {
  258.                 if (gSumm_sound == 0) {
  259.                     gSumm_sound = DRS3StartSound(gEffects_outlet, 3201);
  260.                 }
  261.                 credit_delta = (the_time - last_time) * gCredits_per_ms;
  262.                 gTemp_lost -= credit_delta;
  263.                 gTemp_credits -= credit_delta;
  264.                 RampUpRate(&gCredits_per_ms, the_time - last_change_time);
  265.                 if (gTemp_lost <= 0) {
  266.                     gTemp_credits -= gTemp_lost;
  267.                     S3StopOutletSound(gEffects_outlet);
  268.                     gSumm_sound = 0;
  269.                     gTemp_lost = 0;
  270.                     last_change_time = the_time;
  271.                 }
  272.             }
  273.         } else if (gTemp_rank_increase != 0.f) {
  274.             ResetInterfaceTimeout();
  275.             if (the_time - last_change_time > 1000) {
  276.                 rank_delta = (the_time - last_time) * gRank_per_ms;
  277.                 old_temp_increase = gTemp_rank_increase;
  278.                 gTemp_rank_increase -= rank_delta;
  279.                 if ((int)(old_temp_increase + 0.5f) != (int)(gTemp_rank_increase + 0.5f)) {
  280.                     if (gTemp_rank > 1) {
  281.                         gTemp_rank -= 1;
  282.                     }
  283.                     gSumm_sound = DRS3StartSound(gEffects_outlet, 3202);
  284.                 }
  285.                 if (gTemp_rank_increase <= 0.f) {
  286.                     S3StopOutletSound(gEffects_outlet);
  287.                     gSumm_sound = 0;
  288.                     gTemp_rank_increase = 0.f;
  289.                     last_change_time = the_time;
  290.                 }
  291.             }
  292.         } else {
  293.             S3StopOutletSound(gEffects_outlet);
  294.             gSumm_sound = 0;
  295.         }
  296.     } else {
  297.         last_change_time = the_time;
  298.     }
  299.     DrawSummaryItems();
  300.     last_time = the_time;
  301. }
  302.  
  303. // IDA: void __cdecl StartSummary()
  304. void StartSummary(void) {
  305.     LOG_TRACE("()");
  306.  
  307.     DrawSummaryItems();
  308.     gSummary_start = PDGetTotalTime();
  309. }
  310.  
  311. // IDA: void __cdecl SetUpTemps()
  312. void SetUpTemps(void) {
  313.     LOG_TRACE("()");
  314.  
  315.     gTemp_earned = gProgram_state.credits_earned;
  316.     gTemp_lost = gProgram_state.credits_lost;
  317.     gTemp_rank_increase = gRank_increase;
  318.     gTemp_rank = gProgram_state.rank;
  319.     gTemp_credits = gProgram_state.credits;
  320. }
  321.  
  322. // IDA: int __usercall Summ1GoAhead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  323. int Summ1GoAhead(int* pCurrent_choice, int* pCurrent_mode) {
  324.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  325.  
  326.     S3StopOutletSound(gEffects_outlet);
  327.     MungeRankEtc(&gProgram_state);
  328.     SetUpTemps();
  329.     DrawSummaryItems();
  330.     return 1;
  331. }
  332.  
  333. // IDA: int __usercall SummCheckGameOver@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  334. int SummCheckGameOver(int* pCurrent_choice, int* pCurrent_mode) {
  335.     int i;
  336.     //tS3_sound_tag sound_tag; // Pierre-Marie Baty -- unused variable
  337.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  338.  
  339.     if (gTemp_credits > 0) {
  340.         return 0;
  341.     }
  342.     S3StopOutletSound(gEffects_outlet);
  343.     RemoveTransientBitmaps(1);
  344.     for (i = 0; i < 7; i++) {
  345.         DrawInBox(
  346.             gCurrent_graf_data->summ1_credits_box_left,
  347.             gCurrent_graf_data->summ1_credits_left,
  348.             gCurrent_graf_data->summ1_total_top,
  349.             gCurrent_graf_data->summ1_credits_right,
  350.             gCurrent_graf_data->summ1_total_bottom,
  351.             2, -1);
  352.         ProcessFlicQueue(gFrame_period);
  353.         PDScreenBufferSwap(0);
  354.         SoundService();
  355.         WaitFor(300);
  356.         DrawInBox(
  357.             gCurrent_graf_data->summ1_credits_box_left,
  358.             gCurrent_graf_data->summ1_credits_left,
  359.             gCurrent_graf_data->summ1_total_top,
  360.             gCurrent_graf_data->summ1_credits_right,
  361.             gCurrent_graf_data->summ1_total_bottom,
  362.             2, 0);
  363.         ProcessFlicQueue(gFrame_period);
  364.         PDScreenBufferSwap(0);
  365.         SoundService();
  366.         WaitFor(300);
  367.     }
  368.     S3StopAllOutletSounds();
  369.     return 1;
  370. }
  371.  
  372. // IDA: tSO_result __cdecl DoEndRaceSummary1()
  373. tSO_result DoEndRaceSummary1(void) {
  374.     static tFlicette flicker_on[1] = {
  375.         { 43, { 218, 436 }, { 147, 353 } },
  376.     };
  377.     static tFlicette flicker_off[1] = {
  378.         { 42, { 218, 436 }, { 147, 353 } },
  379.     };
  380.     static tFlicette push[1] = {
  381.         { 154, { 218, 436 }, { 147, 353 } },
  382.     };
  383.     static tMouse_area mouse_areas[1] = {
  384.         { { 218, 436 }, { 147, 353 }, { 281, 562 }, { 167, 401 }, 0, 0, 0, NULL },
  385.     };
  386.     static tInterface_spec interface_spec = {
  387.         0,
  388.         310,
  389.         0,
  390.         0,
  391.         0,
  392.         0,
  393.         -1,
  394.         { -1, 0 },
  395.         { 0, 0 },
  396.         { 0, 0 },
  397.         { 0, 0 },
  398.         { NULL, NULL },
  399.         { -1, 0 },
  400.         { 0, 0 },
  401.         { 0, 0 },
  402.         { 0, 0 },
  403.         { NULL, NULL },
  404.         { -1, 0 },
  405.         { 0, 0 },
  406.         { 0, 0 },
  407.         { 0, 0 },
  408.         { NULL, NULL },
  409.         { -1, 0 },
  410.         { 1, 0 },
  411.         { 0, 0 },
  412.         { 0, 0 },
  413.         { NULL, NULL },
  414.         { 1, 1 },
  415.         { Summ1GoAhead, NULL },
  416.         { 1, 1 },
  417.         { NULL, NULL },
  418.         SummCheckGameOver,
  419.         DrawSummary,
  420.         20000,
  421.         NULL,
  422.         StartSummary,
  423.         RaceSummaryDone,
  424.         0,
  425.         { 0, 0 },
  426.         NULL,
  427.         -1,
  428.         1,
  429.         COUNT_OF(flicker_on),
  430.         flicker_on,
  431.         flicker_off,
  432.         push,
  433.         COUNT_OF(mouse_areas),
  434.         mouse_areas,
  435.         0,
  436.         NULL,
  437.     };
  438.     int result;
  439.     int completed_already;
  440.     LOG_TRACE("()");
  441.  
  442.     NetPlayerStatusChanged(ePlayer_status_summary);
  443.     completed_already = gProgram_state.game_completed;
  444.     gSumm_sound = 0;
  445.     CalcRankIncrease();
  446.     SetUpTemps();
  447.     gCredits_per_ms = 0.1f;
  448.     gRank_per_ms = 0.003f;
  449.     gChrome_font = LoadChromeFont();
  450.     result = DoInterfaceScreen(&interface_spec, 0, 0);
  451.     MungeRankEtc(&gProgram_state);
  452.     NetPlayerStatusChanged(ePlayer_status_loading);
  453.     DisposeChromeFont(gChrome_font);
  454.     DRS3StartSound(gEffects_outlet, 3004);
  455.     if (result < 0) {
  456.         DRS3StartSound(gEffects_outlet, 3007);
  457.         RunFlic(311);
  458.         result = eSO_main_menu_invoked;
  459.     } else if (gTemp_credits <= 0) {
  460.         FadePaletteDown();
  461.         result = eSO_game_over;
  462.     } else if (gProgram_state.game_completed && !completed_already) {
  463.         FadePaletteDown();
  464.         result = eSO_game_completed;
  465.     } else {
  466.         DRS3StartSound(gEffects_outlet, 3007);
  467.         FadePaletteDown();
  468.         result = eSO_continue;
  469.     }
  470.  
  471.     return result;
  472. }
  473.  
  474. // IDA: void __usercall PrepareBoundingRadius(br_model *model@<EAX>)
  475. //  Suffix added to avoid duplicate symbol
  476. void PrepareBoundingRadius__racesumm(br_model* model) {
  477.     float d;
  478.     float max;
  479.     int v;
  480.     br_vertex* vp;
  481.     LOG_TRACE("(%p)", model);
  482.  
  483.     max = 0.f;
  484.     for (v = 0; v < model->nvertices; v++) {
  485.         vp = &model->vertices[v];
  486.         d = BrVector3LengthSquared(&vp->p);
  487.         if (d > max) {
  488.             max = d;
  489.         }
  490.     }
  491.     d = sqrtf(max);
  492.     model->radius = d;
  493. }
  494.  
  495. // IDA: void __cdecl BuildWrecks()
  496. void BuildWrecks(void) {
  497.     int cat;
  498.     int i;
  499.     int position;
  500.     int car_count;
  501.     br_actor* this_car;
  502.     tCar_spec* the_car;
  503.     LOG_TRACE("()");
  504.  
  505.     gWreck_count = 0;
  506.     position = 0;
  507.     gWreck_root = BrActorAllocate(BR_ACTOR_NONE, NULL);
  508.     gWreck_camera = BrActorAllocate(BR_ACTOR_CAMERA, NULL);
  509.     BrActorAdd(gUniverse_actor, gWreck_root);
  510.     BrActorAdd(gWreck_root, gWreck_camera);
  511.     memcpy(gWreck_camera->type_data, gCamera_list[1]->type_data, sizeof(br_camera));
  512.     ((br_camera*)gWreck_camera->type_data)->aspect = 2.f;
  513.     ((br_camera*)gWreck_camera->type_data)->field_of_view = BR_ANGLE_DEG(55);
  514.     BrMatrix34Identity(&gWreck_camera->t.t.mat);
  515.     BrVector3SetFloat(&gWreck_camera->t.t.translate.t, 0.f, 0.f, 2.2f);
  516.     for (cat = eVehicle_self; cat < eVehicle_rozzer; cat++) {
  517.         if (cat == eVehicle_self) {
  518.             car_count = 1;
  519.         } else {
  520.             car_count = GetCarCount(cat);
  521.         }
  522.         for (i = 0; i < car_count; i++) {
  523.             if (cat == eVehicle_self) {
  524.                 the_car = &gProgram_state.current_car;
  525.             } else {
  526.                 the_car = GetCarSpec(cat, i);
  527.             }
  528.             this_car = the_car->car_model_actors[the_car->principal_car_actor].actor;
  529.             memcpy(&gWreck_array[gWreck_count].original_matrix, &this_car->t.t.mat, sizeof(br_matrix34));
  530.             BrActorRelink(gWreck_root, this_car);
  531.             this_car->render_style = BR_RSTYLE_FACES;
  532.             gWreck_array[gWreck_count].customised = 0;
  533.             gWreck_array[gWreck_count].actor = this_car;
  534.             PrepareBoundingRadius__racesumm(this_car->model);
  535.             gWreck_array[gWreck_count].scaling_factor = .47f / this_car->model->radius;
  536.             gWreck_array[gWreck_count].pos_x = (position % 3) - 1.0f;
  537.             gWreck_array[gWreck_count].pos_y = (position / 3) - 0.5f;
  538.             this_car->t.t.translate.t.v[0] = 1.5f * gWreck_array[gWreck_count].pos_x;
  539.             this_car->t.t.translate.t.v[1] = -1.2f * gWreck_array[gWreck_count].pos_y;
  540.             this_car->t.t.translate.t.v[2] = 0.f;
  541.             gWreck_array[gWreck_count].car_type = cat;
  542.             gWreck_array[gWreck_count].car_index = i;
  543.             gWreck_count += 1;
  544.             position += 1;
  545.         }
  546.     }
  547.     gWreck_render_area = BrPixelmapAllocateSub(
  548.         gBack_screen,
  549.         gCurrent_graf_data->wreck_render_x,
  550.         gCurrent_graf_data->wreck_render_y,
  551.         gCurrent_graf_data->wreck_render_w,
  552.         gCurrent_graf_data->wreck_render_h);
  553.     gWreck_render_area->origin_x = gWreck_render_area->width / 2;
  554.     gWreck_render_area->origin_y = gWreck_render_area->height / 2;
  555.     gWreck_z_buffer = BrPixelmapAllocateSub(
  556.         gDepth_buffer,
  557.         gCurrent_graf_data->wreck_render_x,
  558.         gCurrent_graf_data->wreck_render_y,
  559.         gCurrent_graf_data->wreck_render_w,
  560.         gCurrent_graf_data->wreck_render_h);
  561. }
  562.  
  563. // IDA: void __cdecl DisposeWrecks()
  564. void DisposeWrecks(void) {
  565.     int cat;
  566.     int i;
  567.     //int position; // Pierre-Marie Baty -- unused variable
  568.     int car_count;
  569.     br_actor* this_car;
  570.     tCar_spec* the_car;
  571.     LOG_TRACE("()");
  572.  
  573.     for (cat = eVehicle_self; cat < eVehicle_rozzer; cat++) {
  574.         if (cat == eVehicle_self) {
  575.             car_count = 1;
  576.         } else {
  577.             car_count = GetCarCount(cat);
  578.         }
  579.         for (i = 0; i < car_count; i++) {
  580.             if (cat == eVehicle_self) {
  581.                 the_car = &gProgram_state.current_car;
  582.             } else {
  583.                 the_car = GetCarSpec(cat, i);
  584.             }
  585.             this_car = the_car->car_model_actors[the_car->principal_car_actor].actor;
  586.             BrActorRelink(the_car->car_master_actor, this_car);
  587.             this_car->render_style = BR_RSTYLE_NONE;
  588.         }
  589.     }
  590.     for (i = 0; i < gWreck_count; i++) {
  591.         memcpy(&gWreck_array[i].actor->t.t.mat, &gWreck_array[i].original_matrix, sizeof(br_matrix34));
  592.     }
  593.     BrActorRemove(gWreck_root);
  594.     BrActorRemove(gWreck_camera);
  595.     BrActorFree(gWreck_root);
  596.     BrActorFree(gWreck_camera);
  597.     gWreck_render_area->pixels = NULL;
  598.     gWreck_z_buffer->pixels = NULL;
  599.     BrPixelmapFree(gWreck_render_area);
  600.     BrPixelmapFree(gWreck_z_buffer);
  601. }
  602.  
  603. // IDA: int __usercall MatrixIsIdentity@<EAX>(br_matrix34 *pMat@<EAX>)
  604. int MatrixIsIdentity(br_matrix34* pMat) {
  605.     LOG_TRACE("(%p)", pMat);
  606.  
  607.     return (pMat->m[0][0] == 1.f && pMat->m[1][1] == 1.f && pMat->m[2][2] == 1.f && pMat->m[0][1] == 0.f && pMat->m[0][2] == 0.f && pMat->m[1][0] == 0.f && pMat->m[1][2] == 0.f && pMat->m[2][0] == 0.f && pMat->m[2][1] == 0.f);
  608. }
  609.  
  610. // IDA: void __usercall SpinWrecks(tU32 pFrame_period@<EAX>)
  611. void SpinWrecks(tU32 pFrame_period) {
  612.     int i;
  613.     br_vector3 translation;
  614.     br_matrix34 old_mat;
  615.     LOG_TRACE("(%d)", pFrame_period);
  616.  
  617.     for (i = 0; i < gWreck_count; i++) {
  618.         if (gWreck_array[i].customised == 0) {
  619.             BrMatrix34RotateY(&gWreck_array[i].rotation, BR_ANGLE_DEG(.05f * pFrame_period));
  620.         }
  621.         BrVector3Copy(&translation, &gWreck_array[i].actor->t.t.translate.t);
  622.         BrVector3Set(&gWreck_array[i].actor->t.t.translate.t, 0.f, 0.f, 0.f);
  623.         BrMatrix34Post(&gWreck_array[i].actor->t.t.mat, &gWreck_array[i].rotation);
  624.         if (!MatrixIsIdentity(&gWreck_array[i].actor->t.t.mat)) {
  625.             memcpy(&old_mat, &gWreck_array[i].actor->t.t.mat, sizeof(br_matrix34));
  626.             BrMatrix34LPNormalise(&gWreck_array[i].actor->t.t.mat, &old_mat);
  627.             BrMatrix34PostScale(&gWreck_array[i].actor->t.t.mat,
  628.                 gWreck_array[i].scaling_factor, gWreck_array[i].scaling_factor, gWreck_array[i].scaling_factor);
  629.         }
  630.         BrVector3Copy(&gWreck_array[i].actor->t.t.translate.t, &translation);
  631.     }
  632. }
  633.  
  634. // IDA: void __usercall ZoomInTo(int pIndex@<EAX>, int *pCurrent_choice@<EDX>, int *pCurrent_mode@<EBX>)
  635. void ZoomInTo(int pIndex, int* pCurrent_choice, int* pCurrent_mode) {
  636.     LOG_TRACE("(%d, %p, %p)", pIndex, pCurrent_choice, pCurrent_mode);
  637.  
  638.     gWreck_zoom_in = pIndex;
  639.     gWreck_zoom_out = -1;
  640.     gWreck_start_zoom = PDGetTotalTime();
  641.     if (gWreck_selected == 0 && !gUser_interacted) {
  642.         *pCurrent_choice = 2;
  643.         *pCurrent_mode = 1;
  644.     } else {
  645.         *pCurrent_choice = 0;
  646.         *pCurrent_mode = 0;
  647.     }
  648.     RemoveTransientBitmaps(1);
  649.     TurnFlicTransparencyOn();
  650.     RunFlicAt(325, 9, 174);
  651.     TurnFlicTransparencyOff();
  652.     gBack_button_ptr->new_choice = -1;
  653.     gBack_button_ptr->new_mode = -1;
  654. }
  655.  
  656. // IDA: void __usercall ZoomOutTo(int pIndex@<EAX>, int *pCurrent_choice@<EDX>, int *pCurrent_mode@<EBX>)
  657. void ZoomOutTo(int pIndex, int* pCurrent_choice, int* pCurrent_mode) {
  658.     LOG_TRACE("(%d, %p, %p)", pIndex, pCurrent_choice, pCurrent_mode);
  659.  
  660.     gWreck_zoom_out = pIndex;
  661.     gWreck_zoom_in = -1;
  662.     gWreck_start_zoom = PDGetTotalTime();
  663.     RemoveTransientBitmaps(1);
  664.     TurnFlicTransparencyOn();
  665.     RunFlicAt(326, 9, 174);
  666.     TurnFlicTransparencyOff();
  667.     memcpy(gBack_button_ptr, &gOld_back_button, sizeof(tMouse_area));
  668.     *pCurrent_choice = 1;
  669.     *pCurrent_mode = 1;
  670. }
  671.  
  672. // IDA: int __cdecl WreckPick(br_actor *pActor, br_model *pModel, br_material *pMaterial, br_vector3 *pRay_pos, br_vector3 *pRay_dir, br_scalar pNear, br_scalar pFar, void *pArg)
  673. int WreckPick(br_actor* pActor, br_model* pModel, br_material* pMaterial, br_vector3* pRay_pos, br_vector3* pRay_dir, br_scalar pNear, br_scalar pFar, void* pArg) {
  674.     int i;
  675.     LOG_TRACE("(%p, %p, %p, %p, %p, %f, %f, %p)", pActor, pModel, pMaterial, pRay_pos, pRay_dir, pNear, pFar, pArg);
  676.  
  677.     for (i = 0; i < gWreck_count; i++) {
  678.         if (gWreck_array[i].actor == pActor) {
  679.             gWreck_selected = i;
  680.             return 1;
  681.         }
  682.     }
  683.     return 0;
  684. }
  685.  
  686. // IDA: int __usercall CastSelectionRay@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  687. int CastSelectionRay(int* pCurrent_choice, int* pCurrent_mode) {
  688.     int mouse_x;
  689.     int mouse_y;
  690.     int i;
  691.     int result;
  692.     br_scalar inv_wreck_pick_scale_factor;
  693.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  694.  
  695.     if (!gMouse_in_use) {
  696.         return 0;
  697.     }
  698.     if (*pCurrent_choice != 0) {
  699.         return 0;
  700.     }
  701.     if (*pCurrent_mode != 0) {
  702.         return 0;
  703.     }
  704.     if (gWreck_zoomed_in >= 0) {
  705.         return 0;
  706.     }
  707.     if (gWreck_zoom_out >= 0) {
  708.         return 0;
  709.     }
  710.     if (gWreck_zoom_in >= 0) {
  711.         return 0;
  712.     }
  713.  
  714.     inv_wreck_pick_scale_factor = 0.5f;
  715.     GetMousePosition(&mouse_x, &mouse_y);
  716.     if (gReal_graf_data_index != 0) {
  717.         mouse_x = 2 * mouse_x;
  718.         mouse_y = 2 * mouse_y + 40;
  719.     }
  720.     for (i = 0; i < gWreck_count; i++) {
  721.         BrMatrix34PreScale(&gWreck_array[i].actor->t.t.mat, 2.f, 2.f, 2.f);
  722.     }
  723.     result = DRScenePick2DXY(gWreck_root, gWreck_camera, gRender_screen,
  724.         mouse_x - gBack_screen->width / 2,
  725.         mouse_y - gBack_screen->height / 2,
  726.         WreckPick,
  727.         NULL);
  728.     for (i = 0; i < gWreck_count; i++) {
  729.         BrMatrix34PreScale(&gWreck_array[i].actor->t.t.mat,
  730.             inv_wreck_pick_scale_factor, inv_wreck_pick_scale_factor, inv_wreck_pick_scale_factor);
  731.     }
  732.     return result;
  733. }
  734.  
  735. // IDA: int __usercall DamageScrnExit@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  736. int DamageScrnExit(int* pCurrent_choice, int* pCurrent_mode) {
  737.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  738.  
  739.     if (gProgram_state.prog_status == eProg_idling) {
  740.         return 1;
  741.     } else {
  742.         if (gWreck_gallery_start == 0) {
  743.             gWreck_gallery_start = PDGetTotalTime();
  744.         } else if (!gDone_initial && gWreck_selected == 0) {
  745.             if (PDGetTotalTime() - gWreck_gallery_start > 1500) {
  746.                 ZoomOutTo(gWreck_selected, pCurrent_choice, pCurrent_mode);
  747.                 gDone_initial = 1;
  748.             }
  749.         }
  750.         CastSelectionRay(pCurrent_choice, pCurrent_mode);
  751.         return 0;
  752.     }
  753. }
  754.  
  755. // IDA: void __usercall DamageScrnDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  756. void DamageScrnDraw(int pCurrent_choice, int pCurrent_mode) {
  757.     tU32 the_time;
  758.     br_vector3 camera_movement;
  759.     int finished;
  760.     int h;
  761.     int v;
  762.     int rows;
  763.     int columns;
  764.     //float spacing; // Pierre-Marie Baty -- unused variable
  765.     br_actor* sel_actor;
  766.     char* name;
  767.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  768.  
  769.     if (((pCurrent_choice == 0 && pCurrent_mode == 0) || !gDone_initial) && (gWreck_zoomed_in < 0 && gWreck_selected >= 0)) {
  770.         sel_actor = BrActorAllocate(BR_ACTOR_MODEL, NULL);
  771.         sel_actor->render_style = BR_RSTYLE_BOUNDING_EDGES;
  772.         sel_actor->model = gWreck_array[gWreck_selected].actor->model;
  773.         BrActorAdd(gWreck_array[gWreck_selected].actor, sel_actor);
  774.     } else {
  775.         sel_actor = NULL;
  776.     }
  777.     the_time = PDGetTotalTime();
  778.     SpinWrecks(the_time - gLast_wreck_draw);
  779.     gLast_wreck_draw = the_time;
  780.     if (gWreck_zoom_out >= 0 || gWreck_zoom_in >= 0) {
  781.         if (gWreck_start_zoom == 0) {
  782.             gWreck_start_zoom = the_time;
  783.         }
  784.         finished = the_time - gWreck_start_zoom > 1000;
  785.         if (finished) {
  786.             the_time = gWreck_start_zoom + 1000;
  787.         }
  788.         if (gWreck_zoom_out < 0) {
  789.             camera_movement.v[0] = (1.f - (the_time - gWreck_start_zoom) / 1000.f) * gWreck_array[gWreck_zoom_in].actor->t.t.translate.t.v[0];
  790.             camera_movement.v[1] = (1.f - (the_time - gWreck_start_zoom) / 1000.f) * gWreck_array[gWreck_zoom_in].actor->t.t.translate.t.v[1];
  791.             camera_movement.v[2] = (1.f - (the_time - gWreck_start_zoom) / 1000.f) * -1.45f;
  792.             if (finished) {
  793.                 gWreck_zoom_in = -1;
  794.                 gWreck_zoomed_in = -1;
  795.             }
  796.         } else {
  797.             camera_movement.v[0] = (the_time - gWreck_start_zoom) / 1000.f * gWreck_array[gWreck_zoom_out].actor->t.t.translate.t.v[0];
  798.             camera_movement.v[1] = (the_time - gWreck_start_zoom) / 1000.f * gWreck_array[gWreck_zoom_out].actor->t.t.translate.t.v[1];
  799.             camera_movement.v[2] = (the_time - gWreck_start_zoom) / 1000.f * -1.45f;
  800.             if (finished) {
  801.                 gWreck_zoomed_in = gWreck_zoom_out;
  802.                 gWreck_zoom_out = -1;
  803.             }
  804.         }
  805.         gWreck_camera->t.t.translate.t.v[0] = camera_movement.v[0];
  806.         gWreck_camera->t.t.translate.t.v[1] = camera_movement.v[1];
  807.         gWreck_camera->t.t.translate.t.v[2] = camera_movement.v[2] + 2.2f;
  808.     }
  809.     EnsureRenderPalette();
  810.     EnsurePaletteUp();
  811.     BrPixelmapFill(gWreck_z_buffer, 0xffffffff);
  812.     BrPixelmapFill(gWreck_render_area, BR_COLOUR_RGBA(0xb0, 0xb0, 0xb0, 0xb0));
  813.  
  814.     rows = gWreck_render_area->height / 15.f;
  815.     columns = gWreck_render_area->width / 15.f;
  816.     for (v = 0; v <= rows; v++) {
  817.         BrPixelmapLine(gWreck_render_area,
  818.             -gWreck_render_area->origin_x,
  819.             gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
  820.             gWreck_render_area->width - gWreck_render_area->origin_x,
  821.             gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
  822.             8);
  823.     }
  824.     for (h = 0; h <= columns; h++) {
  825.         BrPixelmapLine(gWreck_render_area,
  826.             gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
  827.             -gWreck_render_area->origin_y,
  828.             gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
  829.             gWreck_render_area->height - gWreck_render_area->origin_y,
  830.             8);
  831.     }
  832.     BrZbSceneRenderBegin(gUniverse_actor, gWreck_camera, gWreck_render_area, gWreck_z_buffer);
  833.     BrZbSceneRenderAdd(gWreck_root);
  834.     BrZbSceneRenderEnd();
  835.     if (sel_actor != NULL) {
  836.         BrActorRemove(sel_actor);
  837.         sel_actor->model = NULL;
  838.         BrActorFree(sel_actor);
  839.     }
  840.     BrPixelmapRectangleFill(gBack_screen,
  841.         gCurrent_graf_data->wreck_name_left,
  842.         gCurrent_graf_data->wreck_name_top,
  843.         gCurrent_graf_data->wreck_name_right - gCurrent_graf_data->wreck_name_left,
  844.         gCurrent_graf_data->wreck_name_bottom - gCurrent_graf_data->wreck_name_top,
  845.         0);
  846.     if (gWreck_selected >= 0 && (gWreck_zoomed_in >= 0 || pCurrent_mode == 0)) {
  847.         name = GetDriverName(gWreck_array[gWreck_selected].car_type,
  848.             gWreck_array[gWreck_selected].car_index);
  849.         TransBrPixelmapText(gBack_screen,
  850.             (gCurrent_graf_data->wreck_name_left + gCurrent_graf_data->wreck_name_right - BrPixelmapTextWidth(gBack_screen, gFont_7, name)) / 2,
  851.             gCurrent_graf_data->wreck_name_base_line,
  852.             84,
  853.             gFont_7,
  854.             name);
  855.     }
  856. }
  857.  
  858. // IDA: int __usercall DamageScrnLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  859. int DamageScrnLeft(int* pCurrent_choice, int* pCurrent_mode) {
  860.     int i;
  861.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  862.  
  863.     gDone_initial = 1;
  864.     DRS3StartSound(gEffects_outlet, 3000);
  865.     if (*pCurrent_mode == 0 && gWreck_zoomed_in < 0) {
  866.         if (gWreck_selected < 0) {
  867.             gWreck_selected = gWreck_count - 1;
  868.         } else if (gWreck_selected != 0 && gWreck_array[gWreck_selected - 1].pos_y == gWreck_array[gWreck_selected].pos_y) {
  869.             gWreck_selected--;
  870.         } else {
  871.             for (i = gWreck_count - 1; i >= 0; i--) {
  872.                 if (gWreck_array[i].pos_y == gWreck_array[gWreck_selected].pos_y) {
  873.                     gWreck_selected = i;
  874.                     break;
  875.                 }
  876.             }
  877.         }
  878.     } else if (gWreck_zoomed_in >= 0) {
  879.         *pCurrent_choice = *pCurrent_choice + 1;
  880.         if (*pCurrent_choice == 3) {
  881.             *pCurrent_choice = 1;
  882.         }
  883.     }
  884.     return 1;
  885. }
  886.  
  887. // IDA: int __usercall DamageScrnRight@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  888. int DamageScrnRight(int* pCurrent_choice, int* pCurrent_mode) {
  889.     int i;
  890.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  891.  
  892.     gDone_initial = 1;
  893.     DRS3StartSound(gEffects_outlet, 3000);
  894.     if (*pCurrent_mode == 0 && gWreck_zoomed_in < 0) {
  895.         if (gWreck_selected < 0) {
  896.             gWreck_selected = 0;
  897.         } else if (gWreck_selected - 1 != gWreck_count && gWreck_array[gWreck_selected + 1].pos_y == gWreck_array[gWreck_selected].pos_y) {
  898.             gWreck_selected++;
  899.         } else {
  900.             for (i = 0; i < gWreck_count; i++) {
  901.                 if (gWreck_array[i].pos_y == gWreck_array[gWreck_selected].pos_y) {
  902.                     gWreck_selected = i;
  903.                     break;
  904.                 }
  905.             }
  906.         }
  907.     } else if (gWreck_zoomed_in >= 0) {
  908.         *pCurrent_choice = *pCurrent_choice + 1;
  909.         if (*pCurrent_choice == 3) {
  910.             *pCurrent_choice = 1;
  911.         }
  912.     }
  913.     return 1;
  914. }
  915.  
  916. // IDA: int __usercall DamageScrnUp@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  917. int DamageScrnUp(int* pCurrent_choice, int* pCurrent_mode) {
  918.     int i;
  919.     int difference;
  920.     int new_difference;
  921.     int new_selection;
  922.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  923.  
  924.     gDone_initial = 1;
  925.     DRS3StartSound(gEffects_outlet, 3000);
  926.     if (*pCurrent_mode != 0) {
  927.         *pCurrent_mode = 0;
  928.         *pCurrent_choice = 0;
  929.         if (gWreck_zoomed_in < 0) {
  930.             gWreck_selected = gWreck_count + -1;
  931.         }
  932.     } else {
  933.         if (gWreck_selected < 0) {
  934.             if (gWreck_zoomed_in < 0) {
  935.                 gWreck_selected = gWreck_count - 1;
  936.             }
  937.         } else if (gWreck_zoomed_in >= 0) {
  938.             *pCurrent_mode = 1;
  939.             *pCurrent_choice = 1;
  940.         } else if (gWreck_array[gWreck_selected].pos_y == gWreck_array[0].pos_y) {
  941.             *pCurrent_mode = 1;
  942.             *pCurrent_choice = 2;
  943.         } else {
  944.             new_difference = 1000;
  945.             new_selection = gWreck_selected;
  946.             for (i = 0; i < gWreck_count; i++) {
  947.                 if (gWreck_array[gWreck_selected].pos_y - 1.f == gWreck_array[i].pos_y) {
  948.                     if (gWreck_array[i].pos_x == gWreck_array[gWreck_selected].pos_x) {
  949.                         new_selection = i;
  950.                         break;
  951.                     }
  952.                     difference = abs((int)(gWreck_array[i].pos_x - gWreck_array[gWreck_selected].pos_x));
  953.                     if (difference < new_difference) {
  954.                         new_selection = i;
  955.                         new_difference = difference;
  956.                     }
  957.                 }
  958.             }
  959.             gWreck_selected = new_selection;
  960.         }
  961.     }
  962.     return 1;
  963. }
  964.  
  965. // IDA: int __usercall DamageScrnDown@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  966. int DamageScrnDown(int* pCurrent_choice, int* pCurrent_mode) {
  967.     int i;
  968.     int difference;
  969.     int new_difference;
  970.     int new_selection;
  971.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  972.  
  973.     gDone_initial = 1;
  974.     DRS3StartSound(gEffects_outlet, 3000);
  975.     if (*pCurrent_mode != 0) {
  976.         *pCurrent_mode = 0;
  977.         *pCurrent_choice = 0;
  978.         if (gWreck_zoomed_in < 0) {
  979.             for (i = gWreck_count - 1; i >= 0; i--) {
  980.                 if (gWreck_array[i].pos_y == gWreck_array[0].pos_y) {
  981.                     gWreck_selected = i;
  982.                     break;
  983.                 }
  984.             }
  985.         }
  986.     } else {
  987.         if (gWreck_selected < 0) {
  988.             gWreck_selected = 0;
  989.         } else if (gWreck_zoomed_in >= 0) {
  990.             *pCurrent_mode = 1;
  991.             *pCurrent_choice = 1;
  992.         } else if (gWreck_array[gWreck_selected].pos_y == gWreck_array[gWreck_count - 1].pos_y) {
  993.             *pCurrent_mode = 1;
  994.             *pCurrent_choice = 2;
  995.         } else {
  996.             new_difference = 1000;
  997.             new_selection = gWreck_selected;
  998.             for (i = 0; i < gWreck_count; i++) {
  999.                 if (gWreck_array[gWreck_selected].pos_y + 1.f == gWreck_array[i].pos_y) {
  1000.                     if (gWreck_array[i].pos_x == gWreck_array[gWreck_selected].pos_x) {
  1001.                         new_selection = i;
  1002.                         break;
  1003.                     }
  1004.                     difference = abs((int)(gWreck_array[i].pos_x - gWreck_array[gWreck_selected].pos_x));
  1005.                     if (difference < new_difference) {
  1006.                         new_selection = i;
  1007.                         new_difference = difference;
  1008.                     }
  1009.                 }
  1010.             }
  1011.             gWreck_selected = new_selection;
  1012.         }
  1013.     }
  1014.     return 1;
  1015. }
  1016.  
  1017. // IDA: int __usercall DamageScrnGoHead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1018. int DamageScrnGoHead(int* pCurrent_choice, int* pCurrent_mode) {
  1019.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1020.  
  1021.     gDone_initial = 1;
  1022.     if (*pCurrent_choice == 2) {
  1023.         return 1;
  1024.     } else if (gWreck_zoomed_in < 0) {
  1025.         if (*pCurrent_choice == 0 && gWreck_selected >= 0) {
  1026.             ZoomOutTo(gWreck_selected, pCurrent_choice, pCurrent_mode);
  1027.             gUser_interacted = 1;
  1028.         }
  1029.     } else if (*pCurrent_choice == 1) {
  1030.         ZoomInTo(gWreck_selected, pCurrent_choice, pCurrent_mode);
  1031.     }
  1032.     gDone_initial = 1;
  1033.     return 0;
  1034. }
  1035.  
  1036. // IDA: int __usercall ClickDamage@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  1037. int ClickDamage(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  1038.     int mouse_x;
  1039.     int mouse_y;
  1040.     int old_mouse_x;
  1041.     int old_mouse_y;
  1042.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  1043.  
  1044. #if defined(DETHRACE_FIX_BUGS)
  1045.     old_mouse_x = 0; // Fixes warning caused by -Wsometimes-uninitialized
  1046.     old_mouse_y = 0; // Fixes warning caused by -Wsometimes-uninitialized
  1047. #endif
  1048.     GetMousePosition(&old_mouse_y, &old_mouse_y);
  1049.     if (gWreck_zoomed_in < 0) {
  1050.         if (CastSelectionRay(pCurrent_choice, pCurrent_mode)) {
  1051.             gUser_interacted = 1;
  1052.             return 1;
  1053.         } else {
  1054.             return 0;
  1055.         }
  1056.     } else {
  1057.         while (1) {
  1058.             GetMousePosition(&mouse_x, &mouse_y);
  1059.             BrMatrix34RollingBall(&gWreck_array[gWreck_zoomed_in].rotation, mouse_x - old_mouse_x, old_mouse_y - mouse_y, 50);
  1060.             old_mouse_x = mouse_x;
  1061.             old_mouse_y = mouse_y;
  1062.             gWreck_array[gWreck_zoomed_in].customised = 1;
  1063.             RemoveTransientBitmaps(1);
  1064.             DamageScrnDraw(0, 0);
  1065.             ProcessFlicQueue(gFrame_period);
  1066.             DoMouseCursor();
  1067.             PDScreenBufferSwap(0);
  1068.             ServiceGame();
  1069.             if (!EitherMouseButtonDown()) {
  1070.                 break;
  1071.             }
  1072.         }
  1073.         return 0;
  1074.     }
  1075. }
  1076.  
  1077. // IDA: int __usercall DamageScrnDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  1078. int DamageScrnDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  1079.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  1080.  
  1081.     if (pTimed_out) {
  1082.         pCurrent_choice = 2;
  1083.     }
  1084.     return pCurrent_choice;
  1085. }
  1086.  
  1087. // IDA: tSO_result __cdecl DoEndRaceSummary2()
  1088. tSO_result DoEndRaceSummary2(void) {
  1089.     static tFlicette flicker_on[3] = {
  1090.         { -1, { 0, 0 }, { 0, 0 } },
  1091.         { 321, { 9, 18 }, { 174, 418 } },
  1092.         { 321, { 247, 494 }, { 174, 418 } },
  1093.     };
  1094.     static tFlicette flicker_off[3] = {
  1095.         { -1, { 0, 0 }, { 0, 0 } },
  1096.         { 322, { 9, 18 }, { 174, 418 } },
  1097.         { 322, { 247, 494 }, { 174, 418 } },
  1098.     };
  1099.     static tFlicette push[3] = {
  1100.         { -1, { 0, 0 }, { 0, 0 } },
  1101.         { 324, { 9, 18 }, { 174, 418 } },
  1102.         { 323, { 247, 494 }, { 174, 418 } },
  1103.     };
  1104.     static tMouse_area mouse_areas[3] = {
  1105.         { { 11, 22 }, { 20, 48 }, { 309, 618 }, { 169, 406 }, 0, 0, 0, ClickDamage },
  1106.         { { 9, 18 }, { 174, 418 }, { 72, 144 }, { 194, 466 }, 1, 1, 0, NULL },
  1107.         { { 247, 494 }, { 174, 418 }, { 310, 620 }, { 194, 466 }, 2, 1, 0, NULL },
  1108.     };
  1109.     static tInterface_spec interface_spec = {
  1110.         1, 320, 0, -1, -1, -1, -1,
  1111.         { -1, -1 }, { 0, 0 }, { 0, 0 }, { 0, 2 }, { DamageScrnLeft, DamageScrnLeft },
  1112.         { -1, -1 }, { 0, 0 }, { 0, 0 }, { 0, 2 }, { DamageScrnRight, DamageScrnRight },
  1113.         { 1, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 }, { DamageScrnUp, DamageScrnUp },
  1114.         { 1, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 }, { DamageScrnDown, DamageScrnDown },
  1115.         { 1, 1 }, { DamageScrnGoHead, DamageScrnGoHead },
  1116.         { 1, 1 }, { NULL, NULL },
  1117.         DamageScrnExit, DamageScrnDraw,
  1118.         20000, NULL, NULL, DamageScrnDone, 0,
  1119.         { 0, 0 }, NULL, 2, 1,
  1120.         COUNT_OF(flicker_on), flicker_on, flicker_off, push,
  1121.         COUNT_OF(mouse_areas), mouse_areas,
  1122.         0, NULL
  1123.     };
  1124.     int result;
  1125.     LOG_TRACE("()");
  1126.  
  1127.     if (gAusterity_mode) {
  1128.         return eSO_continue;
  1129.     }
  1130.     NetPlayerStatusChanged(ePlayer_status_wrecks_gallery);
  1131.     gBack_button_ptr = &mouse_areas[1];
  1132.     memcpy(&gOld_back_button, &mouse_areas[1], sizeof(tMouse_area));
  1133.     gOld_back_button.new_choice = 1;
  1134.     gOld_back_button.new_mode = 1;
  1135.     memcpy(gBack_button_ptr, &gOld_back_button, sizeof(tMouse_area));
  1136.     BuildWrecks();
  1137.     gWreck_zoom_out = -1;
  1138.     gWreck_zoom_in = -1;
  1139.     gWreck_start_zoom = 0;
  1140.     gWreck_zoomed_in = -1;
  1141.     gWreck_selected = 0;
  1142.     gUser_interacted = 0;
  1143.     gDone_initial = 0;
  1144.     TurnOnPaletteConversion();
  1145.     gWreck_gallery_start = 0;
  1146.     result = DoInterfaceScreen(&interface_spec, 0, 2);
  1147.     NetPlayerStatusChanged(ePlayer_status_loading);
  1148.     TurnOffPaletteConversion();
  1149.     DisposeWrecks();
  1150.     if (result < 0) {
  1151.         return eSO_main_menu_invoked;
  1152.     }
  1153.     return eSO_continue;
  1154. }
  1155.  
  1156. // IDA: void __usercall DrawAnItem(int pX@<EAX>, int pY_index@<EDX>, int pFont_index@<EBX>, char *pText@<ECX>)
  1157. //  Suffix added to avoid duplicate symbol
  1158. void DrawAnItem__racesumm(int pX, int pY_index, int pFont_index, char* pText) {
  1159.     LOG_TRACE("(%d, %d, %d, \"%s\")", pX, pY_index, pFont_index, pText);
  1160.  
  1161.     TransBrPixelmapText(gBack_screen,
  1162.         pX,
  1163.         gCurrent_graf_data->net_sum_headings_y + gCurrent_graf_data->net_sum_y_pitch * pY_index,
  1164.         pFont_index,
  1165.         gFont_7,
  1166.         pText);
  1167. }
  1168.  
  1169. // IDA: void __usercall DrawColumnHeading(int pStr_index@<EAX>, int pX@<EDX>)
  1170. //  Suffix added to avoid duplicate symbol
  1171. void DrawColumnHeading__racesumm(int pStr_index, int pX) {
  1172.     LOG_TRACE("(%d, %d)", pStr_index, pX);
  1173.  
  1174.     TransBrPixelmapText(gBack_screen,
  1175.         pX,
  1176.         gCurrent_graf_data->net_sum_headings_y - gCurrent_graf_data->net_sum_y_pitch,
  1177.         250,
  1178.         gFont_7,
  1179.         GetMiscString(pStr_index));
  1180. }
  1181.  
  1182. // IDA: int __usercall SortScores@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
  1183. int SortScores(const void* pFirst_one, const void* pSecond_one) {
  1184.     LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
  1185.  
  1186.     return gNet_players[*(int*)pSecond_one].games_score - gNet_players[*(int*)pFirst_one].games_score;
  1187. }
  1188.  
  1189. // IDA: void __cdecl SortGameScores()
  1190. void SortGameScores(void) {
  1191.     LOG_TRACE("()");
  1192.     qsort(gPlayer_lookup, gNumber_of_net_players, sizeof(gPlayer_lookup[0]), SortScores);
  1193. }
  1194.  
  1195. // IDA: void __usercall NetSumDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  1196. void NetSumDraw(int pCurrent_choice, int pCurrent_mode) {
  1197.     int i;
  1198.     char s[256];
  1199.     tNet_game_player_info* player;
  1200.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  1201.  
  1202.     DrawColumnHeading__racesumm(kMiscString_PLAYED, gCurrent_graf_data->net_sum_x_3);
  1203.     DrawColumnHeading__racesumm(kMiscString_WON, gCurrent_graf_data->net_sum_x_4);
  1204.     DrawColumnHeading__racesumm(kMiscString_SCORE, gCurrent_graf_data->net_sum_x_5);
  1205.     BrPixelmapLine(gBack_screen,
  1206.         gCurrent_graf_data->net_sum_x_1,
  1207.         gCurrent_graf_data->net_sum_headings_y + 1 + gFont_7->glyph_y - gCurrent_graf_data->net_sum_y_pitch,
  1208.         gBack_screen->width - gCurrent_graf_data->net_sum_x_1,
  1209.         gCurrent_graf_data->net_sum_headings_y + 1 + gFont_7->glyph_y - gCurrent_graf_data->net_sum_y_pitch,
  1210.         252);
  1211.  
  1212.     for (i = 0; i < gNumber_of_net_players; i++) {
  1213.         player = &gNet_players[gPlayer_lookup[i]];
  1214.  
  1215.         strcpy(s, player->player_name);
  1216.         if (player->host) {
  1217.             strcat(s, " -");
  1218.             strcat(s, GetMiscString(kMiscString_HOST));
  1219.             strcat(s, "-");
  1220.         }
  1221.         TurnOffPaletteConversion();
  1222.         DRPixelmapRectangleMaskedCopy(gBack_screen,
  1223.             gCurrent_graf_data->net_sum_x_1,
  1224.             gCurrent_graf_data->net_sum_headings_y + 1 + i * gCurrent_graf_data->net_sum_y_pitch,
  1225.             gIcons_pix_low_res,  /* DOS version uses low res, Windows version uses normal res */
  1226.             0,
  1227.             gCurrent_graf_data->net_head_icon_height * player->car_index,
  1228.             gIcons_pix_low_res->width,  /* DOS version uses low res, Windows version uses normal res */
  1229.             gCurrent_graf_data->net_head_icon_height);
  1230.         TurnOnPaletteConversion();
  1231.         DrawAnItem__racesumm(gCurrent_graf_data->net_sum_x_2, i, 83, s);
  1232.         sprintf(s, "%d", player->played);
  1233.         DrawAnItem__racesumm(gCurrent_graf_data->net_sum_x_3, i, 83, s);
  1234.         sprintf(s, "%d", player->won);
  1235.         DrawAnItem__racesumm(gCurrent_graf_data->net_sum_x_4, i, 83, s);
  1236.         sprintf(s, "%d", player->games_score);
  1237.         DrawAnItem__racesumm(gCurrent_graf_data->net_sum_x_5, i, 83, s);
  1238.     }
  1239. }
  1240.  
  1241. // IDA: void __cdecl DoNetRaceSummary()
  1242. void DoNetRaceSummary(void) {
  1243.     static tFlicette flicker_on[1] = { { 321, { 219, 112 }, { 172, 362 } } };
  1244.     static tFlicette flicker_off[1] = { { 322, { 219, 112 }, { 172, 362 } } };
  1245.     static tFlicette push[1] = { { 323, { 219, 112 }, { 172, 362 } } };
  1246.     static tMouse_area mouse_areas[1] = { { { 219, 112 }, { 172, 362 }, { 282, 182 }, { 192, 379 }, 0, 0, 0, NULL } };
  1247.     static tInterface_spec interface_spec = {
  1248.         0,              // initial_imode
  1249.         63,             // first_opening_flic
  1250.         0,              // second_opening_flic
  1251.         -1,             // end_flic_go_ahead
  1252.         -1,             // end_flic_escaped
  1253.         -1,             // end_flic_otherwise
  1254.         8,              // flic_bunch_to_load
  1255.         { -1, 0 },      // move_left_new_mode
  1256.         { 0, 0 },       // move_left_delta
  1257.         { 0, 0 },       // move_left_min
  1258.         { 0, 0 },       // move_left_max
  1259.         { NULL, NULL }, // move_left_proc
  1260.         { -1, 0 },      // move_right_new_mode
  1261.         { 0, 0 },       // move_right_delta
  1262.         { 0, 0 },       // move_right_min
  1263.         { 0, 0 },       // move_right_max
  1264.         { NULL, NULL }, // move_right_proc
  1265.         { -1, 0 },      // move_up_new_mode
  1266.         { 0, 0 },       // move_up_delta
  1267.         { 0, 0 },       // move_up_min
  1268.         { 0, 0 },       // move_up_max
  1269.         { NULL, NULL }, // move_up_proc
  1270.         { -1, 0 },      // move_down_new_mode
  1271.         { 0, 0 },       // move_down_delta
  1272.         { 0, 0 },       // move_down_min
  1273.         { 0, 0 },       // move_down_max
  1274.         { NULL, NULL }, // move_down_proc
  1275.         { 1, 1 },       // go_ahead_allowed
  1276.         { NULL, NULL }, // go_ahead_proc
  1277.         { 1, 1 },       // escape_allowed
  1278.         { NULL, NULL }, // escape_proc
  1279.         NULL,           // exit_proc
  1280.         &NetSumDraw,    // draw_proc
  1281.         10000,          // time_out
  1282.         NULL,           // start_proc1
  1283.         NULL,           // start_proc2
  1284.         NULL,           // done_proc
  1285.         0,              // font_needed
  1286.         { 0, 0 },       // typeable
  1287.         NULL,           // get_original_string
  1288.         0,              // escape_code
  1289.         1,              // dont_save_or_load
  1290.         1,              // number_of_button_flics
  1291.         flicker_on,     // flicker_on_flics
  1292.         flicker_off,    // flicker_off_flics
  1293.         push,           // pushed_flics
  1294.         1,              // number_of_mouse_areas
  1295.         mouse_areas,    // mouse_areas
  1296.         0,              // number_of_recopy_areas
  1297.         NULL            // recopy_areas
  1298.     };
  1299.     int i;
  1300.     int result;
  1301.     tS32 start_time;
  1302.     LOG_TRACE("()");
  1303.  
  1304.     NetPlayerStatusChanged(ePlayer_status_summary);
  1305.     start_time = PDGetTotalTime();
  1306.     while (!gReceived_game_scores && PDGetTotalTime() - start_time < 20000) {
  1307.         NetService(0);
  1308.     }
  1309.     if (gReceived_game_scores) {
  1310.         for (i = 0; i < gNumber_of_net_players; i++) {
  1311.             gPlayer_lookup[i] = i;
  1312.         }
  1313.         SortGameScores();
  1314.         TurnOnPaletteConversion();
  1315.         DoInterfaceScreen(&interface_spec, 0, 0);
  1316.         NetPlayerStatusChanged(ePlayer_status_loading);
  1317.         TurnOffPaletteConversion();
  1318.         FadePaletteDown();
  1319.     }
  1320. }
  1321.  
  1322. // IDA: tSO_result __usercall DoEndRaceSummary@<EAX>(int *pFirst_summary_done@<EAX>, tRace_result pRace_result@<EDX>)
  1323. tSO_result DoEndRaceSummary(int* pFirst_summary_done, tRace_result pRace_result) {
  1324.     tSO_result result;
  1325.     LOG_TRACE("(%p, %d)", pFirst_summary_done, pRace_result);
  1326.  
  1327.     if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  1328.         gRank_etc_munged = 1;
  1329.         DoEndRaceSummary2();
  1330.         return eSO_continue;
  1331.     }
  1332.     gRank_etc_munged = 0;
  1333.     if (!*pFirst_summary_done && pRace_result != eRace_timed_out) {
  1334.         if (gNet_mode != eNet_mode_none) {
  1335.             CalcRankIncrease();
  1336.             MungeRankEtc(&gProgram_state);
  1337.             DoNetRaceSummary();
  1338.             return eSO_continue;
  1339.         }
  1340.         result = DoEndRaceSummary1();
  1341.     } else {
  1342.         result = eSO_continue;
  1343.     }
  1344.     *pFirst_summary_done = 1;
  1345.     if (result == eSO_game_over) {
  1346.         DoGameOverAnimation();
  1347.         gProgram_state.prog_status = eProg_opening;
  1348.         result = eSO_continue;
  1349.     } else if (result == eSO_main_menu_invoked) {
  1350.         *pFirst_summary_done = 0;
  1351.         result = eSO_main_menu_invoked;
  1352.     } else {
  1353.         if (result == eSO_game_completed) {
  1354.             DoGameCompletedAnimation();
  1355.         }
  1356.         result = DoEndRaceSummary2();
  1357.         if (result != eSO_main_menu_invoked) {
  1358.             TotallyRepairCar();
  1359.             result = eSO_continue;
  1360.         }
  1361.     }
  1362.     return result;
  1363. }
  1364.