Subversion Repositories Games.Carmageddon

Rev

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

  1. #include "racesumm.h"
  2. #include "brender/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->render_style = BR_RSTYLE_NONE; // FIXME: remove this line once BR_RSTYLE_BOUNDING_EDGES rener style has been implemente
  773.         sel_actor->model = gWreck_array[gWreck_selected].actor->model;
  774.         BrActorAdd(gWreck_array[gWreck_selected].actor, sel_actor);
  775.     } else {
  776.         sel_actor = NULL;
  777.     }
  778.     the_time = PDGetTotalTime();
  779.     SpinWrecks(the_time - gLast_wreck_draw);
  780.     gLast_wreck_draw = the_time;
  781.     if (gWreck_zoom_out >= 0 || gWreck_zoom_in >= 0) {
  782.         if (gWreck_start_zoom == 0) {
  783.             gWreck_start_zoom = the_time;
  784.         }
  785.         finished = the_time - gWreck_start_zoom > 1000;
  786.         if (finished) {
  787.             the_time = gWreck_start_zoom + 1000;
  788.         }
  789.         if (gWreck_zoom_out < 0) {
  790.             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];
  791.             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];
  792.             camera_movement.v[2] = (1.f - (the_time - gWreck_start_zoom) / 1000.f) * -1.45f;
  793.             if (finished) {
  794.                 gWreck_zoom_in = -1;
  795.                 gWreck_zoomed_in = -1;
  796.             }
  797.         } else {
  798.             camera_movement.v[0] = (the_time - gWreck_start_zoom) / 1000.f * gWreck_array[gWreck_zoom_out].actor->t.t.translate.t.v[0];
  799.             camera_movement.v[1] = (the_time - gWreck_start_zoom) / 1000.f * gWreck_array[gWreck_zoom_out].actor->t.t.translate.t.v[1];
  800.             camera_movement.v[2] = (the_time - gWreck_start_zoom) / 1000.f * -1.45f;
  801.             if (finished) {
  802.                 gWreck_zoomed_in = gWreck_zoom_out;
  803.                 gWreck_zoom_out = -1;
  804.             }
  805.         }
  806.         gWreck_camera->t.t.translate.t.v[0] = camera_movement.v[0];
  807.         gWreck_camera->t.t.translate.t.v[1] = camera_movement.v[1];
  808.         gWreck_camera->t.t.translate.t.v[2] = camera_movement.v[2] + 2.2f;
  809.     }
  810.     EnsureRenderPalette();
  811.     EnsurePaletteUp();
  812.     BrPixelmapFill(gWreck_z_buffer, 0xffffffff);
  813.     BrPixelmapFill(gWreck_render_area, BR_COLOUR_RGBA(0xb0, 0xb0, 0xb0, 0xb0));
  814.  
  815.     rows = gWreck_render_area->height / 15.f;
  816.     columns = gWreck_render_area->width / 15.f;
  817.     for (v = 0; v <= rows; v++) {
  818.         BrPixelmapLine(gWreck_render_area,
  819.             -gWreck_render_area->origin_x,
  820.             gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
  821.             gWreck_render_area->width - gWreck_render_area->origin_x,
  822.             gWreck_render_area->height / 2.f - 15.f * (rows / 2.f - v) - gWreck_render_area->origin_y,
  823.             8);
  824.     }
  825.     for (h = 0; h <= columns; h++) {
  826.         BrPixelmapLine(gWreck_render_area,
  827.             gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
  828.             -gWreck_render_area->origin_y,
  829.             gWreck_render_area->width / 2.f - 15.f * (columns / 2.f - h) - gWreck_render_area->origin_x,
  830.             gWreck_render_area->height - gWreck_render_area->origin_y,
  831.             8);
  832.     }
  833.     BrZbSceneRenderBegin(gUniverse_actor, gWreck_camera, gWreck_render_area, gWreck_z_buffer);
  834.     BrZbSceneRenderAdd(gWreck_root);
  835.     BrZbSceneRenderEnd();
  836.     if (sel_actor != NULL) {
  837.         BrActorRemove(sel_actor);
  838.         sel_actor->model = NULL;
  839.         BrActorFree(sel_actor);
  840.     }
  841.     BrPixelmapRectangleFill(gBack_screen,
  842.         gCurrent_graf_data->wreck_name_left,
  843.         gCurrent_graf_data->wreck_name_top,
  844.         gCurrent_graf_data->wreck_name_right - gCurrent_graf_data->wreck_name_left,
  845.         gCurrent_graf_data->wreck_name_bottom - gCurrent_graf_data->wreck_name_top,
  846.         0);
  847.     if (gWreck_selected >= 0 && (gWreck_zoomed_in >= 0 || pCurrent_mode == 0)) {
  848.         name = GetDriverName(gWreck_array[gWreck_selected].car_type,
  849.             gWreck_array[gWreck_selected].car_index);
  850.         TransBrPixelmapText(gBack_screen,
  851.             (gCurrent_graf_data->wreck_name_left + gCurrent_graf_data->wreck_name_right - BrPixelmapTextWidth(gBack_screen, gFont_7, name)) / 2,
  852.             gCurrent_graf_data->wreck_name_base_line,
  853.             84,
  854.             gFont_7,
  855.             name);
  856.     }
  857. }
  858.  
  859. // IDA: int __usercall DamageScrnLeft@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  860. int DamageScrnLeft(int* pCurrent_choice, int* pCurrent_mode) {
  861.     int i;
  862.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  863.  
  864.     gDone_initial = 1;
  865.     DRS3StartSound(gEffects_outlet, 3000);
  866.     if (*pCurrent_mode == 0 && gWreck_zoomed_in < 0) {
  867.         if (gWreck_selected < 0) {
  868.             gWreck_selected = gWreck_count - 1;
  869.         } else if (gWreck_selected != 0 && gWreck_array[gWreck_selected - 1].pos_y == gWreck_array[gWreck_selected].pos_y) {
  870.             gWreck_selected--;
  871.         } else {
  872.             for (i = gWreck_count - 1; i >= 0; i--) {
  873.                 if (gWreck_array[i].pos_y == gWreck_array[gWreck_selected].pos_y) {
  874.                     gWreck_selected = i;
  875.                     break;
  876.                 }
  877.             }
  878.         }
  879.     } else if (gWreck_zoomed_in >= 0) {
  880.         *pCurrent_choice = *pCurrent_choice + 1;
  881.         if (*pCurrent_choice == 3) {
  882.             *pCurrent_choice = 1;
  883.         }
  884.     }
  885.     return 1;
  886. }
  887.  
  888. // IDA: int __usercall DamageScrnRight@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  889. int DamageScrnRight(int* pCurrent_choice, int* pCurrent_mode) {
  890.     int i;
  891.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  892.  
  893.     gDone_initial = 1;
  894.     DRS3StartSound(gEffects_outlet, 3000);
  895.     if (*pCurrent_mode == 0 && gWreck_zoomed_in < 0) {
  896.         if (gWreck_selected < 0) {
  897.             gWreck_selected = 0;
  898.         } else if (gWreck_selected - 1 != gWreck_count && gWreck_array[gWreck_selected + 1].pos_y == gWreck_array[gWreck_selected].pos_y) {
  899.             gWreck_selected++;
  900.         } else {
  901.             for (i = 0; i < gWreck_count; i++) {
  902.                 if (gWreck_array[i].pos_y == gWreck_array[gWreck_selected].pos_y) {
  903.                     gWreck_selected = i;
  904.                     break;
  905.                 }
  906.             }
  907.         }
  908.     } else if (gWreck_zoomed_in >= 0) {
  909.         *pCurrent_choice = *pCurrent_choice + 1;
  910.         if (*pCurrent_choice == 3) {
  911.             *pCurrent_choice = 1;
  912.         }
  913.     }
  914.     return 1;
  915. }
  916.  
  917. // IDA: int __usercall DamageScrnUp@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  918. int DamageScrnUp(int* pCurrent_choice, int* pCurrent_mode) {
  919.     int i;
  920.     int difference;
  921.     int new_difference;
  922.     int new_selection;
  923.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  924.  
  925.     gDone_initial = 1;
  926.     DRS3StartSound(gEffects_outlet, 3000);
  927.     if (*pCurrent_mode != 0) {
  928.         *pCurrent_mode = 0;
  929.         *pCurrent_choice = 0;
  930.         if (gWreck_zoomed_in < 0) {
  931.             gWreck_selected = gWreck_count + -1;
  932.         }
  933.     } else {
  934.         if (gWreck_selected < 0) {
  935.             if (gWreck_zoomed_in < 0) {
  936.                 gWreck_selected = gWreck_count - 1;
  937.             }
  938.         } else if (gWreck_zoomed_in >= 0) {
  939.             *pCurrent_mode = 1;
  940.             *pCurrent_choice = 1;
  941.         } else if (gWreck_array[gWreck_selected].pos_y == gWreck_array[0].pos_y) {
  942.             *pCurrent_mode = 1;
  943.             *pCurrent_choice = 2;
  944.         } else {
  945.             new_difference = 1000;
  946.             new_selection = gWreck_selected;
  947.             for (i = 0; i < gWreck_count; i++) {
  948.                 if (gWreck_array[gWreck_selected].pos_y - 1.f == gWreck_array[i].pos_y) {
  949.                     if (gWreck_array[i].pos_x == gWreck_array[gWreck_selected].pos_x) {
  950.                         new_selection = i;
  951.                         break;
  952.                     }
  953.                     difference = abs((int)(gWreck_array[i].pos_x - gWreck_array[gWreck_selected].pos_x));
  954.                     if (difference < new_difference) {
  955.                         new_selection = i;
  956.                         new_difference = difference;
  957.                     }
  958.                 }
  959.             }
  960.             gWreck_selected = new_selection;
  961.         }
  962.     }
  963.     return 1;
  964. }
  965.  
  966. // IDA: int __usercall DamageScrnDown@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  967. int DamageScrnDown(int* pCurrent_choice, int* pCurrent_mode) {
  968.     int i;
  969.     int difference;
  970.     int new_difference;
  971.     int new_selection;
  972.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  973.  
  974.     gDone_initial = 1;
  975.     DRS3StartSound(gEffects_outlet, 3000);
  976.     if (*pCurrent_mode != 0) {
  977.         *pCurrent_mode = 0;
  978.         *pCurrent_choice = 0;
  979.         if (gWreck_zoomed_in < 0) {
  980.             for (i = gWreck_count - 1; i >= 0; i--) {
  981.                 if (gWreck_array[i].pos_y == gWreck_array[0].pos_y) {
  982.                     gWreck_selected = i;
  983.                     break;
  984.                 }
  985.             }
  986.         }
  987.     } else {
  988.         if (gWreck_selected < 0) {
  989.             gWreck_selected = 0;
  990.         } else if (gWreck_zoomed_in >= 0) {
  991.             *pCurrent_mode = 1;
  992.             *pCurrent_choice = 1;
  993.         } else if (gWreck_array[gWreck_selected].pos_y == gWreck_array[gWreck_count - 1].pos_y) {
  994.             *pCurrent_mode = 1;
  995.             *pCurrent_choice = 2;
  996.         } else {
  997.             new_difference = 1000;
  998.             new_selection = gWreck_selected;
  999.             for (i = 0; i < gWreck_count; i++) {
  1000.                 if (gWreck_array[gWreck_selected].pos_y + 1.f == gWreck_array[i].pos_y) {
  1001.                     if (gWreck_array[i].pos_x == gWreck_array[gWreck_selected].pos_x) {
  1002.                         new_selection = i;
  1003.                         break;
  1004.                     }
  1005.                     difference = abs((int)(gWreck_array[i].pos_x - gWreck_array[gWreck_selected].pos_x));
  1006.                     if (difference < new_difference) {
  1007.                         new_selection = i;
  1008.                         new_difference = difference;
  1009.                     }
  1010.                 }
  1011.             }
  1012.             gWreck_selected = new_selection;
  1013.         }
  1014.     }
  1015.     return 1;
  1016. }
  1017.  
  1018. // IDA: int __usercall DamageScrnGoHead@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>)
  1019. int DamageScrnGoHead(int* pCurrent_choice, int* pCurrent_mode) {
  1020.     LOG_TRACE("(%p, %p)", pCurrent_choice, pCurrent_mode);
  1021.  
  1022.     gDone_initial = 1;
  1023.     if (*pCurrent_choice == 2) {
  1024.         return 1;
  1025.     } else if (gWreck_zoomed_in < 0) {
  1026.         if (*pCurrent_choice == 0 && gWreck_selected >= 0) {
  1027.             ZoomOutTo(gWreck_selected, pCurrent_choice, pCurrent_mode);
  1028.             gUser_interacted = 1;
  1029.         }
  1030.     } else if (*pCurrent_choice == 1) {
  1031.         ZoomInTo(gWreck_selected, pCurrent_choice, pCurrent_mode);
  1032.     }
  1033.     gDone_initial = 1;
  1034.     return 0;
  1035. }
  1036.  
  1037. // IDA: int __usercall ClickDamage@<EAX>(int *pCurrent_choice@<EAX>, int *pCurrent_mode@<EDX>, int pX_offset@<EBX>, int pY_offset@<ECX>)
  1038. int ClickDamage(int* pCurrent_choice, int* pCurrent_mode, int pX_offset, int pY_offset) {
  1039.     int mouse_x;
  1040.     int mouse_y;
  1041.     int old_mouse_x;
  1042.     int old_mouse_y;
  1043.     LOG_TRACE("(%p, %p, %d, %d)", pCurrent_choice, pCurrent_mode, pX_offset, pY_offset);
  1044.  
  1045. #if defined(DETHRACE_FIX_BUGS)
  1046.     old_mouse_x = 0; // Fixes warning caused by -Wsometimes-uninitialized
  1047.     old_mouse_y = 0; // Fixes warning caused by -Wsometimes-uninitialized
  1048. #endif
  1049.     GetMousePosition(&old_mouse_y, &old_mouse_y);
  1050.     if (gWreck_zoomed_in < 0) {
  1051.         if (CastSelectionRay(pCurrent_choice, pCurrent_mode)) {
  1052.             gUser_interacted = 1;
  1053.             return 1;
  1054.         } else {
  1055.             return 0;
  1056.         }
  1057.     } else {
  1058.         while (1) {
  1059.             GetMousePosition(&mouse_x, &mouse_y);
  1060.             BrMatrix34RollingBall(&gWreck_array[gWreck_zoomed_in].rotation, mouse_x - old_mouse_x, old_mouse_y - mouse_y, 50);
  1061.             old_mouse_x = mouse_x;
  1062.             old_mouse_y = mouse_y;
  1063.             gWreck_array[gWreck_zoomed_in].customised = 1;
  1064.             RemoveTransientBitmaps(1);
  1065.             DamageScrnDraw(0, 0);
  1066.             ProcessFlicQueue(gFrame_period);
  1067.             DoMouseCursor();
  1068.             PDScreenBufferSwap(0);
  1069.             ServiceGame();
  1070.             if (!EitherMouseButtonDown()) {
  1071.                 break;
  1072.             }
  1073.         }
  1074.         return 0;
  1075.     }
  1076. }
  1077.  
  1078. // IDA: int __usercall DamageScrnDone@<EAX>(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>, int pGo_ahead@<EBX>, int pEscaped@<ECX>, int pTimed_out)
  1079. int DamageScrnDone(int pCurrent_choice, int pCurrent_mode, int pGo_ahead, int pEscaped, int pTimed_out) {
  1080.     LOG_TRACE("(%d, %d, %d, %d, %d)", pCurrent_choice, pCurrent_mode, pGo_ahead, pEscaped, pTimed_out);
  1081.  
  1082.     if (pTimed_out) {
  1083.         pCurrent_choice = 2;
  1084.     }
  1085.     return pCurrent_choice;
  1086. }
  1087.  
  1088. // IDA: tSO_result __cdecl DoEndRaceSummary2()
  1089. tSO_result DoEndRaceSummary2(void) {
  1090.     static tFlicette flicker_on[3] = {
  1091.         { -1, { 0, 0 }, { 0, 0 } },
  1092.         { 321, { 9, 18 }, { 174, 418 } },
  1093.         { 321, { 247, 494 }, { 174, 418 } },
  1094.     };
  1095.     static tFlicette flicker_off[3] = {
  1096.         { -1, { 0, 0 }, { 0, 0 } },
  1097.         { 322, { 9, 18 }, { 174, 418 } },
  1098.         { 322, { 247, 494 }, { 174, 418 } },
  1099.     };
  1100.     static tFlicette push[3] = {
  1101.         { -1, { 0, 0 }, { 0, 0 } },
  1102.         { 324, { 9, 18 }, { 174, 418 } },
  1103.         { 323, { 247, 494 }, { 174, 418 } },
  1104.     };
  1105.     static tMouse_area mouse_areas[3] = {
  1106.         { { 11, 22 }, { 20, 48 }, { 309, 618 }, { 169, 406 }, 0, 0, 0, ClickDamage },
  1107.         { { 9, 18 }, { 174, 418 }, { 72, 144 }, { 194, 466 }, 1, 1, 0, NULL },
  1108.         { { 247, 494 }, { 174, 418 }, { 310, 620 }, { 194, 466 }, 2, 1, 0, NULL },
  1109.     };
  1110.     static tInterface_spec interface_spec = {
  1111.         1, 320, 0, -1, -1, -1, -1,
  1112.         { -1, -1 }, { 0, 0 }, { 0, 0 }, { 0, 2 }, { DamageScrnLeft, DamageScrnLeft },
  1113.         { -1, -1 }, { 0, 0 }, { 0, 0 }, { 0, 2 }, { DamageScrnRight, DamageScrnRight },
  1114.         { 1, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 }, { DamageScrnUp, DamageScrnUp },
  1115.         { 1, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 }, { DamageScrnDown, DamageScrnDown },
  1116.         { 1, 1 }, { DamageScrnGoHead, DamageScrnGoHead },
  1117.         { 1, 1 }, { NULL, NULL },
  1118.         DamageScrnExit, DamageScrnDraw,
  1119.         20000, NULL, NULL, DamageScrnDone, 0,
  1120.         { 0, 0 }, NULL, 2, 1,
  1121.         COUNT_OF(flicker_on), flicker_on, flicker_off, push,
  1122.         COUNT_OF(mouse_areas), mouse_areas,
  1123.         0, NULL
  1124.     };
  1125.     int result;
  1126.     LOG_TRACE("()");
  1127.  
  1128.     if (gAusterity_mode) {
  1129.         return eSO_continue;
  1130.     }
  1131.     NetPlayerStatusChanged(ePlayer_status_wrecks_gallery);
  1132.     gBack_button_ptr = &mouse_areas[1];
  1133.     memcpy(&gOld_back_button, &mouse_areas[1], sizeof(tMouse_area));
  1134.     gOld_back_button.new_choice = 1;
  1135.     gOld_back_button.new_mode = 1;
  1136.     memcpy(gBack_button_ptr, &gOld_back_button, sizeof(tMouse_area));
  1137.     BuildWrecks();
  1138.     gWreck_zoom_out = -1;
  1139.     gWreck_zoom_in = -1;
  1140.     gWreck_start_zoom = 0;
  1141.     gWreck_zoomed_in = -1;
  1142.     gWreck_selected = 0;
  1143.     gUser_interacted = 0;
  1144.     gDone_initial = 0;
  1145.     TurnOnPaletteConversion();
  1146.     gWreck_gallery_start = 0;
  1147.     result = DoInterfaceScreen(&interface_spec, 0, 2);
  1148.     NetPlayerStatusChanged(ePlayer_status_loading);
  1149.     TurnOffPaletteConversion();
  1150.     DisposeWrecks();
  1151.     if (result < 0) {
  1152.         return eSO_main_menu_invoked;
  1153.     }
  1154.     return eSO_continue;
  1155. }
  1156.  
  1157. // IDA: void __usercall DrawAnItem(int pX@<EAX>, int pY_index@<EDX>, int pFont_index@<EBX>, char *pText@<ECX>)
  1158. //  Suffix added to avoid duplicate symbol
  1159. void DrawAnItem__racesumm(int pX, int pY_index, int pFont_index, char* pText) {
  1160.     LOG_TRACE("(%d, %d, %d, \"%s\")", pX, pY_index, pFont_index, pText);
  1161.     NOT_IMPLEMENTED();
  1162. }
  1163.  
  1164. // IDA: void __usercall DrawColumnHeading(int pStr_index@<EAX>, int pX@<EDX>)
  1165. //  Suffix added to avoid duplicate symbol
  1166. void DrawColumnHeading__racesumm(int pStr_index, int pX) {
  1167.     LOG_TRACE("(%d, %d)", pStr_index, pX);
  1168. }
  1169.  
  1170. // IDA: int __usercall SortScores@<EAX>(void *pFirst_one@<EAX>, void *pSecond_one@<EDX>)
  1171. int SortScores(void* pFirst_one, void* pSecond_one) {
  1172.     LOG_TRACE("(%p, %p)", pFirst_one, pSecond_one);
  1173.     NOT_IMPLEMENTED();
  1174. }
  1175.  
  1176. // IDA: void __cdecl SortGameScores()
  1177. void SortGameScores(void) {
  1178.     LOG_TRACE("()");
  1179.     NOT_IMPLEMENTED();
  1180. }
  1181.  
  1182. // IDA: void __usercall NetSumDraw(int pCurrent_choice@<EAX>, int pCurrent_mode@<EDX>)
  1183. void NetSumDraw(int pCurrent_choice, int pCurrent_mode) {
  1184.     //int i; // Pierre-Marie Baty -- unused variable
  1185.     //char s[256]; // Pierre-Marie Baty -- unused variable
  1186.     //tNet_game_player_info* player; // Pierre-Marie Baty -- unused variable
  1187.     LOG_TRACE("(%d, %d)", pCurrent_choice, pCurrent_mode);
  1188.     NOT_IMPLEMENTED();
  1189. }
  1190.  
  1191. // IDA: void __cdecl DoNetRaceSummary()
  1192. void DoNetRaceSummary(void) {
  1193.     //static tFlicette flicker_on[1]; // Pierre-Marie Baty -- unused variable
  1194.     //static tFlicette flicker_off[1]; // Pierre-Marie Baty -- unused variable
  1195.     //static tFlicette push[1]; // Pierre-Marie Baty -- unused variable
  1196.     //static tMouse_area mouse_areas[1]; // Pierre-Marie Baty -- unused variable
  1197.     //static tInterface_spec interface_spec; // Pierre-Marie Baty -- unused variable
  1198.     //int i; // Pierre-Marie Baty -- unused variable
  1199.     //int result; // Pierre-Marie Baty -- unused variable
  1200.     //tS32 start_time; // Pierre-Marie Baty -- unused variable
  1201.     LOG_TRACE("()");
  1202.     NOT_IMPLEMENTED();
  1203. }
  1204.  
  1205. // IDA: tSO_result __usercall DoEndRaceSummary@<EAX>(int *pFirst_summary_done@<EAX>, tRace_result pRace_result@<EDX>)
  1206. tSO_result DoEndRaceSummary(int* pFirst_summary_done, tRace_result pRace_result) {
  1207.     tSO_result result;
  1208.     LOG_TRACE("(%p, %d)", pFirst_summary_done, pRace_result);
  1209.  
  1210.     if (harness_game_info.mode == eGame_carmageddon_demo || harness_game_info.mode == eGame_splatpack_demo || harness_game_info.mode == eGame_splatpack_xmas_demo) {
  1211.         gRank_etc_munged = 1;
  1212.         DoEndRaceSummary2();
  1213.         return eSO_continue;
  1214.     }
  1215.     gRank_etc_munged = 0;
  1216.     if (!*pFirst_summary_done && pRace_result != eRace_timed_out) {
  1217.         if (gNet_mode != eNet_mode_none) {
  1218.             CalcRankIncrease();
  1219.             MungeRankEtc(&gProgram_state);
  1220.             DoNetRaceSummary();
  1221.             return eSO_continue;
  1222.         }
  1223.         result = DoEndRaceSummary1();
  1224.     } else {
  1225.         result = eSO_continue;
  1226.     }
  1227.     *pFirst_summary_done = 1;
  1228.     if (result == eSO_game_over) {
  1229.         DoGameOverAnimation();
  1230.         gProgram_state.prog_status = eProg_opening;
  1231.         result = eSO_continue;
  1232.     } else if (result == eSO_main_menu_invoked) {
  1233.         *pFirst_summary_done = 0;
  1234.         result = eSO_main_menu_invoked;
  1235.     } else {
  1236.         if (result == eSO_game_completed) {
  1237.             DoGameCompletedAnimation();
  1238.         }
  1239.         result = DoEndRaceSummary2();
  1240.         if (result != eSO_main_menu_invoked) {
  1241.             TotallyRepairCar();
  1242.             result = eSO_continue;
  1243.         }
  1244.     }
  1245.     return result;
  1246. }
  1247.