sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

record_demo.patch (6804B)


      1 diff --git a/src/game/game_init.c b/src/game/game_init.c
      2 index b961ca52..adfde049 100644
      3 --- a/src/game/game_init.c
      4 +++ b/src/game/game_init.c
      5 @@ -11,6 +11,7 @@
      6  #include "game_init.h"
      7  #include "main.h"
      8  #include "memory.h"
      9 +#include "object_list_processor.h"
     10  #include "profiler.h"
     11  #include "save_file.h"
     12  #include "seq_ids.h"
     13 @@ -386,6 +387,45 @@ void display_and_vsync(void) {
     14  // Controls
     15  // ----------------------------------------------------------------------------------------------------
     16  
     17 +/*
     18 + * This enhancement allows you to record gameplay demos for the mario head screen.
     19 + *
     20 + * Note:
     21 + * This enhancement does require the lastest versions of PJ64 from the nightly builds,
     22 + * because it uses the javascript API to automatically dump the demo files from RAM
     23 + * once the demo is completed. See enhancements/RecordDemo.js for more info
     24 + *
     25 +*/
     26 +
     27 +#include "mario.h"
     28 +
     29 +#define DEMOREC_STATUS_NOT_RECORDING 0
     30 +#define DEMOREC_STATUS_PREPARING 1
     31 +#define DEMOREC_STATUS_RECORDING 2
     32 +#define DEMOREC_STATUS_STOPPING 3
     33 +#define DEMOREC_STATUS_DONE 4
     34 +
     35 +#define DEMOREC_PRINT_X 10
     36 +#define DEMOREC_PRINT_Y 10
     37 +
     38 +#define DEMOREC_DONE_DELAY 60 // Show "DONE" message for 2 seconds.
     39 +
     40 +#define DEMOREC_MAX_INPUTS 1025 // Max number of recorded inputs.
     41 +
     42 +/*
     43 +    DO NOT REMOVE, MODIFY, OR MAKE A COPY OF THIS EXACT STRING!
     44 +    This is here so that the js dump script can find the control variables easily.
     45 +*/
     46 +char gDemoRecTag[] = "DEMORECVARS";
     47 +
     48 +// Control variables. It is easier if they are each 4 byte aligned, which is why they are u32.
     49 +u32 gRecordingStatus = DEMOREC_STATUS_NOT_RECORDING;
     50 +u32 gDoneDelay = 0;
     51 +u32 gNumOfRecordedInputs = 0;
     52 +struct DemoInput gRecordedInputs[DEMOREC_MAX_INPUTS];
     53 +struct DemoInput* gRecordedInputsPtr = (struct DemoInput*)gRecordedInputs;
     54 +struct DemoInput gRecordedDemoInputCopy;
     55 +
     56  /**
     57   * This function records distinct inputs over a 255-frame interval to RAM locations and was likely
     58   * used to record the demo sequences seen in the final game. This function is unused.
     59 @@ -420,6 +460,118 @@ UNUSED static void record_demo(void) {
     60      gRecordedDemoInput.timer++;
     61  }
     62  
     63 +void record_new_demo_input(void) {
     64 +    if (gRecordedDemoInput.timer == 1 && gRecordedDemoInputCopy.timer > 0) {
     65 +        gRecordedInputs[gNumOfRecordedInputs].timer = gRecordedDemoInputCopy.timer;
     66 +        gRecordedInputs[gNumOfRecordedInputs + 1].timer = 0;
     67 +        gRecordedInputs[gNumOfRecordedInputs].rawStickX = gRecordedDemoInputCopy.rawStickX;
     68 +        gRecordedInputs[gNumOfRecordedInputs + 1].rawStickX = gRecordedDemoInputCopy.rawStickX;
     69 +        gRecordedInputs[gNumOfRecordedInputs].rawStickY = gRecordedDemoInputCopy.rawStickY;
     70 +        gRecordedInputs[gNumOfRecordedInputs + 1].rawStickY = gRecordedDemoInputCopy.rawStickY;
     71 +        gRecordedInputs[gNumOfRecordedInputs].buttonMask = gRecordedDemoInputCopy.buttonMask;
     72 +        gRecordedInputs[gNumOfRecordedInputs + 1].buttonMask = gRecordedDemoInputCopy.buttonMask;
     73 +        gNumOfRecordedInputs++;
     74 +    }
     75 +}
     76 +
     77 +// Self explanitory
     78 +void copy_gRecordedDemoInput(void) {
     79 +    gRecordedDemoInputCopy.timer = gRecordedDemoInput.timer;
     80 +    gRecordedDemoInputCopy.rawStickX = gRecordedDemoInput.rawStickX;
     81 +    gRecordedDemoInputCopy.rawStickY = gRecordedDemoInput.rawStickY;
     82 +    gRecordedDemoInputCopy.buttonMask = gRecordedDemoInput.buttonMask;
     83 +}
     84 +
     85 +// Runs when the demo is recording.
     86 +void recording(void) {
     87 +
     88 +    // Force-stop when someone makes too many inputs.
     89 +    if (gNumOfRecordedInputs + 1 > DEMOREC_MAX_INPUTS) {
     90 +        gRecordingStatus = DEMOREC_STATUS_STOPPING;
     91 +        return;
     92 +    }
     93 +
     94 +    copy_gRecordedDemoInput();
     95 +    record_demo();
     96 +    record_new_demo_input();
     97 +}
     98 +
     99 +// Makes sure the last demo input is zeroed out, to make it look more clean.
    100 +void record_cleanup(void) {
    101 +    gRecordedInputs[gNumOfRecordedInputs].timer = 0;
    102 +    gRecordedInputs[gNumOfRecordedInputs].rawStickX = 0;
    103 +    gRecordedInputs[gNumOfRecordedInputs].rawStickY = 0;
    104 +    gRecordedInputs[gNumOfRecordedInputs].buttonMask = 0;
    105 +
    106 +    // Make sure the done delay is reset before moving to DONE status.
    107 +    gDoneDelay = 0;
    108 +}
    109 +
    110 +void record_run(void) {
    111 +    switch(gRecordingStatus) {
    112 +        case DEMOREC_STATUS_NOT_RECORDING:
    113 +            break;
    114 +        case DEMOREC_STATUS_PREPARING:
    115 +            if (gMarioObject != NULL && gCurrLevelNum != LEVEL_NONE) { // If the game is in an active level
    116 +                gRecordingStatus = DEMOREC_STATUS_RECORDING;
    117 +
    118 +                // First 4 values in demo inputs are used to define level ID
    119 +                gNumOfRecordedInputs = 1;
    120 +                gRecordedInputs[0].timer = gCurrLevelNum;
    121 +                gRecordedInputs[0].rawStickX = 0;
    122 +                gRecordedInputs[0].rawStickY = 0;
    123 +                gRecordedInputs[0].buttonMask = 0;
    124 +            }
    125 +            break;
    126 +        case DEMOREC_STATUS_RECORDING:
    127 +            recording();
    128 +            break;
    129 +        case DEMOREC_STATUS_DONE:
    130 +            if (gDoneDelay > DEMOREC_DONE_DELAY)
    131 +                gRecordingStatus = DEMOREC_STATUS_NOT_RECORDING;
    132 +            else
    133 +                gDoneDelay++;
    134 +            break;
    135 +    }
    136 +}
    137 +
    138 +// Prints the status on the bottom-left side of the screen in colorful text.
    139 +void print_status(void) {
    140 +    switch(gRecordingStatus) {
    141 +        case DEMOREC_STATUS_PREPARING:
    142 +            print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "READY");
    143 +            break;
    144 +        case DEMOREC_STATUS_RECORDING:
    145 +            print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "REC");
    146 +            break;
    147 +        case DEMOREC_STATUS_STOPPING:
    148 +            print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "WAIT");
    149 +            break;
    150 +        case DEMOREC_STATUS_DONE:
    151 +            print_text(DEMOREC_PRINT_X, DEMOREC_PRINT_Y, "DONE");
    152 +            break;
    153 +    }
    154 +}
    155 +
    156 +// Main function that should be called from thread5_game_loop()
    157 +void recordingDemo(void) {
    158 +    // Mario needs to enter directly into a level and not from a warp,
    159 +    // so the debug level select is used for that.
    160 +    gDebugLevelSelect = TRUE;
    161 +
    162 +    if (gPlayer1Controller->buttonPressed & L_TRIG) {
    163 +        if (gRecordingStatus == DEMOREC_STATUS_NOT_RECORDING) {
    164 +            gRecordingStatus = DEMOREC_STATUS_PREPARING;
    165 +        } else if (gRecordingStatus == DEMOREC_STATUS_RECORDING) {
    166 +            gRecordingStatus = DEMOREC_STATUS_STOPPING;
    167 +            record_cleanup();
    168 +        }
    169 +    }
    170 +
    171 +    record_run();
    172 +    print_status();
    173 +}
    174 +
    175  /**
    176   * Take the updated controller struct and calculate the new x, y, and distance floats.
    177   */
    178 @@ -684,6 +836,7 @@ void thread5_game_loop(UNUSED void *arg) {
    179          audio_game_loop_tick();
    180          select_gfx_pool();
    181          read_controller_inputs();
    182 +        recordingDemo();
    183          addr = level_script_execute(addr);
    184  
    185          display_and_vsync();