diff --git a/.gitignore b/.gitignore index 8adee9aa..ce2bb153 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.cbp *.layout *.kdev4 +.vscode/ # Generated files obj/ diff --git a/USAGE.md b/USAGE.md index 28dbf5ba..19eea832 100644 --- a/USAGE.md +++ b/USAGE.md @@ -15,9 +15,10 @@ - [2.7 Macro](#27-macro) - [2.7.1 Settings](#271-settings) - [2.7.2 Virtual controller](#272-virtual-controller) - - [2.8 Watches](#28-watches) - - [2.9 Debug](#29-debug) - - [2.10 Settings](#210-settings) + - [2.8 Trainer](#28-trainer) + - [2.9 Watches](#29-watches) + - [2.10 Debug](#210-debug) + - [2.11 Settings](#211-settings) - [3 VC issues](#3-vc-issues) - [4 Issues with savestates](#4-issues-with-savestates) - [4.1 Dangling pointers](#41-dangling-pointers) @@ -355,7 +356,22 @@ be connected to the system. The **joystick** controls set the x and y coordinates of the joystick on the virtual controller, and the **buttons** controls decide what buttons are held down on the virtual controller. -### 2.8 Watches +### 2.8 Trainer +This menu lets you practice and get instant feedback on supported tricks or +techniques. Select a checkbox to enable a specific trainer. It will remain +visible even if the menu is hidden. Savestates do not affect the trainer's +state **at all**, but trainers should reset after advancing enough frames. +Trainers currently available: + +- **Roll Timing:** Gives feedback on rolling for optimal speed. +- **Sidehop Timing:** Gives feedback on optimal sidehops. Supports any + manner of height difference (up hills, down hills, flat ground, etc.). +- **Equip Swap:** Gives feedback about equip swapping. Does not ensure proper + inventory setup, only proper inputs and timing. Makes sure the stick is + moved in 1 frame and is diagonal in that frame, and makes sure a c button + is pressed on the correct frame. + +### 2.9 Watches This menu lets you add custom RAM watches to observe arbitrary parts of game's memory in real-time. Pressing the plus icon will add a new watch, and pressing the cross next to a watch will remove that watch. After adding a watch, enter a @@ -387,7 +403,7 @@ the watches you need, press **return** to go back to the watches menu. The format of watch files is described in the wiki, [here](https://github.com/glankk/gz/wiki/Watch-File-Syntax). -### 2.9 Debug +### 2.10 Debug _Note: These features are for advanced users. Be careful._ This menu contains various debug features to use for testing; @@ -431,7 +447,7 @@ This menu contains various debug features to use for testing; detach the debugger. Press **break** to hit a breakpoint on the graph thread. -### 2.10 Settings +### 2.11 Settings This is where most of the functionality of gz is configured. The **profile** option selects which profile to save and load settings to and from. When the game starts, the settings saved to profile zero are automatically loaded, if diff --git a/src/gz/gz.c b/src/gz/gz.c index 3c901763..a5ccd063 100644 --- a/src/gz/gz.c +++ b/src/gz/gz.c @@ -681,6 +681,7 @@ static void state_main_hook(void) zu_reset(); } } + gz.frame_ran = 1; } else { z64_gfx_t *gfx = z64_ctxt.gfx; @@ -718,6 +719,7 @@ static void state_main_hook(void) --z64_ctxt.state_frames; /* do not execute an ocarina frame */ gz.frame_flag = 0; + gz.frame_ran = 0; } } @@ -1016,6 +1018,7 @@ static void init(void) gz.day_time_prev = z64_file.day_time; gz.target_day_time = -1; gz.frames_queued = -1; + gz.frame_ran = 1; gz.movie_state = MOVIE_IDLE; vector_init(&gz.movie_input, sizeof(struct movie_input)); vector_init(&gz.movie_seed, sizeof(struct movie_seed)); @@ -1099,9 +1102,10 @@ static void init(void) menu_add_submenu(&menu, 0, 5, gz_equips_menu(), "equips"); menu_add_submenu(&menu, 0, 6, gz_file_menu(), "file"); menu_add_submenu(&menu, 0, 7, gz_macro_menu(), "macro"); - menu_add_submenu(&menu, 0, 8, &watches, "watches"); - menu_add_submenu(&menu, 0, 9, gz_debug_menu(), "debug"); - menu_add_submenu(&menu, 0, 10, gz_settings_menu(), "settings"); + menu_add_submenu(&menu, 0, 8, gz_trainer_menu(), "trainer"); + menu_add_submenu(&menu, 0, 9, &watches, "watches"); + menu_add_submenu(&menu, 0, 10, gz_debug_menu(), "debug"); + menu_add_submenu(&menu, 0, 11, gz_settings_menu(), "settings"); /* populate watches menu */ watches.selector = menu_add_submenu(&watches, 0, 0, NULL, "return"); diff --git a/src/gz/gz.h b/src/gz/gz.h index 6b1eb1ff..72d343fc 100644 --- a/src/gz/gz.h +++ b/src/gz/gz.h @@ -140,6 +140,7 @@ struct gz uint16_t day_time_prev; int target_day_time; int32_t frames_queued; + _Bool frame_ran; struct zu_disp_p z_disp_p; uint32_t disp_hook_size[4]; uint32_t disp_hook_p[4]; @@ -256,6 +257,7 @@ struct menu *gz_inventory_menu(void); struct menu *gz_equips_menu(void); struct menu *gz_file_menu(void); struct menu *gz_macro_menu(void); +struct menu *gz_trainer_menu(void); struct menu *gz_debug_menu(void); struct menu *gz_settings_menu(void); diff --git a/src/gz/gz_trainer.c b/src/gz/gz_trainer.c new file mode 100644 index 00000000..4460cad8 --- /dev/null +++ b/src/gz/gz_trainer.c @@ -0,0 +1,272 @@ +#include +#include +#include "gfx.h" +#include "gz.h" +#include "menu.h" +#include "resource.h" +#include "settings.h" +#include "z64.h" +#include "trainer.h" + +#define TRAINER_MENU_ITEM_COUNT 3 +#define SIDEHOP_LOG_LENGTH 6 +// length of "perfect! (frame perfect)" +#define SIDEHOP_LOG_STRING_LENGTH 25 + +static struct menu_item* trainer_menu_data[TRAINER_MENU_ITEM_COUNT]; + +static int roll_timing_draw_proc(struct menu_item *item, + struct menu_draw_params *draw_params) +{ + if (gz.menu_active) + return 1; + + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGB24A8(draw_params->color, + draw_params->alpha)); + struct gfx_font *font = draw_params->font; + int ch = menu_get_cell_height(item->owner, 1); + int x = draw_params->x; + int y = draw_params->y; + + if (gz.frame_ran){ + update_roll(); + roll_check_streak(); + } + + set_rgb_white(); + gfx_printf(font, x, y + ch * 0, "best: %i", settings->trainer_roll_pb); + gfx_printf(font, x, y + ch * 1, "streak: %i", roll.streak); + int log_y = 2; + + if (!roll.is_first_roll && roll.timer_active) { + if ((roll.last_roll_frame < 14) || (roll.last_roll_frame > 19)) { + set_rgb_red(); + int amnt = roll.last_roll_frame - 16; + if (amnt < 0) + gfx_printf(font, x, y + ch * log_y, "bad (%i frames early)", abs(amnt)); + else + gfx_printf(font, x, y + ch * log_y, "bad (%i frames late)", abs(amnt)); + } else { + switch (roll.last_roll_frame) { + case 14: + set_rgb_orange(); + gfx_printf(font, x, y + ch * log_y, "okay (2 frames early)"); + break; + case 15: + set_rgb_lgreen(); + gfx_printf(font, x, y + ch * log_y, "good (1 frame early)"); + break; + case 16: + set_rgb_green(); + gfx_printf(font, x, y + ch * log_y, "perfect! (frame perfect)"); + break; + case 17: + set_rgb_lgreen(); + gfx_printf(font, x, y + ch * log_y, "good (1 frame late)"); + break; + case 18: + set_rgb_yellow(); + gfx_printf(font, x, y + ch * log_y, "okay (2 frames late)"); + break; + case 19: + set_rgb_orange(); + gfx_printf(font, x, y + ch * log_y, "okay (3 frames late)"); + break; + default: + break; + } + } + } + return 1; +} + +static int sidehop_timing_draw_proc(struct menu_item *item, + struct menu_draw_params *draw_params) +{ + if (gz.menu_active) + return 1; + + static char log_messages[SIDEHOP_LOG_LENGTH][SIDEHOP_LOG_STRING_LENGTH]; + static void (*log_message_colors[SIDEHOP_LOG_LENGTH])(); + + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGB24A8(draw_params->color, + draw_params->alpha)); + struct gfx_font *font = draw_params->font; + int ch = menu_get_cell_height(item->owner, 1); + int x = draw_params->x; + int y = draw_params->y; + + if (gz.frame_ran) { + // if a new log entry should be added + if (update_sidehop()) { + // move the old messages up, but let "early" get overwritten + if (strcmp(log_messages[0], "early") != 0) { + for (int i = SIDEHOP_LOG_LENGTH - 2; i > -1; i -= 1) { + strcpy(log_messages[i + 1], log_messages[i]); + log_message_colors[i + 1] = log_message_colors[i]; + } + } + + if (sidehop.result < 0) { + log_message_colors[0] = set_rgb_red; + snprintf(log_messages[0], SIDEHOP_LOG_STRING_LENGTH, "early by %i frames", -sidehop.result); + sidehop.streak = 0; + } else if (sidehop.result == 1) { + log_message_colors[0] = set_rgb_green; + snprintf(log_messages[0], SIDEHOP_LOG_STRING_LENGTH, "perfect! (frame perfect)"); + sidehop.streak += 1; + if (sidehop.streak > settings->trainer_sidehop_pb) { + settings->trainer_sidehop_pb = sidehop.streak; + settings_save(gz.profile); + } + sidehop.result = 0; + } else if (sidehop.result > 1) { + log_message_colors[0] = set_rgb_orange; + snprintf(log_messages[0], SIDEHOP_LOG_STRING_LENGTH, "late by %i frames", sidehop.result - 1); + sidehop.streak = 0; + sidehop.result = 0; + } else if (sidehop.a_press != 0) { + log_message_colors[0] = set_rgb_red; + snprintf(log_messages[0], SIDEHOP_LOG_STRING_LENGTH, "early"); + sidehop.streak = 0; + } + } + } + + set_rgb_white(); + gfx_printf(font, x, y + ch * 0, "best: %d", settings->trainer_sidehop_pb); + gfx_printf(font, x, y + ch * 1, "streak: %d", sidehop.streak); + + for (int i = 0; i < SIDEHOP_LOG_LENGTH; i += 1) + { + // the message is unset, stop searching for log messages to print + if (log_messages[i][0] == '\0') + break; + + log_message_colors[i](); + gfx_printf(font, x, y + ch * (i + 2), log_messages[i]); + } + + return 1; +} + +static int equip_swap_draw_proc(struct menu_item *item, + struct menu_draw_params *draw_params) +{ + if (gz.menu_active) + return 1; + + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGB24A8(draw_params->color, + draw_params->alpha)); + struct gfx_font *font = draw_params->font; + int ch = menu_get_cell_height(item->owner, 1); + int x = draw_params->x; + int y = draw_params->y; + + if(gz.frame_ran){ + update_equip_swap(); + } + + set_rgb_white(); + gfx_printf(font, x, y + ch * 0, "best: %d", settings->trainer_equip_swap_pb); + gfx_printf(font, x, y + ch * 1, "streak: %d", equip_swap.streak); + + if (equip_swap.c_button_press_time > 0) + { + set_rgb_red(); + gfx_printf(font, x, y + ch * 2, "c button: early by %i frames", equip_swap.c_button_press_time); + } + else if (equip_swap.c_button_press_time == 0) + { + set_rgb_green(); + gfx_printf(font, x, y + ch * 2, "c button: perfect! (frame perfect)"); + } + else if (equip_swap.c_button_press_time < 0) + { + set_rgb_red(); + gfx_printf(font, x, y + ch * 2, "c button: late by %i frames", -equip_swap.c_button_press_time); + } + + if (equip_swap.control_stick_moved_time > 0) + { + set_rgb_red(); + gfx_printf(font, x, y + ch * 3, "stick: early by %i frames", equip_swap.control_stick_moved_time); + } + else if (equip_swap.control_stick_moved_time == 0) + { + set_rgb_green(); + gfx_printf(font, x, y + ch * 3, "stick: %s (frame perfect)", equip_swap.diagonal_warning ? "good" : "perfect!"); + } + else if (equip_swap.control_stick_moved_time < 0) + { + set_rgb_red(); + gfx_printf(font, x, y + ch * 3, "stick: late by %i frames", -equip_swap.control_stick_moved_time); + } + + if (equip_swap.diagonal_warning) + { + set_rgb_red(); + gfx_printf(font, x, y + ch * 4, "stick input must be diagonal"); + } + + return 1; +} + +static int trainer_radio_button_toggle_proc(struct menu_item *item, + enum menu_callback_reason reason, + void *data) +{ + int index = (int)data; + if (reason == MENU_CALLBACK_SWITCH_ON) + { + for (int i = 0; i < TRAINER_MENU_ITEM_COUNT; i += 1) + { + if (i == index) + trainer_menu_data[i]->enabled = 1; + else + trainer_menu_data[i]->enabled = 0; + } + } + else if (reason == MENU_CALLBACK_SWITCH_OFF) + { + trainer_menu_data[index]->enabled = 0; + } + else if (reason == MENU_CALLBACK_THINK) + menu_checkbox_set(item, trainer_menu_data[index]->enabled); + return 0; +} + +struct menu *gz_trainer_menu(void) +{ + static struct menu menu; + int global_x = 2; + int global_y = 10; + int index = 0; + + /* setup menu */ + menu_init(&menu, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); + menu.selector = menu_add_submenu(&menu, 0, 0, NULL, "return"); + + /*add roll timing option*/ + trainer_menu_data[index] = menu_add_static_custom(gz.menu_global, global_x, global_y, roll_timing_draw_proc, NULL, 0xFFFFFF); + trainer_menu_data[index]->enabled = 0; + menu_add_checkbox(&menu, 0, index + 1, trainer_radio_button_toggle_proc, (void*)index); + menu_add_static(&menu, 2, index + 1, "roll trainer", 0xC0C0C0); + index += 1; + + /*add sidehop timing option*/ + trainer_menu_data[index] = menu_add_static_custom(gz.menu_global, global_x, global_y, sidehop_timing_draw_proc, NULL, 0xFFFFFF); + trainer_menu_data[index]->enabled = 0; + menu_add_checkbox(&menu, 0, index + 1, trainer_radio_button_toggle_proc, (void*)index); + menu_add_static(&menu, 2, index + 1, "sidehop trainer", 0xC0C0C0); + index += 1; + + /*add equip swap training option*/ + trainer_menu_data[index] = menu_add_static_custom(gz.menu_global, global_x, global_y, equip_swap_draw_proc, NULL, 0xFFFFFF); + trainer_menu_data[index]->enabled = 0; + menu_add_checkbox(&menu, 0, index + 1, trainer_radio_button_toggle_proc, (void*)index); + menu_add_static(&menu, 2, index + 1, "equip swap trainer", 0xC0C0C0); + index += 1; + + return &menu; +} diff --git a/src/gz/settings.c b/src/gz/settings.c index d196d649..c75d88dd 100644 --- a/src/gz/settings.c +++ b/src/gz/settings.c @@ -76,6 +76,9 @@ void settings_load_default(void) d->timer_x = 20; d->timer_y = 24; d->n_watches = 0; + d->trainer_roll_pb = 0; + d->trainer_equip_swap_pb = 0; + d->trainer_sidehop_pb = 0; d->cheats = 0; for (int i = 0; i < SETTINGS_TELEPORT_MAX; ++i) { d->teleport_pos[i].x = 0.f; diff --git a/src/gz/settings.h b/src/gz/settings.h index 5b2eddfd..a1b1d9f8 100644 --- a/src/gz/settings.h +++ b/src/gz/settings.h @@ -163,6 +163,9 @@ struct settings_data struct watch_info watch_info[SETTINGS_WATCHES_MAX]; uint8_t teleport_slot; uint8_t n_watches; + uint32_t trainer_roll_pb; + uint32_t trainer_equip_swap_pb; + uint32_t trainer_sidehop_pb; }; struct settings_header diff --git a/src/gz/trainer.c b/src/gz/trainer.c new file mode 100644 index 00000000..839cb01e --- /dev/null +++ b/src/gz/trainer.c @@ -0,0 +1,312 @@ +#include +#include +#include "gfx.h" +#include "gz.h" +#include "menu.h" +#include "resource.h" +#include "settings.h" +#include "z64.h" +#include "input.h" +#include "trainer.h" + + +struct roll roll; +struct sidehop sidehop; +struct hess hess; +struct equip_swap equip_swap; + +_Bool is_rolling() +{ + if((z64_link.current_animation == ANIM_ROLL) || (z64_link.current_animation == ANIM_ROLL_SHIELD)) + return 1; + else + return 0; +} + +_Bool is_sidehopping() +{ + if((z64_link.current_animation == ANIM_SIDEHOP_L) || (z64_link.current_animation == ANIM_SIDEHOP_R)) + return 1; + else + return 0; +} + +_Bool is_landing() +{ + if((z64_link.current_animation == ANIM_LANDING_L) || (z64_link.current_animation == ANIM_LANDING_R)) + return 1; + else + return 0; +} + +_Bool roll_pressed() +{ + if(is_rolling() && (z64_link.animation_timer == 0)) + return 1; + else + return 0; +} + +_Bool sidehop_pressed() +{ + if(is_sidehopping() && (z64_link.animation_timer == 0)) + return 1; + else + return 0; +} + +_Bool sidehop_a_pressed() +{ + uint16_t pad = z64_game.common.input[0].raw.pad; + if(pad & BUTTON_A && !(sidehop.pad_prev & BUTTON_A)){ + return 1; + }else{ + return 0; + } +} + +_Bool z_pressed() +{ + uint16_t pad = input_pad(); + if (pad & BUTTON_Z){ + return 1; + } else { + return 0; + } +} + +_Bool r_pressed() +{ + uint16_t pad = input_pad(); + if (pad & BUTTON_R){ + return 1; + } else { + return 0; + } +} + +_Bool equip_button_pressed() +{ + uint16_t pad = input_pad(); + if (pad & BUTTON_C_LEFT || pad & BUTTON_C_DOWN || pad & BUTTON_C_RIGHT){ + return 1; + } else { + return 0; + } +} + +// Using z64_game.common.input[0].raw_prev was inconsistent so I store previous input myself +_Bool equip_swap_equip_button_pressed_previous() +{ + uint16_t pad = equip_swap.pad_prev; + if (pad & BUTTON_C_LEFT || pad & BUTTON_C_DOWN || pad & BUTTON_C_RIGHT){ + return 1; + } else { + return 0; + } +} + +int16_t equip_swap_control_stick_x_previous() +{ + return equip_swap.x_prev; +} + +int16_t equip_swap_control_stick_y_previous() +{ + return equip_swap.y_prev; +} + +void update_roll() +{ + if(roll_pressed() && !roll.timer_active){ + roll.timer_active = 1; + roll.is_first_roll = 1; + roll.last_roll_frame = 0; + } else if (roll_pressed() && roll.timer_active){ + roll.is_first_roll = 0; + roll.last_roll_frame = roll.timer; + roll.timer = 0; + } + + if(roll.timer_active){ + roll.timer++; + } else{ + roll.timer = 0; + } + + if(roll.timer == 30){ + roll.timer_active = 0; + } +} + +void roll_check_streak() +{ + if((roll_pressed()) && (roll.last_roll_frame == 16)){ + roll.streak++; + if (roll.streak > settings->trainer_roll_pb) { + settings->trainer_roll_pb = roll.streak; + settings_save(gz.profile); + } + } + if(roll.last_roll_frame != 16){ + roll.streak = 0; + } +} + +// returns whether or not a new log entry should be added +_Bool update_sidehop() +{ + _Bool ret = 0; + if(sidehop_pressed()){ + sidehop.sidehop_timer = 0; + sidehop.sidehop_timer_active = 1; + } + + if(sidehop.sidehop_timer_active && (sidehop_a_pressed())){ + sidehop.a_press = sidehop.sidehop_timer; + // if they may be early + if (sidehop.result == 0) + ret = 1; + } + + if(sidehop.sidehop_timer_active){ + sidehop.sidehop_timer++; + } + + if(is_landing()) { + // if they pressed a early + if (!sidehop.land_timer_active && sidehop.a_press != 0) { + sidehop.result = sidehop.a_press - sidehop.sidehop_timer; + ret = 1; + } + sidehop.land_timer_active = 1; + sidehop.landing = 1; + } else { + sidehop.landing = 0; + } + + if(sidehop_pressed() && sidehop.land_timer_active){ + if (sidehop.result == 0) { + sidehop.result = sidehop.land_timer; + ret = 1; + } + sidehop.land_timer = 0; + sidehop.land_timer_active = 0; + } + + if(sidehop.land_timer_active){ + sidehop.land_timer++; + } + + if(sidehop.land_timer == 20){ + sidehop.land_timer = 0; + sidehop.sidehop_timer = 0; + sidehop.land_timer_active = 0; + sidehop.sidehop_timer_active = 0; + sidehop.result = 0; + sidehop.a_press = 0; + } + + // reset the result after they pressed a early then sucessfully sidehopped + if (sidehop.result < 0 && sidehop_pressed()) { + sidehop.result = 0; + } + + // manually store previous inputs + sidehop.pad_prev = input_pad(); + return ret; +} + +void update_equip_swap() +{ + int8_t x = input_x(); + int8_t y = input_y(); + + // 0 is items, 1 is map, 2 is quest status, 3 is equipment + if (!equip_swap.changing_screen && + ((z64_game.pause_ctxt.screen_idx == 3 && r_pressed()) || (z64_game.pause_ctxt.screen_idx == 1 && z_pressed()))) + { + equip_swap.changing_screen = 1; + equip_swap.timer = 0; + equip_swap.diagonal_warning = 0; + } + else if (equip_swap.changing_screen) + { + equip_swap.timer += 1; + + int8_t abs_x = abs(x); + int8_t abs_y = abs(y); + _Bool stickSet = 0; + // 38 is min control stick value to move cursor + if ((abs_x >= 38 || abs_y >= 38) && + abs(equip_swap_control_stick_x_previous()) < 38 && abs(equip_swap_control_stick_y_previous()) < 38) + { + equip_swap.control_stick_moved_time = 15 - equip_swap.timer; + stickSet = 1; + equip_swap.diagonal_warning = abs_x < 38 || abs_y < 38; + } + + if (equip_button_pressed() && !equip_swap_equip_button_pressed_previous()) + { + equip_swap.c_button_press_time = 15 - equip_swap.timer; + + if (stickSet && equip_swap.control_stick_moved_time == 0 && equip_swap.c_button_press_time == 0 && + !equip_swap.diagonal_warning) + { + /* + They properly did equip swap so we can increment their streak and stop checking inputs. + This also avoids letting the timer expire which clears their streak. + */ + equip_swap.streak += 1; + if (equip_swap.streak > settings->trainer_equip_swap_pb) { + settings->trainer_equip_swap_pb = equip_swap.streak; + settings_save(gz.profile); + } + + equip_swap.changing_screen = 0; + } + } + + if (equip_swap.timer >= 20) + { + equip_swap.changing_screen = 0; + equip_swap.streak = 0; + } + } + + // manually store previous inputs + equip_swap.x_prev = x; + equip_swap.y_prev = y; + equip_swap.pad_prev = input_pad(); +} + +/* set color functions */ +void set_rgb_green() +{ + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0x00, 0xFF, 0x2F, 0xFF)); +} + +void set_rgb_lgreen() +{ + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0x7D, 0xFF, 0x95, 0xFF)); +} + +void set_rgb_yellow() +{ + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xEF, 0xFF, 0x20, 0xFF)); +} + +void set_rgb_orange() +{ + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xFF, 0xA5, 0x10, 0xFF)); +} + +void set_rgb_red() +{ + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xFF, 0x24, 0x24, 0xFF)); +} + +void set_rgb_white() +{ + gfx_mode_set(GFX_MODE_COLOR, GPACK_RGBA8888(0xFF, 0xFF, 0xFF, 0xFF)); +} diff --git a/src/gz/trainer.h b/src/gz/trainer.h new file mode 100644 index 00000000..2877a100 --- /dev/null +++ b/src/gz/trainer.h @@ -0,0 +1,95 @@ +#ifndef TRAINER_H +#define TRAINER_H + +#include +#include "gfx.h" +#include "gz.h" +#include "menu.h" +#include "resource.h" +#include "settings.h" +#include "z64.h" + +enum animation +{ + ANIM_ROLL = 0x04003038, + ANIM_ROLL_SHIELD = 0x04003030, + ANIM_SIDEHOP_L = 0x04002950, + ANIM_LANDING_L = 0x04002960, + ANIM_SIDEHOP_R = 0x04002988, + ANIM_LANDING_R = 0x04002998, + +}; + +struct roll +{ + uint8_t timer; + uint8_t timer_active; + int streak; + uint8_t is_first_roll; + uint8_t last_roll_frame; +}; + +struct hess +{ + int roll_frame; + _Bool ess; + _Bool zr_same; + int zr_frame; + int setup; +}; + +struct sidehop +{ + _Bool sidehopping; + _Bool landing; + _Bool sidehop_timer_active; + _Bool land_timer_active; + uint8_t sidehop_timer; + uint8_t land_timer; + int8_t a_press; + /* + Negative values: frames early + Zero: default + One: Successful + Greater than one: frames late + 1 + */ + int8_t result; + uint16_t pad_prev; + uint8_t streak; +}; + +struct equip_swap +{ + _Bool changing_screen; + uint8_t timer; + int c_button_press_time; + int control_stick_moved_time; + int8_t x_prev; + int8_t y_prev; + uint16_t pad_prev; + uint8_t streak; + _Bool diagonal_warning; +}; + +_Bool is_rolling(); +_Bool roll_pressed(); +void update_roll(); +void roll_check_streak(); + +_Bool update_sidehop(); + +void update_equip_swap(); + +void set_rgb_green(); +void set_rgb_lgreen(); +void set_rgb_yellow(); +void set_rgb_orange(); +void set_rgb_red(); +void set_rgb_white(); + +extern struct roll roll; +extern struct sidehop sidehop; +extern struct hess hess; +extern struct equip_swap equip_swap; + +#endif diff --git a/src/gz/z64.h b/src/gz/z64.h index 9a86b015..d8c5ff3c 100644 --- a/src/gz/z64.h +++ b/src/gz/z64.h @@ -991,7 +991,11 @@ struct z64_actor_s typedef struct { z64_actor_t common; /* 0x0000 */ - char unk_0x13C[0x02F8]; /* 0x013C */ + char unk_0x13C[0x0070]; /* 0x013C */ + uint32_t current_animation; /* 0x01AC */ + char unk_0x1B0[0x000C]; /* 0x01B0 */ + float animation_timer; /* 0x01BC */ + char unk_0x1C0[0x0274]; /* 0x01C0 */ uint8_t action; /* 0x0434 */ char unk_0x435[0x0237]; /* 0x0435 */ uint32_t state_flags_1; /* 0x066C */