Subversion Repositories Games.Carmageddon

Rev

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

  1. #include "replay.h"
  2. #include "brender.h"
  3. #include "brhton.h"
  4. #include "car.h"
  5. #include "controls.h"
  6. #include "displays.h"
  7. #include "globvars.h"
  8. #include "globvrpb.h"
  9. #include "grafdata.h"
  10. #include "graphics.h"
  11. #include "input.h"
  12. #include "loading.h"
  13. #include "main.h"
  14. #include "netgame.h"
  15. #include "oil.h"
  16. #include "opponent.h"
  17. #include "piping.h"
  18. #include "s3/s3.h"
  19. #include "sys.h"
  20. #include "utility.h"
  21. #include "world.h"
  22.  
  23. #include "harness/config.h"
  24. #include "harness/os.h"
  25. #include "harness/trace.h"
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29.  
  30. char* gReplay_pixie_names[10] = {
  31.     "REPLAY.PIX",
  32.     "RBUTTONS.PIX",
  33.     "REWSTART.PIX",
  34.     "REW.PIX",
  35.     "REVPLAY.PIX",
  36.     "PAUSE.PIX",
  37.     "PLAY.PIX",
  38.     "FFWD.PIX",
  39.     "FWDEND.PIX",
  40.     "CAMERA.PIX",
  41. };
  42. int gSingle_frame_mode = 0;
  43. tU32 gCam_change_time = 0;
  44. int gSave_file = 0;
  45. int gProgress_line_left[2] = { 70, 141 };
  46. int gProgress_line_right[2] = { 279, 558 };
  47. int gProgress_line_top[2] = { 178, 429 };
  48. br_pixelmap* gReplay_pixies[10];
  49. int gKey_down;
  50. int gNo_cursor;
  51. int gSave_frame_number;
  52. int gCam_change_button_down;
  53. tU32 gAction_replay_start_time;
  54. tU32 gLast_replay_zappy_screen;
  55. tS32 gStopped_time;
  56. float gPending_replay_rate;
  57. tU32 gAction_replay_end_time;
  58. float gReplay_rate;
  59. int gSave_bunch_ID;
  60. int gPlay_direction;
  61. int gPaused;
  62. tAction_replay_camera_type gAction_replay_camera_mode;
  63.  
  64. // IDA: int __cdecl ReplayIsPaused()
  65. int ReplayIsPaused(void) {
  66.     LOG_TRACE8("()");
  67.  
  68.     return gReplay_rate == 0.f;
  69. }
  70.  
  71. // IDA: float __cdecl GetReplayRate()
  72. float GetReplayRate(void) {
  73.     LOG_TRACE("()");
  74.  
  75.     return gReplay_rate;
  76. }
  77.  
  78. // IDA: int __cdecl GetReplayDirection()
  79. int GetReplayDirection(void) {
  80.     LOG_TRACE("()");
  81.  
  82.     return gPlay_direction;
  83. }
  84.  
  85. // IDA: void __cdecl StopSaving()
  86. void StopSaving(void) {
  87.     LOG_TRACE("()");
  88.  
  89.     gSave_file = 0;
  90.     gPaused = 1;
  91. }
  92.  
  93. // IDA: void __usercall ActualActionReplayHeadups(int pSpecial_zappy_bastard@<EAX>)
  94. void ActualActionReplayHeadups(int pSpecial_zappy_bastard) {
  95.     tU32 the_time;
  96.     int x;
  97.     //tU16 played_col1; // Pierre-Marie Baty -- unused variable
  98.     //tU16 played_col2; // Pierre-Marie Baty -- unused variable
  99.     //tU16 to_play_col1; // Pierre-Marie Baty -- unused variable
  100.     //tU16 to_play_col2; // Pierre-Marie Baty -- unused variable
  101.     LOG_TRACE("(%d)", pSpecial_zappy_bastard);
  102.  
  103.     the_time = PDGetTotalTime();
  104.     if (gSave_file || PDKeyDown(KEY_SHIFT_ANY)) {
  105.         return;
  106.     }
  107.     if ((the_time / 400) % 2) {
  108.         DRMaskedStamp(gCurrent_graf_data->action_replay_R_x,
  109.             gCurrent_graf_data->action_replay_R_y,
  110.             gReplay_pixies[0]);
  111.     }
  112.     DRMaskedStamp(gCurrent_graf_data->action_replay_controls_x,
  113.         gCurrent_graf_data->action_replay_controls_y,
  114.         gReplay_pixies[1]);
  115.     if (pSpecial_zappy_bastard < 0) {
  116.         DRMaskedStamp(gCurrent_graf_data->action_replay_rew_start_x,
  117.             gCurrent_graf_data->action_replay_hilite_y,
  118.             gReplay_pixies[2]);
  119.     } else if (pSpecial_zappy_bastard == 0) {
  120.         if (gReplay_rate < -1.f) {
  121.             DRMaskedStamp(gCurrent_graf_data->action_replay_rew_x,
  122.                 gCurrent_graf_data->action_replay_hilite_y,
  123.                 gReplay_pixies[3]);
  124.         } else if (gReplay_rate > 1.f) {
  125.             DRMaskedStamp(gCurrent_graf_data->action_replay_ffwd_x,
  126.                 gCurrent_graf_data->action_replay_hilite_y,
  127.                 gReplay_pixies[7]);
  128.         } else if (gReplay_rate == 1.f) {
  129.             DRMaskedStamp(gCurrent_graf_data->action_replay_play_x,
  130.                 gCurrent_graf_data->action_replay_hilite_y,
  131.                 gReplay_pixies[6]);
  132.         } else if (gReplay_rate == -1.f) {
  133.             DRMaskedStamp(gCurrent_graf_data->action_replay_rev_play_x,
  134.                 gCurrent_graf_data->action_replay_hilite_y,
  135.                 gReplay_pixies[4]);
  136.         } else {
  137.             DRMaskedStamp(gCurrent_graf_data->action_replay_pause_x,
  138.                 gCurrent_graf_data->action_replay_hilite_y,
  139.                 gReplay_pixies[5]);
  140.         }
  141.     } else {
  142.         DRMaskedStamp(gCurrent_graf_data->action_replay_fwd_end_x,
  143.             gCurrent_graf_data->action_replay_hilite_y,
  144.             gReplay_pixies[8]);
  145.     }
  146.  
  147.     x = gProgress_line_left[gGraf_data_index] + (float)(gProgress_line_right[gGraf_data_index] - gProgress_line_left[gGraf_data_index]) * (gLast_replay_frame_time - gAction_replay_start_time) / (gAction_replay_end_time - gAction_replay_start_time);
  148.     if (x > gProgress_line_left[gGraf_data_index]) {
  149.         BrPixelmapLine(gBack_screen,
  150.             gProgress_line_left[gGraf_data_index], gProgress_line_top[gGraf_data_index],
  151.             x - 1, gProgress_line_top[gGraf_data_index],
  152.             2);
  153.         BrPixelmapLine(gBack_screen,
  154.             gProgress_line_left[gGraf_data_index], gProgress_line_top[gGraf_data_index] + 1,
  155.             x - 1, gProgress_line_top[gGraf_data_index] + 1,
  156.             4);
  157.         BrPixelmapLine(gBack_screen,
  158.             gProgress_line_left[gGraf_data_index], gProgress_line_top[gGraf_data_index] + 2,
  159.             x - 1, gProgress_line_top[gGraf_data_index] + 2,
  160.             2);
  161.     }
  162.     if (x < gProgress_line_right[gGraf_data_index]) {
  163.         BrPixelmapLine(gBack_screen,
  164.             x, gProgress_line_top[gGraf_data_index],
  165.             gProgress_line_right[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index],
  166.             81);
  167.         BrPixelmapLine(gBack_screen,
  168.             x, gProgress_line_top[gGraf_data_index] + 1,
  169.             gProgress_line_right[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index] + 1,
  170.             82);
  171.         BrPixelmapLine(gBack_screen,
  172.             x, gProgress_line_top[gGraf_data_index] + 2,
  173.             gProgress_line_right[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index] + 2,
  174.             81);
  175.     }
  176.     BrPixelmapLine(gBack_screen,
  177.         gProgress_line_left[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index],
  178.         gProgress_line_left[gGraf_data_index] - 1, gProgress_line_top[gGraf_data_index] + 2,
  179.         2);
  180.     BrPixelmapLine(gBack_screen,
  181.         gProgress_line_right[gGraf_data_index], gProgress_line_top[gGraf_data_index],
  182.         gProgress_line_right[gGraf_data_index], gProgress_line_top[gGraf_data_index] + 2,
  183.         81);
  184.     if (gCam_change_button_down) {
  185.         DRMaskedStamp(gCurrent_graf_data->action_replay_camera_x,
  186.             gCurrent_graf_data->action_replay_hilite_y,
  187.             gReplay_pixies[9]);
  188.     }
  189.     if (the_time - gCam_change_time < 2000) {
  190.         TransDRPixelmapText(gBack_screen,
  191.             gCurrent_graf_data->action_replay_cam_text_x - DRTextWidth(&gFonts[kFont_ORANGHED], GetMiscString(gAction_replay_camera_mode ? kMiscString_PanningCamera : kMiscString_StandardCamera)),
  192.             gCurrent_graf_data->action_replay_cam_text_y,
  193.             &gFonts[kFont_ORANGHED],
  194.             GetMiscString(gAction_replay_camera_mode ? kMiscString_PanningCamera : kMiscString_StandardCamera),
  195.             2 * gCurrent_graf_data->action_replay_cam_text_x);
  196.     }
  197.     TurnOnPaletteConversion();
  198.     DoMouseCursor();
  199.     TurnOffPaletteConversion();
  200. }
  201.  
  202. // IDA: void __cdecl DoActionReplayPostSwap()
  203. void DoActionReplayPostSwap(void) {
  204.     LOG_TRACE("()");
  205.  
  206.     RemoveTransientBitmaps(1);
  207. }
  208.  
  209. // IDA: void __usercall DoZappyActionReplayHeadups(int pSpecial_zappy_bastard@<EAX>)
  210. void DoZappyActionReplayHeadups(int pSpecial_zappy_bastard) {
  211.     tU32 the_time;
  212.     LOG_TRACE("(%d)", pSpecial_zappy_bastard);
  213.  
  214.     the_time = PDGetTotalTime();
  215.     // Draw screen every 50ms (when we are going fast)
  216.     if (abs(pSpecial_zappy_bastard) > 10000 && the_time - gLast_replay_zappy_screen > 50) {
  217.         ActualActionReplayHeadups(pSpecial_zappy_bastard);
  218.         gLast_replay_zappy_screen = the_time;
  219.         PDScreenBufferSwap(0);
  220.         RemoveTransientBitmaps(1);
  221.     }
  222. }
  223.  
  224. // IDA: void __cdecl DoActionReplayHeadups()
  225. void DoActionReplayHeadups(void) {
  226.     LOG_TRACE("()");
  227.  
  228.     ActualActionReplayHeadups(0);
  229. }
  230.  
  231. // IDA: void __usercall MoveReplayBuffer(tS32 pMove_amount@<EAX>)
  232. void MoveReplayBuffer(tS32 pMove_amount) {
  233.     tU8* play_ptr;
  234.     tU8* old_play_ptr;
  235.     tU8* old_play_ptr2;
  236.     int i;
  237.     //int a; // Pierre-Marie Baty -- unused variable
  238.     tU32 old_time;
  239.     LOG_TRACE("(%d)", pMove_amount);
  240.  
  241.     old_play_ptr = NULL;
  242.     gLast_replay_zappy_screen = 0;
  243.     old_play_ptr2 = GetPipePlayPtr();
  244.     play_ptr = old_play_ptr2;
  245.     old_time = GetTotalTime();
  246.     for (i = 0; i < abs(pMove_amount) && play_ptr != old_play_ptr; i++) {
  247.         if (KeyIsDown(KEYMAP_ESCAPE)) {
  248.             break;
  249.         }
  250.         if (SomeReplayLeft()) {
  251.             PipingFrameReset();
  252.         }
  253.         old_play_ptr = play_ptr;
  254.         if (pMove_amount >= 1) {
  255.             while (!ApplyPipedSession(&play_ptr)) {
  256.                 DoZappyActionReplayHeadups(pMove_amount);
  257.             }
  258.             SetPipePlayPtr(play_ptr);
  259.         } else if (pMove_amount <= -1) {
  260.             while (!UndoPipedSession(&play_ptr)) {
  261.                 DoZappyActionReplayHeadups(pMove_amount);
  262.             }
  263.             SetPipePlayPtr(play_ptr);
  264.         }
  265.         ProcessOilSpills(gFrame_period);
  266.     }
  267.     if (gReplay_rate < 0.f) {
  268.         CheckSound((tPipe_chunk*)old_play_ptr2, old_time, GetTotalTime());
  269.     }
  270.     if (old_play_ptr == play_ptr) {
  271.         gReplay_rate = 0.f;
  272.         gPaused = 1;
  273.         StopSaving();
  274.     }
  275.     if (KeyIsDown(KEYMAP_ESCAPE)) {
  276.         WaitForNoKeys();
  277.     }
  278. }
  279.  
  280. // IDA: void __cdecl MoveToEndOfReplay()
  281. void MoveToEndOfReplay(void) {
  282.     float old_replay_rate;
  283.     LOG_TRACE("()");
  284.  
  285.     DisablePipedSounds();
  286.     old_replay_rate = gReplay_rate;
  287.     gReplay_rate = 100.f;
  288.     MoveReplayBuffer(INT32_MAX);
  289.     gReplay_rate = old_replay_rate;
  290.     EnablePipedSounds();
  291. }
  292.  
  293. // IDA: void __cdecl MoveToStartOfReplay()
  294. void MoveToStartOfReplay(void) {
  295.     float old_replay_rate;
  296.     LOG_TRACE("()");
  297.  
  298.     DisablePipedSounds();
  299.     old_replay_rate = gReplay_rate;
  300.     gReplay_rate = -100.f;
  301.     MoveReplayBuffer(-INT32_MAX);
  302.     gReplay_rate = old_replay_rate;
  303.     EnablePipedSounds();
  304. }
  305.  
  306. // IDA: void __cdecl ToggleReplay()
  307. void ToggleReplay(void) {
  308.     LOG_TRACE("()");
  309.  
  310.     if (!IsActionReplayAvailable()) {
  311.         NewTextHeadupSlot(4, 0, 1000, -4, GetMiscString(kMiscString_ACTION_REPLAY_UNAVAILABLE));
  312.         return;
  313.     }
  314.     if (!gAction_replay_mode) {
  315.         if (gMap_mode) {
  316.             ToggleMap();
  317.         }
  318.         if (gNet_mode == eNet_mode_host) {
  319.             SendGameplayToAllPlayers(eNet_gameplay_host_paused, 0, 0, 0, 0);
  320.         }
  321.         gReplay_rate = 1.f;
  322.         gPaused = 1;
  323.         gStopped_time = PDGetTotalTime();
  324.         gPlay_direction = 1;
  325.         gAction_replay_end_time = GetTotalTime();
  326.         gLast_replay_frame_time = gAction_replay_end_time;
  327.         gAction_replay_start_time = GetARStartTime();
  328.         ResetPipePlayToEnd();
  329.         LoadInterfaceStuff(1);
  330.         StartMouseCursor();
  331.         gKey_down = KEY_KP_ENTER;
  332.         gPending_replay_rate = 0;
  333.         gCam_change_time = PDGetTotalTime();
  334.         if (!gRace_finished) {
  335.             SaveCameraPosition(0);
  336.         }
  337.     } else {
  338.         MoveToEndOfReplay();
  339.         EndMouseCursor();
  340.         S3SetEffects(NULL, NULL);
  341.         UnlockInterfaceStuff();
  342.         AddLostTime(PDGetTotalTime() - gStopped_time);
  343.         if (!gRace_finished) {
  344.             RestoreCameraPosition(0);
  345.         }
  346.         if (gNet_mode == eNet_mode_host) {
  347.             SendGameplayToAllPlayers(eNet_gameplay_host_unpaused, 0, 0, 0, 0);
  348.         }
  349.     }
  350.     gAction_replay_mode = !gAction_replay_mode;
  351.     ForceRebuildActiveCarList();
  352. }
  353.  
  354. // IDA: void __usercall ReverseSound(tS3_effect_tag pEffect_index@<EAX>, tS3_sound_tag pSound_tag@<EDX>)
  355. void ReverseSound(tS3_effect_tag pEffect_index, tS3_sound_tag pSound_tag) {
  356.     LOG_TRACE("(%d, %d)", pEffect_index, pSound_tag);
  357.     NOT_IMPLEMENTED();
  358. }
  359.  
  360. // IDA: int __cdecl FindUniqueFile()
  361. int FindUniqueFile(void) {
  362.     int index;
  363.     FILE* f;
  364.     tPath_name the_path;
  365.     LOG_TRACE("()");
  366.  
  367.     for (index = 0; index < 1000; index++) {
  368.         PathCat(the_path, gApplication_path, "BMPFILES");
  369.         PathCat(the_path, the_path, "");
  370.         sprintf(&the_path[strlen(the_path)], "%03d", index);
  371.         strcat(the_path, "_0000.BMP");
  372.         f = DRfopen(the_path, "rt");
  373.         if (f == NULL) {
  374.             return index;
  375.         }
  376.         fclose(f);
  377.     }
  378.     return 0;
  379. }
  380.  
  381. // IDA: void __usercall PollActionReplayControls(tU32 pFrame_period@<EAX>)
  382. void PollActionReplayControls(tU32 pFrame_period) {
  383.     float old_replay_rate;
  384.     int old_key_down;
  385.     int x_coord;
  386.     int y_coord;
  387.     int i;
  388.     tU32 real_time;
  389.     static tU32 last_real_time = 0;
  390.     static int psuedo_mouse_keys[8] = {
  391.         KEY_KP_7,
  392.         KEY_KP_4,
  393.         KEY_COMMA,
  394.         KEY_SPACE,
  395.         KEY_PERIOD,
  396.         KEY_KP_6,
  397.         KEY_KP_9,
  398.         KEY_KP_MULTIPLY,
  399.     };
  400.     /* clang-format off */
  401.     static tRectangle mouse_areas[2][8] = {
  402.         {
  403.             {  63, 182,  92, 198, },
  404.             {  93, 182, 118, 198, },
  405.             { 119, 182, 144, 198, },
  406.             { 145, 182, 166, 198, },
  407.             { 167, 182, 192, 198, },
  408.             { 193, 182, 218, 198, },
  409.             { 219, 182, 244, 198, },
  410.             { 245, 182, 272, 198, },
  411.         },
  412.         {
  413.             { 126, 436, 184, 475, },
  414.             { 186, 436, 236, 475, },
  415.             { 238, 436, 288, 475, },
  416.             { 290, 436, 332, 475, },
  417.             { 334, 436, 384, 475, },
  418.             { 386, 436, 436, 475, },
  419.             { 438, 436, 488, 475, },
  420.             { 490, 436, 544, 475, },
  421.         },
  422.     };
  423.     /* clang-format on */
  424.     LOG_TRACE("(%d)", pFrame_period);
  425.  
  426.     real_time = PDGetTotalTime();
  427.     old_replay_rate = gReplay_rate;
  428.     old_key_down = gKey_down == KEY_CAPSLOCK ? -1 : gKey_down;
  429.     gKey_down = PDAnyKeyDown();
  430.     if (KeyIsDown(KEYMAP_REPLAYMODE) && old_key_down == -1) {
  431.         ToggleReplay();
  432.         return;
  433.     }
  434.  
  435.     if (gKey_down == -1) {
  436.         if ((old_key_down == -1 || old_key_down == KEY_KP_4 || old_key_down == KEY_KP_6 || old_key_down == KEY_KP_MULTIPLY) && EitherMouseButtonDown()) {
  437.             GetMousePosition(&x_coord, &y_coord);
  438.             for (i = 0; i < COUNT_OF(mouse_areas[0]); i++) {
  439.                 if (mouse_areas[gGraf_data_index][i].left <= x_coord && mouse_areas[gGraf_data_index][i].top <= y_coord && mouse_areas[gGraf_data_index][i].right >= x_coord && mouse_areas[gGraf_data_index][i].bottom >= y_coord) {
  440.                     gKey_down = psuedo_mouse_keys[i];
  441.                     break;
  442.                 }
  443.             }
  444.         }
  445.     } else {
  446.         gMouse_in_use = 0;
  447.     }
  448.  
  449.     if (gKey_down == KEY_KP_DIVIDE && old_key_down != KEY_KP_DIVIDE) {
  450.         if (gSave_file) {
  451.             StopSaving();
  452.         } else {
  453.             gSave_bunch_ID = FindUniqueFile();
  454.             gSave_frame_number = 0;
  455.             gSave_file = 1;
  456.             gPlay_direction = 1;
  457.             gPaused = 0;
  458.         }
  459.     }
  460.  
  461.     if (gKey_down == KEY_KP_MULTIPLY) {
  462.         gCam_change_button_down = 1;
  463.         if (old_key_down != KEY_KP_MULTIPLY) {
  464.             gCam_change_time = PDGetTotalTime();
  465.             if (gAction_replay_camera_mode == eAction_replay_action) {
  466.                 gAction_replay_camera_mode = eAction_replay_standard;
  467.             } else {
  468.                 gAction_replay_camera_mode++;
  469.             }
  470.         }
  471.     } else {
  472.         gCam_change_button_down = 0;
  473.     }
  474.  
  475.     if ((gKey_down == KEY_KP_5 || gKey_down == KEY_SPACE) && old_key_down == -1) {
  476.         gPaused = !gPaused;
  477.     } else if ((gKey_down == KEY_KP_0 || gKey_down == KEY_BACKSPACE) && old_key_down == -1) {
  478.         gPlay_direction = -gPlay_direction;
  479.         if (gPaused) {
  480.             gPaused = 0;
  481.         }
  482.     }
  483.  
  484.     if (gKey_down == KEY_KP_1 && old_key_down == -1) {
  485.         gReplay_rate = -1.f;
  486.         gPlay_direction = -1;
  487.         gSingle_frame_mode = 1;
  488.     } else if (gKey_down == KEY_KP_3 && old_key_down == -1) {
  489.         gReplay_rate = 1.f;
  490.         gPlay_direction = 1;
  491.         gSingle_frame_mode = 1;
  492.     } else if (gKey_down == KEY_KP_4 || gKey_down == KEY_PAGEUP) {
  493.         if (gReplay_rate > -1.2f) {
  494.             gReplay_rate = -1.2f;
  495.         }
  496.         if (last_real_time != 0) {
  497.             gReplay_rate -= 0.002f * (real_time - last_real_time);
  498.         }
  499.         if (gReplay_rate < -8.f) {
  500.             gReplay_rate = -8.f;
  501.         }
  502.     } else if (gKey_down == KEY_KP_6 || gKey_down == KEY_PAGEDOWN) {
  503.         if (gReplay_rate < 1.2f) {
  504.             gReplay_rate = 1.2f;
  505.         }
  506.         if (last_real_time != 0) {
  507.             gReplay_rate += 0.002f * (real_time - last_real_time);
  508.         }
  509.         if (gReplay_rate > 8.f) {
  510.             gReplay_rate = 8.f;
  511.         }
  512.     } else if (gKey_down == KEY_COMMA) {
  513.         gReplay_rate = -1.f;
  514.         gPlay_direction = -1;
  515.         gPaused = 0;
  516.     } else if (gKey_down == KEY_PERIOD) {
  517.         gReplay_rate = 1.f;
  518.         gPlay_direction = 1;
  519.         gPaused = 0;
  520.     } else if (gPaused) {
  521.         gReplay_rate = 0.f;
  522.     } else {
  523.         gReplay_rate = (float)gPlay_direction;
  524.     }
  525.  
  526.     if ((gKey_down == KEY_KP_7 || gKey_down == KEY_HOME) && old_key_down == -1) {
  527.         MoveToStartOfReplay();
  528.         gReplay_rate = 1.f;
  529.         MungeCarGraphics(gFrame_period);
  530.         GrooveThoseDelics();
  531.         gReplay_rate = 0.f;
  532.         gPlay_direction = 1;
  533.         gPaused = 1;
  534.     } else if ((gKey_down == KEY_KP_9 || gKey_down == KEY_END) && old_key_down == -1) {
  535.         MoveToEndOfReplay();
  536.         gReplay_rate = -1.f;
  537.         MungeCarGraphics(gFrame_period);
  538.         GrooveThoseDelics();
  539.         gReplay_rate = 0.f;
  540.         gPlay_direction = -1;
  541.         gPaused = 1;
  542.     }
  543.  
  544.     if (gPending_replay_rate != 0.f) {
  545.         gReplay_rate = gPending_replay_rate;
  546.     }
  547.     if (old_replay_rate * gReplay_rate >= 0.f) {
  548.         gPending_replay_rate = 0.f;
  549.     } else {
  550.         gPending_replay_rate = gReplay_rate;
  551.         gReplay_rate = 0.f;
  552.     }
  553.  
  554.     if (old_replay_rate != 0.f) {
  555.         gFrame_period = gFrame_period * gReplay_rate / old_replay_rate;
  556.     }
  557.     last_real_time = fabsf(gReplay_rate) >= 1.2f ? real_time : 0;
  558.  
  559.     if (old_replay_rate <= 0.f && gReplay_rate > 0.f) {
  560.         S3SetEffects(NULL, NULL);
  561.     } else if (old_replay_rate >= 0.f && gReplay_rate < 0.f) {
  562.         S3SetEffects(ReverseSound, ReverseSound);
  563.     }
  564. }
  565.  
  566. // IDA: void __cdecl CheckReplayTurnOn()
  567. void CheckReplayTurnOn(void) {
  568.     LOG_TRACE("()");
  569.  
  570.     if (!gAction_replay_mode) {
  571.         if (!KeyIsDown(KEYMAP_REPLAYMODE) || gEntering_message) {
  572.             gKey_down = -1;
  573.         } else if (gKey_down == -1) {
  574.             ToggleReplay();
  575.         }
  576.     }
  577. }
  578.  
  579. // IDA: void __cdecl InitializeActionReplay()
  580. void InitializeActionReplay(void) {
  581.     int i;
  582.     LOG_TRACE("()");
  583.  
  584.     for (i = 0; i < COUNT_OF(gReplay_pixie_names); i++) {
  585.         gReplay_pixies[i] = LoadPixelmap(gReplay_pixie_names[i]);
  586.     }
  587.     gAction_replay_camera_mode = eAction_replay_action;
  588. }
  589.  
  590. // IDA: void __usercall DoActionReplay(tU32 pFrame_period@<EAX>)
  591. void DoActionReplay(tU32 pFrame_period) {
  592.     LOG_TRACE("(%d)", pFrame_period);
  593.  
  594.     if (gReplay_rate != 0.f) {
  595.         MoveReplayBuffer((tS32)gReplay_rate);
  596.     }
  597. }
  598.  
  599. // IDA: void __cdecl SynchronizeActionReplay()
  600. void SynchronizeActionReplay(void) {
  601.     FILE* f;
  602.     tPath_name the_path;
  603.     static tU32 gLast_synch_time;
  604.     LOG_TRACE("()");
  605.  
  606.     while (gReplay_rate != 0.f) {
  607.         if (PDGetTotalTime() - gLast_synch_time >= gFrame_period / fabsf(gReplay_rate)) {
  608.             break;
  609.         }
  610.         ServiceGameInRace();
  611.     }
  612.     gLast_synch_time = PDGetTotalTime();
  613.     if (gSingle_frame_mode) {
  614.         gReplay_rate = 0.f;
  615.         gSingle_frame_mode = 0;
  616.     }
  617.  
  618.     if (gSave_file) {
  619.         PathCat(the_path, gApplication_path, "BMPFILES");
  620.         strcat(the_path, gDir_separator);
  621.         sprintf(&the_path[strlen(the_path)], "%03d_%04d.BMP", gSave_bunch_ID, gSave_frame_number);
  622.         f = DRfopen(the_path, "wb");
  623.         if (f != NULL) {
  624.             PrintScreenFile(f);
  625.             fclose(f);
  626.         }
  627.         gSave_frame_number++;
  628.     }
  629. }
  630.