Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roll, Sidehop, and Equip Swap trainers #45

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e32857c
add epona freed to file menu
fig02 Aug 26, 2019
f1f359b
remove hooks
fig02 Aug 26, 2019
e0445db
move cheat checkboxes to left
fig02 Aug 26, 2019
8275d36
merge upstream
fig02 Oct 21, 2019
31c54df
start roll trainer
fig02 Oct 21, 2019
cfc9789
roll trainer working. sync
fig02 Oct 22, 2019
cd55347
hess thing crashing. sync
fig02 Oct 22, 2019
e344b16
sync
fig02 Oct 22, 2019
4642777
seperate helper functions to new file. off by one error still present
fig02 Oct 23, 2019
1523f0b
fixing frame advance detection
fig02 Oct 23, 2019
7ae1433
start trainer
fig02 Nov 12, 2019
417f822
Merge branch 'master' into trainer
nfaltermeier Mar 21, 2020
7d61388
WIP: Equip swap trainer
nfaltermeier Mar 22, 2020
aeaca8a
Delete old folders for hooks
nfaltermeier Mar 22, 2020
67794e9
Equip swap trainer works sometimes, maybe
nfaltermeier Mar 22, 2020
68f466b
Equip swap trainer doesn't 'miss' inputs
nfaltermeier Apr 2, 2020
35b96f6
Clean up equip swap trainer code and menu
nfaltermeier Apr 2, 2020
5e91f70
Add warning for not having diagonal stick input
nfaltermeier Apr 3, 2020
05901b0
Account for stick diagonal in streak and feedback
nfaltermeier Apr 3, 2020
a022095
Use one unified menu for all trainers
nfaltermeier Apr 8, 2020
eb99c96
Early sidehops work
nfaltermeier Apr 13, 2020
59a22e5
Sidehop message log works, message gen needs work
nfaltermeier Apr 14, 2020
ec3c6e7
1 message per hop, 6 log messages, late is orange
nfaltermeier Apr 15, 2020
8695725
Draft manual changes
nfaltermeier Apr 15, 2020
ba49f23
Cleanup before PR
nfaltermeier Apr 16, 2020
e785bc6
Remove bomb hess trainer code
nfaltermeier Apr 20, 2020
f2ed98b
Best Counter for Trainers
kdjmonaghan Jun 24, 2020
07b0c78
Trainer - Personal Best Tracker
kdjmonaghan Jun 24, 2020
db7a9e7
Trainer - Fix missing semicolon
kdjmonaghan Jun 24, 2020
692bdf5
Trainer - Fix Labels
kdjmonaghan Jun 24, 2020
47802c4
Trainer - Save PB to Settings
kdjmonaghan Jun 24, 2020
ef5aa8c
Trainer - Display Fix
kdjmonaghan Jun 24, 2020
25d6ca8
Add volv's changes
nfaltermeier Aug 7, 2020
2e49655
Shorten equip swap trainer timer
nfaltermeier Aug 7, 2020
c5e6d43
Merge branch 'master' into trainer
nfaltermeier Aug 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*.cbp
*.layout
*.kdev4
.vscode/

# Generated files
obj/
Expand Down
28 changes: 22 additions & 6 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions src/gz/gz.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ static void state_main_hook(void)
zu_reset();
}
}
gz.frame_ran = 1;
}
else {
z64_gfx_t *gfx = z64_ctxt.gfx;
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions src/gz/gz.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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);

Expand Down
272 changes: 272 additions & 0 deletions src/gz/gz_trainer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
#include <stdlib.h>
#include <stdio.h>
#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;
}
Loading