mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Run the sprint patch set only while a sprint button is chosen, and never switch it on over a start that kept it off.
This commit is contained in:
@@ -486,7 +486,8 @@ A change to the sprint button (`runtime/sprint.c`, the set `sprint` of `recomp/p
|
||||
`runtime/ov_input.c` `npad_states`) passes `python tests\sprint_check.py`, which builds `sprint.c` with the rest of the
|
||||
runtime stubbed and plays frames against a stand-in Link: toggle and hold on each kind of button, the states that keep
|
||||
and end a sprint, ZL, the button kept from the game only during play (the bottom button only while Link moves) and a
|
||||
Link the player does not control. No game files.
|
||||
Link the player does not control, and the set switched off with the button off but never over a start that kept it
|
||||
off. No game files.
|
||||
|
||||
A change to the mouse camera (`runtime/mouse_direct.c`, the mouse path of `runtime/input.c`, the set `mouse_direct` of
|
||||
`recomp/patches-v111.txt` or `patches-v143.txt`) passes `python tests\mouse_direct_check.py --cc <clang> --deps
|
||||
|
||||
@@ -151,7 +151,7 @@ The log says which it is when a gamepad is taken: `input: HD rumble available fo
|
||||
|
||||
## Sprint button
|
||||
|
||||
On 1.1.1, `NX2NAVI_SPRINT_BUTTON` (`sprint_button`, the *Sprint button* row on the Controls tab, live) gives a button that makes Link sprint without rolling first: `b` (the bottom face button), `l`, `r`, `l3` or `r3`, or `off` (the default). `NX2NAVI_SPRINT_MODE` (`sprint_mode`, the *Sprint mode* row) is `toggle` (the default: a press starts the sprint, the next press ends it) or `hold` (Link sprints while the button is held). Link sprints while his input flags hold the dash input; the set `sprint` of `recomp/patches-v111.txt` calls `runtime/sprint.c` after Link's movement update and the stance update that follows load those flags, and the host functions add the dash input while the sprint is on, so the game's own sprint runs with its speed, animation and camera. A toggled sprint goes on through jumps, slides and landings and ends when Link's state is anything else (an attack, a roll, an item, swimming, climbing, a cutscene), when ZL is held, or after four frames standing still (ninety after a press made while standing, before Link has moved). During play (Link's update ran within the last eight controller polls) the button is kept from the game, so it does not also fire its own action; the bottom button is kept only while Link moves, so it attacks as usual otherwise. In the settings menu, a menu of the game or a cutscene every button keeps its role. `NX2NAVI_SPRINT_PATCH=0` turns the patch set off and hides the rows.
|
||||
On 1.1.1, `NX2NAVI_SPRINT_BUTTON` (`sprint_button`, the *Sprint button* row on the Controls tab, live) gives a button that makes Link sprint without rolling first: `b` (the bottom face button), `l`, `r`, `l3` or `r3`, or `off` (the default). `NX2NAVI_SPRINT_MODE` (`sprint_mode`, the *Sprint mode* row) is `toggle` (the default: a press starts the sprint, the next press ends it) or `hold` (Link sprints while the button is held). Link sprints while his input flags hold the dash input; the set `sprint` of `recomp/patches-v111.txt` calls `runtime/sprint.c` after Link's movement update and the stance update that follows load those flags, and the host functions add the dash input while the sprint is on, so the game's own sprint runs with its speed, animation and camera. A toggled sprint goes on through jumps, slides and landings and ends when Link's state is anything else (an attack, a roll, an item, swimming, climbing, a cutscene), when ZL is held, or after four frames standing still (ninety after a press made while standing, before Link has moved). During play (Link's update ran within the last eight controller polls) the button is kept from the game, so it does not also fire its own action; the bottom button is kept only while Link moves, so it attacks as usual otherwise. In the settings menu, a menu of the game or a cutscene every button keeps its role. The set runs only while a button is chosen, so with the sprint button off its sites cost nothing. `NX2NAVI_SPRINT_PATCH=0` (or a mod whose code patches the same sites) keeps the patch set off and hides the rows.
|
||||
|
||||
## Nintendo Switch 2 controllers
|
||||
|
||||
|
||||
@@ -98,10 +98,24 @@ static int state_kind(uint64_t link) {
|
||||
return STOP;
|
||||
}
|
||||
|
||||
int sprint_available(void) { return rt_patch_enabled(rt_patch_set("sprint")); }
|
||||
// The set runs only while a button is chosen, so with the sprint button off its sites cost nothing. Whether it may run
|
||||
// at all is read once, after the start: NX2NAVI_SPRINT_PATCH=0 or a mod whose code patches the same sites (mods.c
|
||||
// mods_exefs_switch) leaves it off then, and switching it on later must not undo that.
|
||||
static _Atomic int allowed = -1;
|
||||
static int sprint_set(void) { return rt_patch_set("sprint"); }
|
||||
int sprint_available(void) {
|
||||
int a = atomic_load(&allowed);
|
||||
if (a < 0) {
|
||||
a = rt_patch_enabled(sprint_set());
|
||||
atomic_store(&allowed, a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
void sprint_pad(uint64_t *buttons) {
|
||||
int b = setting_button();
|
||||
if (!sprint_available()) return;
|
||||
int b = setting_button(), set = sprint_set();
|
||||
if (rt_patch_enabled(set) != (b != BTN_OFF)) rt_patch_switch(set, b != BTN_OFF);
|
||||
if (b == BTN_OFF) return;
|
||||
static uint64_t last;
|
||||
uint64_t bit = button_bits[b], now = *buttons;
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
void sprint_pad(uint64_t *buttons);
|
||||
// One update of Link's sprint: kind is how his state treats it (sprint.c), button and hold the settings. 1 while on.
|
||||
int sprint_step(int kind, int button, int hold);
|
||||
// 1 when the game version has the patch set sprint and it is on: the Controls tab shows the sprint rows then.
|
||||
// 1 when the game version has the patch set sprint and nothing keeps it off (NX2NAVI_SPRINT_PATCH=0, a mod patching
|
||||
// its sites): the Controls tab shows the sprint rows then. The set itself runs only while a button is chosen.
|
||||
int sprint_available(void);
|
||||
|
||||
@@ -19,8 +19,10 @@ const char *cfg_value(const char *key) {
|
||||
if (!strcmp(key, "NX2NAVI_SPRINT_MODE")) return mode;
|
||||
return "";
|
||||
}
|
||||
static int set_on = 1; // the patch set sprint, on at the start as its default
|
||||
int rt_patch_set(const char *name) { return strcmp(name, "sprint") ? -1 : 3; }
|
||||
int rt_patch_enabled(int set) { return set == 3; }
|
||||
int rt_patch_enabled(int set) { return set == 3 && set_on; }
|
||||
void rt_patch_switch(int set, int on) { assert(set == 3); set_on = on != 0; }
|
||||
void rt_register_host(uint64_t addr, GuestFn fn) {
|
||||
if (addr == RT_PATCH_PAGE + 0xf40) link_fn = fn;
|
||||
else if (addr == RT_PATCH_PAGE + 0xf50) stance_fn = fn;
|
||||
@@ -43,10 +45,10 @@ static uint64_t frame(uint64_t buttons, int32_t state, uint64_t *who) {
|
||||
memset(&c, 0, sizeof c);
|
||||
c.x[19] = (uintptr_t)who;
|
||||
c.x[10] = 0x40; // other flag bits stay
|
||||
link_fn(&c);
|
||||
if (set_on) link_fn(&c); // the sites call in only while the set is on
|
||||
flags_x10 = c.x[10];
|
||||
c.x[8] = 0x01;
|
||||
stance_fn(&c);
|
||||
if (set_on) stance_fn(&c);
|
||||
flags_w8 = c.x[8];
|
||||
return buttons;
|
||||
}
|
||||
@@ -64,11 +66,23 @@ static void reset(const char *btn, const char *md) {
|
||||
assert(!dashing());
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int main(int argc, char **argv) {
|
||||
char *img = calloc(1, 0x4c80000);
|
||||
assert(img);
|
||||
test_main_base = (uintptr_t)img;
|
||||
assert(link_fn && stance_fn && sprint_available());
|
||||
assert(link_fn && stance_fn);
|
||||
if (argc > 1 && !strcmp(argv[1], "kept-off")) {
|
||||
// off at the start (NX2NAVI_SPRINT_PATCH=0, or a mod patching its sites): no rows, never switched on, and the
|
||||
// button always reaches the game
|
||||
set_on = 0, button = "l3";
|
||||
assert(!sprint_available());
|
||||
uint64_t bt = L3;
|
||||
for (int i = 0; i < 20; i++) bt = L3, sprint_pad(&bt);
|
||||
assert(!set_on && bt == L3);
|
||||
puts("sprint_check kept-off: ok");
|
||||
return 0;
|
||||
}
|
||||
assert(sprint_available());
|
||||
link[0] = test_main_base + 0x4b4dfc0, link[3] = (uintptr_t)owner;
|
||||
other[0] = test_main_base + 0x4b4dfc0, other[3] = (uintptr_t)a; // Link's class, another owner
|
||||
owner[0x270 / 8] = (uintptr_t)a, a[0x188 / 8] = (uintptr_t)b, b[0x1c0 / 8] = (uintptr_t)state_obj;
|
||||
@@ -79,9 +93,12 @@ int main(void) {
|
||||
manager[0] = test_main_base + 0x4bdfe68, manager[0x470 / 8] = (uintptr_t)controlled;
|
||||
controlled[0] = test_main_base + 0x4b49698, controlled[3] = (uintptr_t)owner;
|
||||
|
||||
// off: nothing changes, the button reaches the game
|
||||
// off: nothing changes, the button reaches the game, and the set's sites stop calling in
|
||||
reset("off", "toggle");
|
||||
assert(frame(L3, WALK, link) == L3 && !dashing());
|
||||
assert(frame(L3, WALK, link) == L3 && !dashing() && !set_on);
|
||||
button = "l3";
|
||||
frame(0, WALK, link);
|
||||
assert(set_on); // a button chosen: the set runs again
|
||||
|
||||
// toggle on L3: a press while walking starts it; the button never reaches the game during play
|
||||
reset("l3", "toggle");
|
||||
|
||||
@@ -46,7 +46,8 @@ def main():
|
||||
subprocess.check_call([compiler(), '-O1', '-Wall', '-Wno-unused-function', '-fno-strict-aliasing',
|
||||
'-I', str(work), '-I', str(ROOT / 'runtime'),
|
||||
str(ROOT / 'tests' / 'sprint_check.c'), '-o', str(exe)], env=env)
|
||||
return subprocess.run([str(exe)], env=env).returncode
|
||||
rc = subprocess.run([str(exe)], env=env).returncode
|
||||
return subprocess.run([str(exe), 'kept-off'], env=env).returncode or rc
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user