From 5e138304d80f8abce63cd604fb3143fac7ee96ff Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Thu, 7 May 2020 23:30:08 -0400 Subject: [PATCH 01/28] investigate TAS-ing this --- src/pc/controller/controller_recorded_tas.c | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/pc/controller/controller_recorded_tas.c b/src/pc/controller/controller_recorded_tas.c index 4f5c1bb..c55c533 100644 --- a/src/pc/controller/controller_recorded_tas.c +++ b/src/pc/controller/controller_recorded_tas.c @@ -1,25 +1,35 @@ #include #include +#include +#include #include "controller_api.h" -static FILE *fp; +static int fd; +static int counter; + +#define OFFSET 0x400 static void tas_init(void) { - fp = fopen("cont.m64", "rb"); - if (fp != NULL) { - uint8_t buf[0x400]; - fread(buf, 1, sizeof(buf), fp); + fd = open("cont.m64", O_RDONLY); + if (fd != 0) { + uint8_t buf[OFFSET]; + read(fd, buf, sizeof(buf)); + counter = 0; } } static void tas_read(OSContPad *pad) { - if (fp != NULL) { + if (fd != 0) { uint8_t bytes[4] = {0}; - fread(bytes, 1, 4, fp); + int result = read(fd, bytes, 4); + assert(result > 0); pad->button = (bytes[0] << 8) | bytes[1]; pad->stick_x = bytes[2]; pad->stick_y = bytes[3]; + counter+=4; + printf("%x called %04x %d %d\n", (counter + OFFSET), pad->button, bytes[2], bytes[3]); + fflush(stdout); } } From 0a4f693fdcd3b8bdcda3c2353207f2917d449136 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 06:48:52 -0400 Subject: [PATCH 02/28] tas recorder is functional --- shell.nix | 1 + src/pc/controller/controller_entry_point.c | 7 +-- src/pc/controller/controller_recorded_tas.c | 5 +- src/pc/controller/controller_tas_recorder.c | 52 +++++++++++++++++++++ src/pc/controller/controller_tas_recorder.h | 8 ++++ 5 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 src/pc/controller/controller_tas_recorder.c create mode 100644 src/pc/controller/controller_tas_recorder.h diff --git a/shell.nix b/shell.nix index c611f0b..2198fbb 100644 --- a/shell.nix +++ b/shell.nix @@ -16,5 +16,6 @@ in pkgs.mkShell { glfw libGL clang_10 + gdb ]; } diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index faf91f5..a99b5e5 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -3,13 +3,14 @@ #include "controller_recorded_tas.h" #include "controller_keyboard.h" - #include "controller_sdl.h" +#include "controller_tas_recorder.h" static struct ControllerAPI *controller_implementations[] = { &controller_recorded_tas, - &controller_sdl, - &controller_keyboard, + /* &controller_sdl, */ + /* &controller_keyboard, */ + /* &controller_tas_recorder, */ }; s32 osContInit(OSMesgQueue *mq, u8 *controllerBits, OSContStatus *status) { diff --git a/src/pc/controller/controller_recorded_tas.c b/src/pc/controller/controller_recorded_tas.c index c55c533..83b982b 100644 --- a/src/pc/controller/controller_recorded_tas.c +++ b/src/pc/controller/controller_recorded_tas.c @@ -23,7 +23,10 @@ static void tas_read(OSContPad *pad) { if (fd != 0) { uint8_t bytes[4] = {0}; int result = read(fd, bytes, 4); - assert(result > 0); + if (result < 0) { + return; + } + pad->button = (bytes[0] << 8) | bytes[1]; pad->stick_x = bytes[2]; pad->stick_y = bytes[3]; diff --git a/src/pc/controller/controller_tas_recorder.c b/src/pc/controller/controller_tas_recorder.c new file mode 100644 index 0000000..d1c95a3 --- /dev/null +++ b/src/pc/controller/controller_tas_recorder.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "controller_api.h" + +static FILE *fout; +static int counter; + +#define OFFSET 0x400 +#define fname "rec.m64" + +static void tas_recorder_close(void) { + fclose(fout); +} + +static void tas_recorder_init(void) { + if (fname == NULL) { + fout = NULL; + return; + } + + unlink(fname); + printf("[tas_recorder] writing output to %s\n", fname); + fout = fopen(fname, "wb"); + assert(fout != NULL); + uint8_t buf[OFFSET]; + memset(buf, 0, sizeof(buf)); + fwrite(buf, 1, sizeof(buf), fout); + atexit(tas_recorder_close); +} + +static void tas_recorder_read(OSContPad *pad) { + if (fout == NULL) { + return; + } + + uint8_t bytes[4] = {0}; + int button1 = pad->button; + int button2 = pad->button; + bytes[0] = button1 >> 8; + bytes[1] = button2 & 0x00FF; + bytes[2] = pad->stick_x; + bytes[3] = pad->stick_y; + fwrite(bytes, 1, 4, fout); +} + +struct ControllerAPI controller_tas_recorder = { + tas_recorder_init, + tas_recorder_read +}; diff --git a/src/pc/controller/controller_tas_recorder.h b/src/pc/controller/controller_tas_recorder.h new file mode 100644 index 0000000..8ceccf8 --- /dev/null +++ b/src/pc/controller/controller_tas_recorder.h @@ -0,0 +1,8 @@ +#ifndef CONTROLLER_TAS_RECORDER_H +#define CONTROLLER_TAS_RECORDER_H + +#include "controller_api.h" + +extern struct ControllerAPI controller_tas_recorder; + +#endif From cf7c89e31f453ef0c48967f21375d6a0ae2d1620 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 08:47:25 -0400 Subject: [PATCH 03/28] let tas playback and input coexist --- src/pc/controller/controller_entry_point.c | 6 +-- src/pc/controller/controller_recorded_tas.c | 47 ++++++++++++--------- src/pc/controller/controller_tas_recorder.c | 3 +- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index a99b5e5..5daed3c 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -8,9 +8,9 @@ static struct ControllerAPI *controller_implementations[] = { &controller_recorded_tas, - /* &controller_sdl, */ - /* &controller_keyboard, */ - /* &controller_tas_recorder, */ + &controller_sdl, + &controller_keyboard, + &controller_tas_recorder, }; s32 osContInit(OSMesgQueue *mq, u8 *controllerBits, OSContStatus *status) { diff --git a/src/pc/controller/controller_recorded_tas.c b/src/pc/controller/controller_recorded_tas.c index 83b982b..af6bf43 100644 --- a/src/pc/controller/controller_recorded_tas.c +++ b/src/pc/controller/controller_recorded_tas.c @@ -5,35 +5,44 @@ #include "controller_api.h" -static int fd; +static FILE *fin; static int counter; #define OFFSET 0x400 +#define fname "cont.m64" static void tas_init(void) { - fd = open("cont.m64", O_RDONLY); - if (fd != 0) { - uint8_t buf[OFFSET]; - read(fd, buf, sizeof(buf)); - counter = 0; + fin = fopen(fname, "rb"); + if (fin == NULL) { + return; } + + printf("[tas_playback] loading %s\n", fname); + uint8_t buf[OFFSET]; + fread(buf, 1, sizeof(buf), fin); + counter = 0; } static void tas_read(OSContPad *pad) { - if (fd != 0) { - uint8_t bytes[4] = {0}; - int result = read(fd, bytes, 4); - if (result < 0) { - return; - } - - pad->button = (bytes[0] << 8) | bytes[1]; - pad->stick_x = bytes[2]; - pad->stick_y = bytes[3]; - counter+=4; - printf("%x called %04x %d %d\n", (counter + OFFSET), pad->button, bytes[2], bytes[3]); - fflush(stdout); + if (fin == NULL) { + return; } + + uint8_t bytes[4] = {0}; + int result = fread(bytes, 1, 4, fin); + if (feof(fin)) { + printf("[tas_playback] end of tas input\n"); + fclose(fin); + fin = NULL; + return; + } + + pad->button = (bytes[0] << 8) | bytes[1]; + pad->stick_x = bytes[2]; + pad->stick_y = bytes[3]; + counter+=4; + printf("[tas_playback] %08x called %04x %02x%02x\n", (counter + OFFSET), pad->button, bytes[2], bytes[3]); + fflush(stdout); } struct ControllerAPI controller_recorded_tas = { diff --git a/src/pc/controller/controller_tas_recorder.c b/src/pc/controller/controller_tas_recorder.c index d1c95a3..6bd1c99 100644 --- a/src/pc/controller/controller_tas_recorder.c +++ b/src/pc/controller/controller_tas_recorder.c @@ -12,7 +12,8 @@ static int counter; #define fname "rec.m64" static void tas_recorder_close(void) { - fclose(fout); + fclose(fout); + printf("[tas_recorder] saving tas data to %s\n", fname); } static void tas_recorder_init(void) { From a08fffa9a40608139b8e31982ac1cd1eb97b270d Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 16:10:51 -0400 Subject: [PATCH 04/28] pin nixpkgs --- nix/sources.json | 26 +++++++++ nix/sources.nix | 134 +++++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 14 ++++- 3 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 nix/sources.json create mode 100644 nix/sources.nix diff --git a/nix/sources.json b/nix/sources.json new file mode 100644 index 0000000..45710de --- /dev/null +++ b/nix/sources.json @@ -0,0 +1,26 @@ +{ + "niv": { + "branch": "master", + "description": "Easy dependency management for Nix projects", + "homepage": "https://github.com/nmattia/niv", + "owner": "nmattia", + "repo": "niv", + "rev": "f73bf8d584148677b01859677a63191c31911eae", + "sha256": "0jlmrx633jvqrqlyhlzpvdrnim128gc81q5psz2lpp2af8p8q9qs", + "type": "tarball", + "url": "https://github.com/nmattia/niv/archive/f73bf8d584148677b01859677a63191c31911eae.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + }, + "nixpkgs": { + "branch": "nixpkgs-unstable", + "description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to", + "homepage": "https://github.com/NixOS/nixpkgs", + "owner": "NixOS", + "repo": "nixpkgs-channels", + "rev": "5f14d99efed32721172a819b6e78a5520bab4bc6", + "sha256": "1nxqbcsc8bfmwy450pv6s12nbvzqxai5mr6v41y478pya26lb108", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs-channels/archive/5f14d99efed32721172a819b6e78a5520bab4bc6.tar.gz", + "url_template": "https://github.com///archive/.tar.gz" + } +} diff --git a/nix/sources.nix b/nix/sources.nix new file mode 100644 index 0000000..8a725cb --- /dev/null +++ b/nix/sources.nix @@ -0,0 +1,134 @@ +# This file has been generated by Niv. + +let + + # + # The fetchers. fetch_ fetches specs of type . + # + + fetch_file = pkgs: spec: + if spec.builtin or true then + builtins_fetchurl { inherit (spec) url sha256; } + else + pkgs.fetchurl { inherit (spec) url sha256; }; + + fetch_tarball = pkgs: spec: + if spec.builtin or true then + builtins_fetchTarball { inherit (spec) url sha256; } + else + pkgs.fetchzip { inherit (spec) url sha256; }; + + fetch_git = spec: + builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; }; + + fetch_builtin-tarball = spec: + builtins.trace + '' + WARNING: + The niv type "builtin-tarball" will soon be deprecated. You should + instead use `builtin = true`. + + $ niv modify -a type=tarball -a builtin=true + '' + builtins_fetchTarball { inherit (spec) url sha256; }; + + fetch_builtin-url = spec: + builtins.trace + '' + WARNING: + The niv type "builtin-url" will soon be deprecated. You should + instead use `builtin = true`. + + $ niv modify -a type=file -a builtin=true + '' + (builtins_fetchurl { inherit (spec) url sha256; }); + + # + # Various helpers + # + + # The set of packages used when specs are fetched using non-builtins. + mkPkgs = sources: + let + sourcesNixpkgs = + import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {}; + hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; + hasThisAsNixpkgsPath = == ./.; + in + if builtins.hasAttr "nixpkgs" sources + then sourcesNixpkgs + else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then + import {} + else + abort + '' + Please specify either (through -I or NIX_PATH=nixpkgs=...) or + add a package called "nixpkgs" to your sources.json. + ''; + + # The actual fetching function. + fetch = pkgs: name: spec: + + if ! builtins.hasAttr "type" spec then + abort "ERROR: niv spec ${name} does not have a 'type' attribute" + else if spec.type == "file" then fetch_file pkgs spec + else if spec.type == "tarball" then fetch_tarball pkgs spec + else if spec.type == "git" then fetch_git spec + else if spec.type == "builtin-tarball" then fetch_builtin-tarball spec + else if spec.type == "builtin-url" then fetch_builtin-url spec + else + abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; + + # Ports of functions for older nix versions + + # a Nix version of mapAttrs if the built-in doesn't exist + mapAttrs = builtins.mapAttrs or ( + f: set: with builtins; + listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) + ); + + # fetchTarball version that is compatible between all the versions of Nix + builtins_fetchTarball = { url, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchTarball; + in + if lessThan nixVersion "1.12" then + fetchTarball { inherit url; } + else + fetchTarball attrs; + + # fetchurl version that is compatible between all the versions of Nix + builtins_fetchurl = { url, sha256 }@attrs: + let + inherit (builtins) lessThan nixVersion fetchurl; + in + if lessThan nixVersion "1.12" then + fetchurl { inherit url; } + else + fetchurl attrs; + + # Create the final "sources" from the config + mkSources = config: + mapAttrs ( + name: spec: + if builtins.hasAttr "outPath" spec + then abort + "The values in sources.json should not have an 'outPath' attribute" + else + spec // { outPath = fetch config.pkgs name spec; } + ) config.sources; + + # The "config" used by the fetchers + mkConfig = + { sourcesFile ? ./sources.json + , sources ? builtins.fromJSON (builtins.readFile sourcesFile) + , pkgs ? mkPkgs sources + }: rec { + # The sources, i.e. the attribute set of spec name to spec + inherit sources; + + # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers + inherit pkgs; + }; +in +mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/shell.nix b/shell.nix index c611f0b..d6583eb 100644 --- a/shell.nix +++ b/shell.nix @@ -1,7 +1,9 @@ -let pkgs = import { }; +let + sources = import ./nix/sources.nix; + pkgs = import sources.nixpkgs { }; in pkgs.mkShell { buildInputs = with pkgs; [ - gnumake + # Mario 64 python3 audiofile pkg-config @@ -15,6 +17,14 @@ in pkgs.mkShell { alsaLib glfw libGL + + # build tools + gnumake clang_10 + + # gamebridge + rustc + cargo + rls ]; } From cc11d483cb8a60ffd8239bcdf7df5d43a493095e Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 16:30:43 -0400 Subject: [PATCH 05/28] start game bridge code --- doc/gamebridge.org | 47 ++++++++++++++++++++++++++++++++++++++++++ gamebridge/.gitignore | 1 + gamebridge/Cargo.toml | 9 ++++++++ gamebridge/src/main.rs | 3 +++ 4 files changed, 60 insertions(+) create mode 100644 doc/gamebridge.org create mode 100644 gamebridge/.gitignore create mode 100644 gamebridge/Cargo.toml create mode 100644 gamebridge/src/main.rs diff --git a/doc/gamebridge.org b/doc/gamebridge.org new file mode 100644 index 0000000..d2bdfc5 --- /dev/null +++ b/doc/gamebridge.org @@ -0,0 +1,47 @@ +#+TITLE: gamebridge + +An interface between Super Mario 64 and just about anything else your heart +desires. + +* High level ideas + +the rust program has two threads, one is getting the inputs from $SOMEWHERE and +the other is doing the file I/O to the game \\ +the game bridge thread has two file descriptors open, a fifo for feeding inputs +to the game and a fifo opened by the game for signaling vblanks \\ +the game will then write a readiness signal to the rust program and read the +button data fifo \\ +then things will progress normally \\ +the getting the input thread of the rust program will have some logic for +telling how long ago it got liveness of the input source (explicitly vaguely +defined to allow a controller to sit there and do nothing as long as it still +exists), and then "sticking" it until, say, 10 frames have passed and then it +will block infinitely, freezing the game in place + +* Goals + ++ Blocking, synchronous rust as much as possible ++ unix fifos are great, let's use them + +* Protocol + +The protocol between the game and the bridge will be as follows (based on the +[[http://tasvideos.org/EmulatorResources/Mupen/M64.html][Mupen64 demo format]]). Two unix fifos will be created by the bridge. + +The game and the bridge will both need to take care that the files are opened in +*unbuffered I/O modes*. This can be done with +=setvbuf( f, (char *)NULL, _IONBF, 0 );= in C and is the default in Rust. + +The first one will be called =vblank= and will then be opened by the game in +write mode when it starts. The bridge will open this fifo in read mode. + +The second one will be called =input= and will be opened by the game in read +mode. The bridge will open this fifo in write mode. + +On every frame, the game will write the text ="OK\n"= to the vblank fifo. This +will signal the bridge that it should write four bytes of data to the input +fifo, conforming to the [[http://tasvideos.org/EmulatorResources/Mupen/M64.html#ControllerData][Controller Data]] specification of the Mupen64 format. +This data will be interpreted by the game as actions for Mario to take. + +The bridge *MUST* block on waiting for the vblank fifo to be written to by the +game and the game *MUST* block on the input fifo being written to by the bridge. diff --git a/gamebridge/.gitignore b/gamebridge/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/gamebridge/.gitignore @@ -0,0 +1 @@ +/target diff --git a/gamebridge/Cargo.toml b/gamebridge/Cargo.toml new file mode 100644 index 0000000..be21824 --- /dev/null +++ b/gamebridge/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "gamebridge" +version = "0.1.0" +authors = ["Christine Dodrill "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/gamebridge/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 7be9a1aef640f0976cc1890eac1d9675b3a37e33 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 16:59:08 -0400 Subject: [PATCH 06/28] implement gamebridge in the game code --- src/pc/configfile.c | 2 + src/pc/configfile.h | 1 + src/pc/controller/controller_entry_point.c | 2 + src/pc/controller/controller_gamebridge.c | 69 ++++++++++++++++++++++ src/pc/controller/controller_gamebridge.h | 8 +++ 5 files changed, 82 insertions(+) create mode 100644 src/pc/controller/controller_gamebridge.c create mode 100644 src/pc/controller/controller_gamebridge.h diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 10ff1e6..b8b1b4b 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -29,6 +29,7 @@ struct ConfigOption { /* *Config options and default values */ +bool configGameBridge = false; bool configFullscreen = false; // Keyboard mappings (scancode values) unsigned int configKeyA = 0x26; @@ -47,6 +48,7 @@ unsigned int configKeyStickRight = 0x20; static const struct ConfigOption options[] = { + {.name = "gamebridge", .type = CONFIG_TYPE_BOOL, .boolValue = &configGameBridge}, {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen}, {.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA}, {.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB}, diff --git a/src/pc/configfile.h b/src/pc/configfile.h index ae9070b..7e55613 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -1,6 +1,7 @@ #ifndef CONFIGFILE_H #define CONFIGFILE_H +extern bool configGameBridge; extern bool configFullscreen; extern unsigned int configKeyA; extern unsigned int configKeyB; diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index faf91f5..be8e773 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -5,11 +5,13 @@ #include "controller_keyboard.h" #include "controller_sdl.h" +#include "controller_gamebridge.h" static struct ControllerAPI *controller_implementations[] = { &controller_recorded_tas, &controller_sdl, &controller_keyboard, + &controller_gamebridge, }; s32 osContInit(OSMesgQueue *mq, u8 *controllerBits, OSContStatus *status) { diff --git a/src/pc/controller/controller_gamebridge.c b/src/pc/controller/controller_gamebridge.c new file mode 100644 index 0000000..222dfe4 --- /dev/null +++ b/src/pc/controller/controller_gamebridge.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "controller_api.h" +#include "../configfile.h" + +static FILE *vblank; +static FILE *input; + +#define vblank_fname "vblank" +#define input_fname "input" + +static void gamebridge_init(void) { + if (!configGameBridge) { + return; + } + + unlink(vblank_fname); + unlink(input_fname); + + int result; + + result = mkfifo(vblank_fname, S_IRUSR|S_IWUSR); + if (result < 0) { + perror("mkfifo "vblank_fname); + assert(result < 0); + } + + result = mkfifo(input_fname, S_IRUSR| S_IWUSR); + if (result < 0) { + perror("mkfifo "input_fname); + assert(result < 0); + } + + vblank = fopen(vblank_fname, "w"); + input = fopen(input_fname, "rb"); + assert(vblank); + assert(input); + + setvbuf(vblank, NULL, _IONBF, 0); + setvbuf(input, NULL, _IONBF, 0); +} + +static void gamebridge_read(OSContPad *pad) { + if (!configGameBridge) { + return; + } + + char* ok = "OK\n"; + fwrite(ok, 1, sizeof(ok), vblank); + uint8_t bytes[4] = {0}; + fread(bytes, 1, 4, input); + pad->button = (bytes[0] << 8) | bytes[1]; + pad->stick_x = bytes[2]; + pad->stick_y = bytes[3]; +} + +struct ControllerAPI controller_gamebridge = { + gamebridge_init, + gamebridge_read +}; diff --git a/src/pc/controller/controller_gamebridge.h b/src/pc/controller/controller_gamebridge.h new file mode 100644 index 0000000..cae2a4c --- /dev/null +++ b/src/pc/controller/controller_gamebridge.h @@ -0,0 +1,8 @@ +#ifndef CONTROLLER_GAMEBRIDGE_H +#define CONTROLLER_GAMEBRIDGE_H + +#include "controller_api.h" + +extern struct ControllerAPI controller_gamebridge; + +#endif From a8ecacd2ca818c9730b3f3352d3cf175589987a2 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 17:54:57 -0400 Subject: [PATCH 07/28] gamebridge: implement simple protocol --- .gitignore | 4 + Cargo.lock | 183 ++++++++++++++++++++++ Cargo.toml | 2 + doc/gamebridge.org | 9 +- gamebridge/Cargo.toml | 3 + gamebridge/src/main.rs | 30 +++- shell.nix | 2 + src/pc/controller/controller_gamebridge.c | 24 ++- 8 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml diff --git a/.gitignore b/.gitignore index 6c157d2..701e4ef 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,7 @@ sm64config.txt !/sound/**/*custom*/**/*.aiff !/assets/**/*custom*.bin !/assets/**/*custom*/**/*.bin + +/target +vblank +input diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4c79be1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,183 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "gamebridge" +version = "0.1.0" +dependencies = [ + "anyhow", + "log", + "pretty_env_logger", +] + +[[package]] +name = "hermit-abi" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "regex" +version = "1.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" + +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..62d61f0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = [ "./gamebridge" ] diff --git a/doc/gamebridge.org b/doc/gamebridge.org index d2bdfc5..2c2ebcf 100644 --- a/doc/gamebridge.org +++ b/doc/gamebridge.org @@ -30,7 +30,7 @@ The protocol between the game and the bridge will be as follows (based on the The game and the bridge will both need to take care that the files are opened in *unbuffered I/O modes*. This can be done with -=setvbuf( f, (char *)NULL, _IONBF, 0 );= in C and is the default in Rust. +=setvbuf(f, (char *)NULL, _IONBF, 0);= in C and is the default in Rust. The first one will be called =vblank= and will then be opened by the game in write mode when it starts. The bridge will open this fifo in read mode. @@ -38,10 +38,13 @@ write mode when it starts. The bridge will open this fifo in read mode. The second one will be called =input= and will be opened by the game in read mode. The bridge will open this fifo in write mode. -On every frame, the game will write the text ="OK\n"= to the vblank fifo. This -will signal the bridge that it should write four bytes of data to the input +On every frame, the game *MUST* write the text ="OK\n"= to the vblank fifo. This +will signal the bridge that it *MUST* write four bytes of data to the input fifo, conforming to the [[http://tasvideos.org/EmulatorResources/Mupen/M64.html#ControllerData][Controller Data]] specification of the Mupen64 format. This data will be interpreted by the game as actions for Mario to take. The bridge *MUST* block on waiting for the vblank fifo to be written to by the game and the game *MUST* block on the input fifo being written to by the bridge. + +When the game is exiting, the game *SHOULD* write ="BYE"= to the vblank fifo. +When the bridge recieves a ="BYE"= message, it *MUST* exit. diff --git a/gamebridge/Cargo.toml b/gamebridge/Cargo.toml index be21824..7bc5eff 100644 --- a/gamebridge/Cargo.toml +++ b/gamebridge/Cargo.toml @@ -7,3 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0" +log = "0.4" +pretty_env_logger = "0.4" diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index e7a11a9..c270d81 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -1,3 +1,29 @@ -fn main() { - println!("Hello, world!"); +use log::{debug, info, warn}; +use std::{ + fs::{File, OpenOptions}, + io::{Read, Write}, + str::from_utf8, +}; + +fn main() -> anyhow::Result<()> { + pretty_env_logger::try_init()?; + let controller_data = [0; 4]; + + let mut vblank = File::open("vblank")?; + let mut input = OpenOptions::new().write(true).open("input")?; + + info!("[gamebridge::rust] ready"); + + loop { + let mut data = [0; 3]; + info!("waiting for vblank"); + vblank.read(&mut data)?; + let str = from_utf8(&data)?; + debug!("got data: {}", str); + if str == "BYE" { + warn!("asked to exit by the game"); + return Ok(()); + } + input.write(&controller_data)?; + } } diff --git a/shell.nix b/shell.nix index d6583eb..feaec63 100644 --- a/shell.nix +++ b/shell.nix @@ -21,10 +21,12 @@ in pkgs.mkShell { # build tools gnumake clang_10 + gdb # gamebridge rustc cargo rls + rustfmt ]; } diff --git a/src/pc/controller/controller_gamebridge.c b/src/pc/controller/controller_gamebridge.c index 222dfe4..a08daf8 100644 --- a/src/pc/controller/controller_gamebridge.c +++ b/src/pc/controller/controller_gamebridge.c @@ -17,12 +17,21 @@ static FILE *input; #define vblank_fname "vblank" #define input_fname "input" +#define ok "OK\n" +#define bye "BYE" + +static void gamebridge_close(void) { + fwrite(bye, 1, strlen(bye), vblank); +} static void gamebridge_init(void) { if (!configGameBridge) { return; } + printf("[gamebridge] starting...\n"); + fflush(stdout); + unlink(vblank_fname); unlink(input_fname); @@ -40,13 +49,18 @@ static void gamebridge_init(void) { assert(result < 0); } - vblank = fopen(vblank_fname, "w"); - input = fopen(input_fname, "rb"); + vblank = fopen(vblank_fname, "w+"); + input = fopen(input_fname, "rb+"); assert(vblank); assert(input); setvbuf(vblank, NULL, _IONBF, 0); setvbuf(input, NULL, _IONBF, 0); + + printf("[gamebridge] starting rust daemon\n"); + fflush(stdout); + system("./target/debug/gamebridge &"); + atexit(gamebridge_close); } static void gamebridge_read(OSContPad *pad) { @@ -54,13 +68,15 @@ static void gamebridge_read(OSContPad *pad) { return; } - char* ok = "OK\n"; - fwrite(ok, 1, sizeof(ok), vblank); + printf("[gamebridge] waiting for input\n"); + fwrite(ok, 1, strlen(ok), vblank); uint8_t bytes[4] = {0}; fread(bytes, 1, 4, input); pad->button = (bytes[0] << 8) | bytes[1]; pad->stick_x = bytes[2]; pad->stick_y = bytes[3]; + printf("[gamebridge] %02x%02x %02x%02x\n", bytes[0], bytes[1], bytes[2], bytes[3]); + fflush(stdout); } struct ControllerAPI controller_gamebridge = { From 0f3a72ac7869f7c112f78d26307a3275bc9470a6 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 18:00:20 -0400 Subject: [PATCH 08/28] remove gamebridge tag --- gamebridge/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index c270d81..da2de5b 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -12,7 +12,7 @@ fn main() -> anyhow::Result<()> { let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; - info!("[gamebridge::rust] ready"); + info!("ready"); loop { let mut data = [0; 3]; From 1d0da2661faeecfe2352b44b132108304864bc57 Mon Sep 17 00:00:00 2001 From: Cadey Ratio Date: Fri, 8 May 2020 22:03:34 +0000 Subject: [PATCH 09/28] Update 'doc/gamebridge.org' --- doc/gamebridge.org | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/gamebridge.org b/doc/gamebridge.org index 2c2ebcf..77c6c35 100644 --- a/doc/gamebridge.org +++ b/doc/gamebridge.org @@ -6,12 +6,16 @@ desires. * High level ideas the rust program has two threads, one is getting the inputs from $SOMEWHERE and -the other is doing the file I/O to the game \\ +the other is doing the file I/O to the game + the game bridge thread has two file descriptors open, a fifo for feeding inputs -to the game and a fifo opened by the game for signaling vblanks \\ +to the game and a fifo opened by the game for signaling vblanks + the game will then write a readiness signal to the rust program and read the -button data fifo \\ -then things will progress normally \\ +button data fifo + +then things will progress normally + the getting the input thread of the rust program will have some logic for telling how long ago it got liveness of the input source (explicitly vaguely defined to allow a controller to sit there and do nothing as long as it still From 1fd397d89c34a10f4b14a5a4059d667b80798609 Mon Sep 17 00:00:00 2001 From: Cadey Ratio Date: Fri, 8 May 2020 22:03:57 +0000 Subject: [PATCH 10/28] Update 'gamebridge/Cargo.toml' --- gamebridge/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamebridge/Cargo.toml b/gamebridge/Cargo.toml index 7bc5eff..5f31f62 100644 --- a/gamebridge/Cargo.toml +++ b/gamebridge/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gamebridge" version = "0.1.0" -authors = ["Christine Dodrill "] +authors = ["Cadey Ratio "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 4357cd04943f614b03220103f9d10fb42c3e852c Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 18:18:45 -0400 Subject: [PATCH 11/28] proof of concept: pipe a demo into the game --- .gitignore | 1 + gamebridge/src/main.rs | 11 +++++++++-- src/pc/controller/controller_gamebridge.c | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 701e4ef..cf0d4e7 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,4 @@ sm64config.txt /target vblank input +*.m64 diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index da2de5b..51ffc6e 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -7,16 +7,22 @@ use std::{ fn main() -> anyhow::Result<()> { pretty_env_logger::try_init()?; - let controller_data = [0; 4]; + let mut controller_data = [0; 4]; let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; + let mut demofile = File::open("demo.m64")?; + + { + let mut buf = [0; 1024]; + demofile.read(&mut buf)?; + } info!("ready"); loop { let mut data = [0; 3]; - info!("waiting for vblank"); + debug!("waiting for vblank"); vblank.read(&mut data)?; let str = from_utf8(&data)?; debug!("got data: {}", str); @@ -24,6 +30,7 @@ fn main() -> anyhow::Result<()> { warn!("asked to exit by the game"); return Ok(()); } + demofile.read(&mut controller_data)?; input.write(&controller_data)?; } } diff --git a/src/pc/controller/controller_gamebridge.c b/src/pc/controller/controller_gamebridge.c index a08daf8..a6eeb89 100644 --- a/src/pc/controller/controller_gamebridge.c +++ b/src/pc/controller/controller_gamebridge.c @@ -21,7 +21,17 @@ static FILE *input; #define bye "BYE" static void gamebridge_close(void) { + if (!configGameBridge) { + return; + } + + printf("[gamebridge] exiting\n"); fwrite(bye, 1, strlen(bye), vblank); + fclose(vblank); + fclose(input); + + unlink(vblank_fname); + unlink(input_fname); } static void gamebridge_init(void) { @@ -68,14 +78,14 @@ static void gamebridge_read(OSContPad *pad) { return; } - printf("[gamebridge] waiting for input\n"); + //printf("[gamebridge] waiting for input\n"); fwrite(ok, 1, strlen(ok), vblank); uint8_t bytes[4] = {0}; fread(bytes, 1, 4, input); pad->button = (bytes[0] << 8) | bytes[1]; pad->stick_x = bytes[2]; pad->stick_y = bytes[3]; - printf("[gamebridge] %02x%02x %02x%02x\n", bytes[0], bytes[1], bytes[2], bytes[3]); + //printf("[gamebridge] %02x%02x %02x%02x\n", bytes[0], bytes[1], bytes[2], bytes[3]); fflush(stdout); } From 70338a9b47019ebdf89b2d88bd3e124694eddd72 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 18:32:29 -0400 Subject: [PATCH 12/28] gamebridge: use a match here --- gamebridge/src/main.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 51ffc6e..d91f73c 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -1,22 +1,17 @@ -use log::{debug, info, warn}; +use anyhow::{anyhow, Result}; +use log::{debug, error, info, warn}; use std::{ fs::{File, OpenOptions}, io::{Read, Write}, str::from_utf8, }; -fn main() -> anyhow::Result<()> { +fn main() -> Result<()> { pretty_env_logger::try_init()?; let mut controller_data = [0; 4]; let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; - let mut demofile = File::open("demo.m64")?; - - { - let mut buf = [0; 1024]; - demofile.read(&mut buf)?; - } info!("ready"); @@ -26,11 +21,17 @@ fn main() -> anyhow::Result<()> { vblank.read(&mut data)?; let str = from_utf8(&data)?; debug!("got data: {}", str); - if str == "BYE" { - warn!("asked to exit by the game"); - return Ok(()); - } - demofile.read(&mut controller_data)?; - input.write(&controller_data)?; + + match str { + "OK\n" => input.write(&controller_data)?, + "BYE" => { + warn!("asked to exit by the game"); + return Ok(()); + }, + _ => { + error!("got unknown FIFO data {}", str); + return Err(anyhow!("unknown FIFO data received")); + } + }; } } From f477907e1eb688da72f364cfc06e5236273d78cc Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 18:35:15 -0400 Subject: [PATCH 13/28] controller: newline before exit message --- src/pc/controller/controller_gamebridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pc/controller/controller_gamebridge.c b/src/pc/controller/controller_gamebridge.c index a6eeb89..65809a0 100644 --- a/src/pc/controller/controller_gamebridge.c +++ b/src/pc/controller/controller_gamebridge.c @@ -25,7 +25,7 @@ static void gamebridge_close(void) { return; } - printf("[gamebridge] exiting\n"); + printf("\n[gamebridge] exiting\n"); fwrite(bye, 1, strlen(bye), vblank); fclose(vblank); fclose(input); From 26b6c1dfd3936b72a614029ec8e6a91b8a16dd84 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 19:17:41 -0400 Subject: [PATCH 14/28] gamebridge: press a and start forever (a long time!) --- Cargo.lock | 7 +++ gamebridge/Cargo.toml | 1 + gamebridge/src/controller.rs | 86 ++++++++++++++++++++++++++++++++++++ gamebridge/src/main.rs | 40 +++++++++++++++-- 4 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 gamebridge/src/controller.rs diff --git a/Cargo.lock b/Cargo.lock index 4c79be1..4853d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "cfg-if" version = "0.1.10" @@ -50,6 +56,7 @@ name = "gamebridge" version = "0.1.0" dependencies = [ "anyhow", + "bitflags", "log", "pretty_env_logger", ] diff --git a/gamebridge/Cargo.toml b/gamebridge/Cargo.toml index 5f31f62..155c798 100644 --- a/gamebridge/Cargo.toml +++ b/gamebridge/Cargo.toml @@ -8,5 +8,6 @@ edition = "2018" [dependencies] anyhow = "1.0" +bitflags = "1.2" log = "0.4" pretty_env_logger = "0.4" diff --git a/gamebridge/src/controller.rs b/gamebridge/src/controller.rs new file mode 100644 index 0000000..d0b0e6b --- /dev/null +++ b/gamebridge/src/controller.rs @@ -0,0 +1,86 @@ +bitflags! { + // 0x0100 Digital Pad Right + // 0x0200 Digital Pad Left + // 0x0400 Digital Pad Down + // 0x0800 Digital Pad Up + // 0x1000 Start + // 0x2000 Z + // 0x4000 B + // 0x8000 A + pub(crate) struct HiButtons: u8 { + const NONE = 0x00; + const DPAD_RIGHT = 0x01; + const DPAD_LEFT = 0x02; + const DPAD_DOWN = 0x04; + const DPAD_UP = 0x08; + const START = 0x10; + const Z_BUTTON = 0x20; + const B_BUTTON = 0x40; + const A_BUTTON = 0x80; + } +} + +impl HiButtons { + pub fn clear(&mut self) { + self.bits = 0; + } +} + +bitflags! { + // 0x0001 C-Right + // 0x0002 C-Left + // 0x0004 C-Down + // 0x0008 C-Up + // 0x0010 R + // 0x0020 L + // 0x0040 (reserved) + // 0x0080 (reserved) + pub(crate) struct LoButtons: u8 { + const NONE = 0x00; + const C_RIGHT = 0x01; + const C_LEFT = 0x02; + const C_DOWN = 0x04; + const C_UP = 0x08; + const R_BUTTON = 0x10; + const L_BUTTON = 0x20; + } +} + +impl LoButtons { + pub fn clear(&mut self) { + self.bits = 0; + } +} + +pub(crate) fn test(st: crate::MTState) { + let mut lo: LoButtons = LoButtons::NONE; + let mut hi: HiButtons = HiButtons::NONE; + + loop { + use std::{thread::sleep, time::Duration}; + let one_second = Duration::new(1, 0); + + hi = HiButtons::A_BUTTON | HiButtons::START; + + { + println!("pressing a + start"); + let mut data = st.write().unwrap(); + data.controller[0] = hi.bits as u8; + data.controller[1] = lo.bits as u8; + } + + sleep(one_second); + + hi.clear(); + lo.clear(); + + { + println!("releasing a + start"); + let mut data = st.write().unwrap(); + data.controller[0] = hi.bits as u8; + data.controller[1] = lo.bits as u8; + } + + sleep(one_second); + } +} diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index d91f73c..12410d9 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -1,20 +1,51 @@ +#[macro_use] +extern crate bitflags; + +pub(crate) mod controller; + use anyhow::{anyhow, Result}; use log::{debug, error, info, warn}; use std::{ fs::{File, OpenOptions}, io::{Read, Write}, str::from_utf8, + sync::{Arc, RwLock}, + thread::spawn, + time::Instant, }; +#[derive(Debug)] +pub(crate) struct State { + controller: [u8; 4], + last_got: Box, + ok: bool, +} + +pub(crate) type MTState = Arc>; + fn main() -> Result<()> { pretty_env_logger::try_init()?; - let mut controller_data = [0; 4]; let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; + let st = { + let st = State { + controller: [0; 4], + last_got: Box::new(Instant::now()), + ok: true, + }; + + Arc::new(RwLock::new(st)) + }; + info!("ready"); + { + let st = st.clone(); + spawn(move || controller::test(st)); + } + loop { let mut data = [0; 3]; debug!("waiting for vblank"); @@ -23,11 +54,14 @@ fn main() -> Result<()> { debug!("got data: {}", str); match str { - "OK\n" => input.write(&controller_data)?, + "OK\n" => { + let data = st.read().unwrap(); + input.write(&data.controller)? + } "BYE" => { warn!("asked to exit by the game"); return Ok(()); - }, + } _ => { error!("got unknown FIFO data {}", str); return Err(anyhow!("unknown FIFO data received")); From afd84937a19df640c92d1e294175f5ba590baad1 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 19:23:56 -0400 Subject: [PATCH 15/28] gamebridge: clear the controller every input frame --- gamebridge/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 12410d9..5967d53 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -55,8 +55,9 @@ fn main() -> Result<()> { match str { "OK\n" => { - let data = st.read().unwrap(); - input.write(&data.controller)? + let mut data = st.write().unwrap(); + input.write(&data.controller)?; + data.controller = [0; 4]; } "BYE" => { warn!("asked to exit by the game"); From 42e09e71890d3f2a8d0c636e791a5976f767a352 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 20:47:36 -0400 Subject: [PATCH 16/28] twitch control --- .gitignore | 1 + Cargo.lock | 681 ++++++++++++++++++++++++++++++++++- gamebridge/Cargo.toml | 3 + gamebridge/src/controller.rs | 4 +- gamebridge/src/main.rs | 7 +- gamebridge/src/twitch.rs | 131 +++++++ shell.nix | 1 + 7 files changed, 822 insertions(+), 6 deletions(-) create mode 100644 gamebridge/src/twitch.rs diff --git a/.gitignore b/.gitignore index cf0d4e7..303c45e 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ sm64config.txt vblank input *.m64 +.env diff --git a/Cargo.lock b/Cargo.lock index 4853d4c..c666a3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,12 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" +[[package]] +name = "arc-swap" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" + [[package]] name = "atty" version = "0.2.14" @@ -23,21 +29,64 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi", "libc", - "winapi", + "winapi 0.3.8", ] +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bytes" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" + +[[package]] +name = "cc" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" + [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + [[package]] name = "env_logger" version = "0.7.1" @@ -51,14 +100,126 @@ dependencies = [ "termcolor", ] +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + +[[package]] +name = "futures-sink" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project", + "pin-utils", +] + [[package]] name = "gamebridge" version = "0.1.0" dependencies = [ "anyhow", "bitflags", + "kankyo", "log", "pretty_env_logger", + "tokio", + "twitchchat", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -79,6 +240,31 @@ dependencies = [ "quick-error", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "kankyo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325a11231fa70c1d1b562655db757cefb6022876d62f173831f35bd670ae0c40" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -91,6 +277,15 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.8" @@ -106,6 +301,210 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +dependencies = [ + "log", + "mio", + "miow 0.3.3", + "winapi 0.3.8", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" +dependencies = [ + "socket2", + "winapi 0.3.8", +] + +[[package]] +name = "native-tls" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "openssl" +version = "0.10.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.8", +] + +[[package]] +name = "pin-project" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + [[package]] name = "pretty_env_logger" version = "0.4.0" @@ -116,12 +515,77 @@ dependencies = [ "log", ] +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +dependencies = [ + "unicode-xid", +] + [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quote" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + [[package]] name = "regex" version = "1.3.7" @@ -140,6 +604,119 @@ version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +[[package]] +name = "remove_dir_all" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi 0.3.8", +] + +[[package]] +name = "schannel" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" +dependencies = [ + "lazy_static", + "winapi 0.3.8", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.8", +] + [[package]] name = "termcolor" version = "1.1.0" @@ -158,6 +735,90 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "tokio" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.8", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "twitchchat" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "905384e7e60a8bb3a1a4528a62c97d4d39c6522bd6c2885b52e01acb710dd276" +dependencies = [ + "futures", + "log", + "native-tls", + "parking_lot", + "static_assertions", + "tokio", + "tokio-tls", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vcpkg" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.8" @@ -168,6 +829,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -180,7 +847,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.8", ] [[package]] @@ -188,3 +855,13 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] diff --git a/gamebridge/Cargo.toml b/gamebridge/Cargo.toml index 155c798..3d552a3 100644 --- a/gamebridge/Cargo.toml +++ b/gamebridge/Cargo.toml @@ -9,5 +9,8 @@ edition = "2018" [dependencies] anyhow = "1.0" bitflags = "1.2" +kankyo = "0.3" log = "0.4" pretty_env_logger = "0.4" +tokio = { version = "0.2", features = ["full", "macros"] } +twitchchat = "0.10" diff --git a/gamebridge/src/controller.rs b/gamebridge/src/controller.rs index d0b0e6b..3daa1d4 100644 --- a/gamebridge/src/controller.rs +++ b/gamebridge/src/controller.rs @@ -21,7 +21,7 @@ bitflags! { } impl HiButtons { - pub fn clear(&mut self) { + pub(crate) fn clear(&mut self) { self.bits = 0; } } @@ -47,7 +47,7 @@ bitflags! { } impl LoButtons { - pub fn clear(&mut self) { + pub(crate) fn clear(&mut self) { self.bits = 0; } } diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 5967d53..e4cebeb 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -2,6 +2,7 @@ extern crate bitflags; pub(crate) mod controller; +pub(crate) mod twitch; use anyhow::{anyhow, Result}; use log::{debug, error, info, warn}; @@ -25,6 +26,7 @@ pub(crate) type MTState = Arc>; fn main() -> Result<()> { pretty_env_logger::try_init()?; + kankyo::init()?; let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; @@ -43,7 +45,7 @@ fn main() -> Result<()> { { let st = st.clone(); - spawn(move || controller::test(st)); + spawn(move || twitch::run(st)); } loop { @@ -57,7 +59,8 @@ fn main() -> Result<()> { "OK\n" => { let mut data = st.write().unwrap(); input.write(&data.controller)?; - data.controller = [0; 4]; + data.controller[0] = 0; + data.controller[1] = 0; } "BYE" => { warn!("asked to exit by the game"); diff --git a/gamebridge/src/twitch.rs b/gamebridge/src/twitch.rs new file mode 100644 index 0000000..14b71bf --- /dev/null +++ b/gamebridge/src/twitch.rs @@ -0,0 +1,131 @@ +use crate::{ + controller::{HiButtons, LoButtons}, + MTState, +}; +use tokio::stream::StreamExt as _; +use twitchchat::{events, Control, Dispatcher, Runner, Status}; + +pub(crate) fn run(st: MTState) { + use tokio::runtime::Runtime; + Runtime::new() + .expect("Failed to create Tokio runtime") + .block_on(handle(st)); +} + +async fn handle(st: MTState) { + let (nick, pass) = ( + // twitch name + std::env::var("TWITCH_NICK").unwrap(), + // oauth token for twitch name + std::env::var("TWITCH_PASS").unwrap(), + ); + + // putting this in the env so people don't join my channel when running this + let channels = &[std::env::var("TWITCH_CHANNEL").unwrap()]; + + let dispatcher = Dispatcher::new(); + let (runner, control) = Runner::new(dispatcher.clone(), twitchchat::RateLimit::default()); + let fut = run_loop(control.clone(), dispatcher, channels, st); + + let conn = twitchchat::connect_easy_tls(&nick, &pass).await.unwrap(); + + tokio::select! { + _ = fut => { control.stop() } + status = runner.run(conn) => { + match status { + Ok(Status::Eof) => {} + Ok(Status::Canceled) => {} + Ok(Status::Timeout) => {} + Err(err) => panic!(err), + } + } + } +} + +async fn run_loop( + mut control: Control, + mut dispatcher: Dispatcher, + channels: &[String], + st: MTState, +) { + let mut join = dispatcher.subscribe::(); + let mut part = dispatcher.subscribe::(); + let mut pmsg = dispatcher.subscribe::(); + + async fn wait_and_join( + control: &mut Control, + dispatcher: &mut Dispatcher, + channels: &[String], + ) { + let ready = dispatcher.wait_for::().await.unwrap(); + eprintln!("our name: {}", ready.nickname); + + let w = control.writer(); + for channel in channels { + eprintln!("joining: {}", channel); + let _ = w.join(channel).await; + eprintln!("joined"); + } + eprintln!("joined all channels") + } + + wait_and_join(&mut control, &mut dispatcher, channels).await; + let mut stickx: i8 = 0; + let mut sticky: i8 = 0; + + loop { + tokio::select! { + Some(msg) = join.next() => { + eprintln!("{} joined {}", msg.name, msg.channel); + } + Some(msg) = part.next() => { + eprintln!("{} left {}", msg.name, msg.channel); + } + Some(msg) = pmsg.next() => { + let mut hi = HiButtons::NONE; + let mut lo = LoButtons::NONE; + + let mut data = msg.data.to_string(); + let data = data.to_ascii_lowercase(); + + match data.as_str() { + "a" => hi = hi | HiButtons::A_BUTTON, + "b" => hi = hi | HiButtons::B_BUTTON, + "z" => hi = hi | HiButtons::Z_BUTTON, + "r" => lo = lo | LoButtons::R_BUTTON, + "cup" => lo = lo | LoButtons::C_UP, + "cdown" => lo = lo | LoButtons::C_DOWN, + "cleft" => lo = lo | LoButtons::C_LEFT, + "cright" => lo = lo | LoButtons::C_RIGHT, + "start" => hi = hi | HiButtons::START, + "up" => sticky = 127, + "down" => sticky = -128, + "left" => stickx = -128, + "right" => stickx = 127, + "stop" => {stickx = 0; sticky = 0;}, + _ => {}, + } + + { + let mut data = st.write().unwrap(); + data.controller[0] = hi.bits() as u8; + data.controller[1] = lo.bits() as u8; + data.controller[2] = stickx as u8; + data.controller[3] = sticky as u8; + } + + eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data); + + match msg.data.split(" ").next() { + Some("!quit") => { + // causes the runner to shutdown + control.stop(); + } + _ => {} + } + } + + else => { break } + } + } +} diff --git a/shell.nix b/shell.nix index feaec63..5334d8d 100644 --- a/shell.nix +++ b/shell.nix @@ -17,6 +17,7 @@ in pkgs.mkShell { alsaLib glfw libGL + openssl # build tools gnumake From 2f5d5a16597dcd71905950a7c9b9faa857ae6c8b Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 21:25:25 -0400 Subject: [PATCH 17/28] gamebridge: remove killcmd --- gamebridge/src/twitch.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/gamebridge/src/twitch.rs b/gamebridge/src/twitch.rs index 14b71bf..3cc3138 100644 --- a/gamebridge/src/twitch.rs +++ b/gamebridge/src/twitch.rs @@ -115,14 +115,6 @@ async fn run_loop( } eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data); - - match msg.data.split(" ").next() { - Some("!quit") => { - // causes the runner to shutdown - control.stop(); - } - _ => {} - } } else => { break } From cafe38aae6c7fad56e03399e31f836ee76f97fe8 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 23:44:03 -0400 Subject: [PATCH 18/28] lerp debugging --- gamebridge/src/main.rs | 92 ++++++++++++++++++++++++++++++++++++++++ gamebridge/src/twitch.rs | 10 ++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index e4cebeb..426cd3e 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -20,6 +20,7 @@ pub(crate) struct State { controller: [u8; 4], last_got: Box, ok: bool, + frame: u64, } pub(crate) type MTState = Arc>; @@ -36,6 +37,7 @@ fn main() -> Result<()> { controller: [0; 4], last_got: Box::new(Instant::now()), ok: true, + frame: 0, }; Arc::new(RwLock::new(st)) @@ -48,6 +50,10 @@ fn main() -> Result<()> { spawn(move || twitch::run(st)); } + const LERP_TIME: f64 = 1.0; // 15 frames to lerp stick positions down to 0 + let mut xmax_frame: u64 = 0; + let mut ymax_frame: u64 = 0; + loop { let mut data = [0; 3]; debug!("waiting for vblank"); @@ -58,9 +64,56 @@ fn main() -> Result<()> { match str { "OK\n" => { let mut data = st.write().unwrap(); + data.frame += 1; + + let mut stickx = data.controller[2] as i8; + let mut sticky = data.controller[3] as i8; + + let dist = stick_distance(stickx, sticky); + if dist <= 10 { + stickx = 0; + sticky = 0; + xmax_frame = 0; + ymax_frame = 0; + } + + stickx = match stickx { + 0 => stickx, + 127 => { + xmax_frame = data.frame; + stickx - 10 + }, + -128 => { + xmax_frame = data.frame; + stickx + 10 + }, + _ => { + let t = (data.frame - xmax_frame) as f64 / (LERP_TIME as f64); + lerp(stickx, 0, t) + }, + }; + + sticky = match stickx { + 0 => sticky, + 127 => { + ymax_frame = data.frame; + sticky - 10 + }, + -128 => { + ymax_frame = data.frame; + sticky + 10 + }, + _ => { + let t = (data.frame - ymax_frame) as f64 / (LERP_TIME as f64); + lerp(sticky, 0, t) + }, + }; + input.write(&data.controller)?; data.controller[0] = 0; data.controller[1] = 0; + data.controller[2] = stickx as u8; + data.controller[3] = sticky as u8; } "BYE" => { warn!("asked to exit by the game"); @@ -73,3 +126,42 @@ fn main() -> Result<()> { }; } } + +fn lerp(start: i8, end: i8, t: f64) -> i8 { + (start as f64 * (1.0 - t) + (end as f64) * t) as i8 +} + +fn stick_distance(x: i8, y: i8) -> i8 { + let x = (x as f64).powi(2); + let y = (y as f64).powi(2); + (x + y).sqrt() as i8 +} + +#[cfg(test)] +mod test { + #[test] + fn lerp_scale() { + for case in [(0.1, 10), (0.5, 31)].iter() { + let t = case.0; + let start = 127.0 * t; + assert_eq!(crate::lerp(start as i8, 0, t), case.1); + } + } + + #[test] + fn stick_distance() { + for case in [ + (0, 0, 0), + (127, 0, 127), + (64, 64, 90), + (-64, 64, 90), + (-64, -64, 90), + ] + .iter() + { + let x = case.0; + let y = case.1; + assert_eq!(crate::stick_distance(x, y), case.2); + } + } +} diff --git a/gamebridge/src/twitch.rs b/gamebridge/src/twitch.rs index 3cc3138..795e1bc 100644 --- a/gamebridge/src/twitch.rs +++ b/gamebridge/src/twitch.rs @@ -70,8 +70,6 @@ async fn run_loop( } wait_and_join(&mut control, &mut dispatcher, channels).await; - let mut stickx: i8 = 0; - let mut sticky: i8 = 0; loop { tokio::select! { @@ -84,6 +82,14 @@ async fn run_loop( Some(msg) = pmsg.next() => { let mut hi = HiButtons::NONE; let mut lo = LoButtons::NONE; + let mut stickx: i8 = 0; + let mut sticky: i8 = 0; + + { + let data = st.read().unwrap(); + stickx = data.controller[2] as i8; + sticky = data.controller[3] as i8; + } let mut data = msg.data.to_string(); let data = data.to_ascii_lowercase(); From bf3e531d2020b4d900ce78527ce2e42e91c1b410 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 8 May 2020 23:50:23 -0400 Subject: [PATCH 19/28] fix lerping --- gamebridge/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 426cd3e..6bc3f59 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -50,7 +50,7 @@ fn main() -> Result<()> { spawn(move || twitch::run(st)); } - const LERP_TIME: f64 = 1.0; // 15 frames to lerp stick positions down to 0 + const LERP_TIME: f64 = 90.0; // 90 frames to lerp stick positions down to 0 let mut xmax_frame: u64 = 0; let mut ymax_frame: u64 = 0; @@ -93,7 +93,7 @@ fn main() -> Result<()> { }, }; - sticky = match stickx { + sticky = match sticky { 0 => sticky, 127 => { ymax_frame = data.frame; From 74862e92a474ae936bb7c4ca2b4bf497b6653329 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 06:30:23 -0400 Subject: [PATCH 20/28] 330 frames to lerp down, i guess --- gamebridge/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 6bc3f59..48a5543 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -50,7 +50,7 @@ fn main() -> Result<()> { spawn(move || twitch::run(st)); } - const LERP_TIME: f64 = 90.0; // 90 frames to lerp stick positions down to 0 + const LERP_TIME: f64 = 330.0; // 330 frames to lerp stick positions down to 0 let mut xmax_frame: u64 = 0; let mut ymax_frame: u64 = 0; From b9fd048aad800d559a244cb16e0e5394af136465 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 06:39:38 -0400 Subject: [PATCH 21/28] Revert "integrate puppycam patch" This reverts commit c52fdb27f81cbb39459e1200cd3498b820c6da6a. --- Makefile | 3 - enhancements/bettercamera.h | 48 -- enhancements/bettercamera.inc.c | 882 ------------------------------ enhancements/bettercamera.txt | 7 - include/segments.h | 10 +- include/text_strings.h.in | 14 - src/engine/math_util.c | 31 +- src/engine/math_util.h | 6 - src/engine/surface_collision.c | 165 ------ src/engine/surface_collision.h | 2 +- src/game/camera.c | 62 +-- src/game/camera.h | 1 - src/game/game.c | 1 - src/game/ingame_menu.c | 12 +- src/game/mario.c | 6 +- src/game/mario_actions_airborne.c | 71 +-- src/game/save_file.c | 46 +- src/game/save_file.h | 21 +- src/pc/pc_main.c | 1 - 19 files changed, 40 insertions(+), 1349 deletions(-) delete mode 100644 enhancements/bettercamera.h delete mode 100644 enhancements/bettercamera.inc.c delete mode 100644 enhancements/bettercamera.txt diff --git a/Makefile b/Makefile index 1d6ba20..ccf81a9 100644 --- a/Makefile +++ b/Makefile @@ -471,9 +471,6 @@ asm/boot.s: $(BUILD_DIR)/lib/bin/ipl3_font.bin $(BUILD_DIR)/lib/bin/ipl3_font.bin: lib/ipl3_font.png $(IPLFONTUTIL) e $< $@ -#Required so the compiler doesn't complain about this not existing. -$(BUILD_DIR)/src/game/camera.o: $(BUILD_DIR)/include/text_strings.h - $(BUILD_DIR)/include/text_strings.h: include/text_strings.h.in $(TEXTCONV) charmap.txt $< $@ diff --git a/enhancements/bettercamera.h b/enhancements/bettercamera.h deleted file mode 100644 index 2896103..0000000 --- a/enhancements/bettercamera.h +++ /dev/null @@ -1,48 +0,0 @@ -enum newcam_flagvalues -{ - NC_FLAG_XTURN = 0x0001,//If this flag is set, the camera's yaw can be moved by the player. - NC_FLAG_YTURN = 0x0002, //If this flag is set, the camera's pitch can be moved by the player. - NC_FLAG_ZOOM = 0x0004, //If this flag is set, the camera's distance can be set by the player. - NC_FLAG_8D = 0x0008, //If this flag is set, the camera will snap to an 8 directional axis - NC_FLAG_4D = 0x0010, //If this flag is set, the camera will snap to a 4 directional axis - NC_FLAG_2D = 0x0020, //If this flag is set, the camera will stick to 2D. - NC_FLAG_FOCUSX = 0x0040, //If this flag is set, the camera will point towards its focus on the X axis. - NC_FLAG_FOCUSY = 0x0080, //If this flag is set, the camera will point towards its focus on the Y axis. - NC_FLAG_FOCUSZ = 0x0100, //If this flag is set, the camera will point towards its focus on the Z axis. - NC_FLAG_POSX = 0x0200, //If this flag is set, the camera will move along the X axis. - NC_FLAG_POSY = 0x0400, //If this flag is set, the camera will move along the Y axis. - NC_FLAG_POSZ = 0x0800, //If this flag is set, the camera will move along the Z axis. - NC_FLAG_COLLISION = 0x1000, //If this flag is set, the camera will collide and correct itself with terrain. - NC_FLAG_SLIDECORRECT = 0x2000, //If this flag is set, the camera will attempt to centre itself behind Mario whenever he's sliding. - - NC_MODE_NORMAL = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, - NC_MODE_SLIDE = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION | NC_FLAG_SLIDECORRECT, - NC_MODE_FIXED = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ, - NC_MODE_2D = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, - NC_MODE_8D = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_8D | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, - NC_MODE_FIXED_NOMOVE = 0x0000, - NC_MODE_NOTURN = NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, - NC_MODE_NOROTATE = NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION - -}; - -extern void newcam_display_options(void); -extern void newcam_check_pause_buttons(void); -extern void newcam_init_settings(void); -extern void newcam_render_option_text(void); -extern void newcam_diagnostics(void); - -extern u8 newcam_option_open; - -extern u8 newcam_sensitivityX; //How quick the camera works. -extern u8 newcam_sensitivityY; -extern u8 newcam_invertX; -extern u8 newcam_invertY; -extern u8 newcam_panlevel; //How much the camera sticks out a bit in the direction you're looking. -extern u8 newcam_aggression; //How much the camera tries to centre itself to Mario's facing and movement. -extern u8 newcam_active; // basically the thing that governs if newcam is on. -extern u8 newcam_analogue; -extern u16 newcam_intendedmode; - -extern u16 newcam_mode; -extern s16 newcam_yaw; diff --git a/enhancements/bettercamera.inc.c b/enhancements/bettercamera.inc.c deleted file mode 100644 index 7320aa9..0000000 --- a/enhancements/bettercamera.inc.c +++ /dev/null @@ -1,882 +0,0 @@ -#include "sm64.h" -#include "game/camera.h" -#include "game/level_update.h" -#include "game/print.h" -#include "engine/math_util.h" -#include "game/segment2.h" -#include "game/save_file.h" -#include "bettercamera.h" -#include "include/text_strings.h" -#include "engine/surface_collision.h" - - - -/** -Quick explanation of the camera modes - -NC_MODE_NORMAL: Standard mode, allows dualaxial movement and free control of the camera. -NC_MODE_FIXED: Disables control of camera, and the actual position of the camera doesn't update. -NC_MODE_2D: Disables horizontal control of the camera and locks Mario's direction to the X axis. NYI though. -NC_MODE_8D: 8 directional movement. Similar to standard, except the camera direction snaps to 8 directions. -NC_MODE_FIXED_NOMOVE: Disables control and movement of the camera. -NC_MODE_NOTURN: Disables horizontal and vertical control of the camera. -**/ - -//!A bunch of developer intended options, to cover every base, really. -//#define NEWCAM_DEBUG //Some print values for puppycam. Not useful anymore, but never hurts to keep em around. -//#define nosound //If for some reason you hate the concept of audio, you can disable it. -//#define noaccel //Disables smooth movement of the camera with the C buttons. -#define DEGRADE 0.1f //What percent of the remaining camera movement is degraded. Default is 10% - - -//!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes. -///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons. -struct newcam_hardpos -{ - u8 newcam_hard_levelID; - u8 newcam_hard_areaID; - u8 newcam_hard_permaswap; - u16 newcam_hard_modeset; - s16 newcam_hard_X1; - s16 newcam_hard_Y1; - s16 newcam_hard_Z1; - s16 newcam_hard_X2; - s16 newcam_hard_Y2; - s16 newcam_hard_Z2; - s16 newcam_hard_camX; - s16 newcam_hard_camY; - s16 newcam_hard_camZ; - s16 newcam_hard_lookX; - s16 newcam_hard_lookY; - s16 newcam_hard_lookZ; -}; - -///This is the bit that defines where the angles happen. They're basically environment boxes that dictate camera behaviour. -//Permaswap is a boolean that simply determines wether or not when the camera changes at this point it stays changed. 0 means it resets when you leave, and 1 means it stays changed. -//The camera position fields accept "32767" as an ignore flag. -struct newcam_hardpos newcam_fixedcam[] = -{ -{/*Level ID*/ 16,/*Area ID*/ 1,/*Permaswap*/ 0,/*Mode*/ NC_MODE_FIXED_NOMOVE, //Standard params. -/*X begin*/ -540,/*Y begin*/ 800,/*Z begin*/ -3500, //Where the activation box begins -/*X end*/ 540,/*Y end*/ 2000,/*Z end*/ -1500, //Where the activation box ends. -/*Cam X*/ 0,/*Cam Y*/ 1500,/*Cam Z*/ -1000, //The position the camera gets placed for NC_MODE_FIXED and NC_MODE_FIXED_NOMOVE -/*Look X*/ 0,/*Look Y*/ 800,/*Look Z*/ -2500}, //The position the camera looks at for NC_MODE_FIXED_NOMOVE -}; - - -#ifdef noaccel - u8 accel = 255; - #else - u8 accel = 10; -#endif // noaccel - -s16 newcam_yaw; //Z axis rotation -s8 newcam_yaw_acc; -s16 newcam_tilt = 1500; //Y axis rotation -s8 newcam_tilt_acc; -u16 newcam_distance = 750; //The distance the camera stays from the player -u16 newcam_distance_target = 750; //The distance the player camera tries to reach. -f32 newcam_pos_target[3]; //The position the camera is basing calculations off. *usually* Mario. -f32 newcam_pos[3]; //Position the camera is in the world -f32 newcam_lookat[3]; //Position the camera is looking at -f32 newcam_framessincec[2]; -f32 newcam_extheight = 125; -u8 newcam_centering = 0; // The flag that depicts wether the camera's goin gto try centering. -s16 newcam_yaw_target; // The yaw value the camera tries to set itself to when the centre flag is active. Is set to Mario's face angle. -f32 newcam_turnwait; // The amount of time to wait after landing before allowing the camera to turn again -f32 newcam_pan_x; -f32 newcam_pan_z; -u8 newcam_cstick_down = 0; //Just a value that triggers true when the player 2 stick is moved in 8 direction move to prevent holding it down. -u8 newcam_target; - -u8 newcam_sensitivityX; //How quick the camera works. -u8 newcam_sensitivityY; -u8 newcam_invertX; //Reverses movement of the camera axis. -u8 newcam_invertY; -u8 newcam_panlevel; //How much the camera sticks out a bit in the direction you're looking. -u8 newcam_aggression; //How much the camera tries to centre itself to Mario's facing and movement. -u8 newcam_analogue; //Wether to accept inputs from a player 2 joystick, and then disables C button input. -s16 newcam_distance_values[] = {750,1250,2000}; -u8 newcam_active = 1; // basically the thing that governs if newcam is on. -u16 newcam_mode; -u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another. -u16 newcam_modeflags; - -u8 newcam_option_open = 0; -s8 newcam_option_selection = 0; -f32 newcam_option_timer = 0; -u8 newcam_option_index = 0; -u8 newcam_option_scroll = 0; -u8 newcam_option_scroll_last = 0; -u8 newcam_total = 7; //How many options there are in newcam_uptions. - -u8 newcam_options[][64] = {{NC_ANALOGUE}, {NC_CAMX}, {NC_CAMY}, {NC_INVERTX}, {NC_INVERTY}, {NC_CAMC}, {NC_CAMP}}; -u8 newcam_flags[][64] = {{NC_DISABLED}, {NC_ENABLED}}; -u8 newcam_strings[][64] = {{NC_BUTTON}, {NC_BUTTON2}, {NC_OPTION}, {NC_HIGHLIGHT}}; - -///This is called at every level initialisation. -void newcam_init(struct Camera *c, u8 dv) -{ - newcam_tilt = 1500; - newcam_distance_target = newcam_distance_values[dv]; - newcam_yaw = -c->yaw+0x4000; //Mario and the camera's yaw have this offset between them. - newcam_mode = NC_MODE_NORMAL; - ///This here will dictate what modes the camera will start in at the beginning of a level. Below are some examples. - switch (gCurrLevelNum) - { - case LEVEL_BITDW: newcam_yaw = 0x4000; newcam_mode = NC_MODE_8D; newcam_tilt = 4000; newcam_distance_target = newcam_distance_values[2]; break; - case LEVEL_BITFS: newcam_yaw = 0x4000; newcam_mode = NC_MODE_8D; newcam_tilt = 4000; newcam_distance_target = newcam_distance_values[2]; break; - case LEVEL_BITS: newcam_yaw = 0x4000; newcam_mode = NC_MODE_8D; newcam_tilt = 4000; newcam_distance_target = newcam_distance_values[2]; break; - case LEVEL_WF: newcam_yaw = 0x4000; newcam_tilt = 2000; newcam_distance_target = newcam_distance_values[1]; break; - case LEVEL_RR: newcam_yaw = 0x6000; newcam_tilt = 2000; newcam_distance_target = newcam_distance_values[2]; break; - case LEVEL_CCM: if (gCurrAreaIndex == 1) {newcam_yaw = -0x4000; newcam_tilt = 2000; newcam_distance_target = newcam_distance_values[1];} else newcam_mode = NC_MODE_SLIDE; break; - case LEVEL_WDW: newcam_yaw = 0x2000; newcam_tilt = 3000; newcam_distance_target = newcam_distance_values[1]; break; - case 27: newcam_mode = NC_MODE_SLIDE; break; - case LEVEL_THI: if (gCurrAreaIndex == 2) newcam_mode = NC_MODE_SLIDE; break; - } - - newcam_distance = newcam_distance_target; - newcam_intendedmode = newcam_mode; - newcam_modeflags = newcam_mode; -} -static f32 newcam_clamp(f32 value, f32 max, f32 min) -{ - if (value > max) - value = max; - if (value < min) - value = min; - return value; -} -///These are the default settings for Puppycam. You may change them to change how they'll be set for first timers. -void newcam_init_settings() -{ - if (save_check_firsttime()) - { - save_file_get_setting(); - newcam_clamp(newcam_sensitivityX, 10, 250); - newcam_clamp(newcam_sensitivityY, 10, 250); - newcam_clamp(newcam_aggression, 0, 100); - newcam_clamp(newcam_panlevel, 0, 100); - newcam_clamp(newcam_invertX, 0, 1); - newcam_clamp(newcam_invertY, 0, 1); - } - else - { - newcam_sensitivityX = 75; - newcam_sensitivityY = 75; - newcam_aggression = 0; - newcam_panlevel = 75; - newcam_invertX = 0; - newcam_invertY = 0; - save_set_firsttime(); - } -} - -/** Mathematic calculations. This stuffs so basic even *I* understand it lol -Basically, it just returns a position based on angle */ -static s16 lengthdir_x(f32 length, s16 dir) -{ - return (s16) (length * coss(dir)); -} -static s16 lengthdir_y(f32 length, s16 dir) -{ - return (s16) (length * sins(dir)); -} - -void newcam_diagnostics(void) -{ - print_text_fmt_int(32,192,"Lv %d",gCurrLevelNum); - print_text_fmt_int(32,176,"Area %d",gCurrAreaIndex); - print_text_fmt_int(32,160,"X %d",gMarioState->pos[0]); - print_text_fmt_int(32,144,"Y %d",gMarioState->pos[1]); - print_text_fmt_int(32,128,"Z %d",gMarioState->pos[2]); - print_text_fmt_int(32,112,"FLAGS %d",newcam_modeflags); - print_text_fmt_int(180,112,"INTM %d",newcam_intendedmode); - print_text_fmt_int(32,96,"TILT UP %d",newcam_tilt_acc); - print_text_fmt_int(32,80,"YAW UP %d",newcam_yaw_acc); - print_text_fmt_int(32,64,"YAW %d",newcam_yaw); - print_text_fmt_int(32,48,"TILT %d",newcam_tilt); - print_text_fmt_int(32,32,"DISTANCE %d",newcam_distance); -} - -static s16 newcam_adjust_value(s16 var, s16 val) -{ - var += val; - if (var > 100) - var = 100; - if (var < -100) - var = -100; - - return var; -} - -static f32 newcam_approach_float(f32 var, f32 val, f32 inc) -{ - if (var < val) - return min(var + inc, val); - else - return max(var - inc, val); -} - -static s16 newcam_approach_s16(s16 var, s16 val, s16 inc) -{ - if (var < val) - return max(var + inc, val); - else - return min(var - inc, val); -} - -static u8 ivrt(u8 axis) -{ - if (axis == 0) - { - if (newcam_invertX == 0) - return 1; - else - return -1; - } - else - { - if (newcam_invertY == 0) - return 1; - else - return -1; - } -} - -static void newcam_rotate_button(void) -{ - if ((newcam_modeflags & NC_FLAG_8D || newcam_modeflags & NC_FLAG_4D) && newcam_modeflags & NC_FLAG_XTURN) //8 directional camera rotation input for buttons. - { - if ((gPlayer1Controller->buttonPressed & L_CBUTTONS) && newcam_analogue == 0) - { - #ifndef nosound - play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); - #endif - if (newcam_modeflags & NC_FLAG_8D) - newcam_yaw_target = newcam_yaw_target+0x2000; - else - newcam_yaw_target = newcam_yaw_target+0x4000; - newcam_centering = 1; - } - else - if ((gPlayer1Controller->buttonPressed & R_CBUTTONS) && newcam_analogue == 0) - { - #ifndef nosound - play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); - #endif - if (newcam_modeflags & NC_FLAG_8D) - newcam_yaw_target = newcam_yaw_target-0x2000; - else - newcam_yaw_target = newcam_yaw_target-0x4000; - newcam_centering = 1; - } - } - else //Standard camera movement - if (newcam_modeflags & NC_FLAG_XTURN) - { - if ((gPlayer1Controller->buttonDown & L_CBUTTONS) && newcam_analogue == 0) - newcam_yaw_acc = newcam_adjust_value(newcam_yaw_acc,accel); - else if ((gPlayer1Controller->buttonDown & R_CBUTTONS) && newcam_analogue == 0) - newcam_yaw_acc = newcam_adjust_value(newcam_yaw_acc,-accel); - else - #ifdef noaccel - newcam_yaw_acc = 0; - #else - newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); - #endif - } - - if (gPlayer1Controller->buttonDown & U_CBUTTONS && newcam_modeflags & NC_FLAG_YTURN && newcam_analogue == 0) - newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,accel); - else if (gPlayer1Controller->buttonDown & D_CBUTTONS && newcam_modeflags & NC_FLAG_YTURN && newcam_analogue == 0) - newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,-accel); - else - #ifdef noaccel - newcam_tilt_acc = 0; - #else - newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); - #endif - - newcam_framessincec[0] += 1; - newcam_framessincec[1] += 1; - if ((gPlayer1Controller->buttonPressed & L_CBUTTONS) && newcam_modeflags & NC_FLAG_XTURN && !(newcam_modeflags & NC_FLAG_8D) && newcam_analogue == 0) - { - if (newcam_framessincec[0] < 6) - { - newcam_yaw_target = newcam_yaw+0x3000; - newcam_centering = 1; - #ifndef nosound - play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); - #endif - } - newcam_framessincec[0] = 0; - } - if ((gPlayer1Controller->buttonPressed & R_CBUTTONS) && newcam_modeflags & NC_FLAG_XTURN && !(newcam_modeflags & NC_FLAG_8D) && newcam_analogue == 0) - { - if (newcam_framessincec[1] < 6) - { - newcam_yaw_target = newcam_yaw-0x3000; - newcam_centering = 1; - #ifndef nosound - play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); - #endif - } - newcam_framessincec[1] = 0; - } - - - if (newcam_analogue == 1) //There's not much point in keeping this behind a check, but it wouldn't hurt, just incase any 2player shenanigans ever happen, it makes it easy to disable. - { //The joystick values cap at 80, so divide by 8 to get the same net result at maximum turn as the button - if (ABS(gPlayer2Controller->rawStickX) > 20 && newcam_modeflags & NC_FLAG_XTURN) - { - if (newcam_modeflags & NC_FLAG_8D) - { - if (newcam_cstick_down == 0) - { - newcam_cstick_down = 1; - newcam_centering = 1; - #ifndef nosound - play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); - #endif - if (gPlayer2Controller->rawStickX > 20) - { - if (newcam_modeflags & NC_FLAG_8D) - newcam_yaw_target = newcam_yaw_target+0x2000; - else - newcam_yaw_target = newcam_yaw_target+0x4000; - } - else - { - if (newcam_modeflags & NC_FLAG_8D) - newcam_yaw_target = newcam_yaw_target-0x2000; - else - newcam_yaw_target = newcam_yaw_target-0x4000; - } - } - } - else - newcam_yaw_acc = newcam_adjust_value(newcam_yaw_acc,(-gPlayer2Controller->rawStickX/4)); - } - else - { - newcam_cstick_down = 0; - newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); - } - - if (ABS(gPlayer2Controller->rawStickY) > 20 && newcam_modeflags & NC_FLAG_YTURN) - newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->rawStickY/4)); - else - newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); - } -} - -static void newcam_zoom_button(void) -{ - //Smoothly move the camera to the new spot. - if (newcam_distance > newcam_distance_target) - { - newcam_distance -= 250; - if (newcam_distance < newcam_distance_target) - newcam_distance = newcam_distance_target; - } - if (newcam_distance < newcam_distance_target) - { - newcam_distance += 250; - if (newcam_distance > newcam_distance_target) - newcam_distance = newcam_distance_target; - } - - //When you press L and R together, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. - if (gPlayer1Controller->buttonDown & L_TRIG && gPlayer1Controller->buttonDown & R_TRIG && newcam_modeflags & NC_FLAG_ZOOM) - { - newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000; - newcam_centering = 1; - } - else //Each time the player presses R, but NOT L the camera zooms out more, until it hits the limit and resets back to close view. - if (gPlayer1Controller->buttonPressed & R_TRIG && newcam_modeflags & NC_FLAG_XTURN) - { - #ifndef nosound - play_sound(SOUND_MENU_CLICK_CHANGE_VIEW, gDefaultSoundArgs); - #endif - - if (newcam_distance_target == newcam_distance_values[0]) - newcam_distance_target = newcam_distance_values[1]; - else - if (newcam_distance_target == newcam_distance_values[1]) - newcam_distance_target = newcam_distance_values[2]; - else - newcam_distance_target = newcam_distance_values[0]; - - } - if (newcam_centering && newcam_modeflags & NC_FLAG_XTURN) - { - newcam_yaw = approach_s16_symmetric(newcam_yaw,newcam_yaw_target,0x800); - if (newcam_yaw = newcam_yaw_target) - newcam_centering = 0; - } - else - newcam_yaw_target = newcam_yaw; -} - -static void newcam_update_values(void) -{//For tilt, this just limits it so it doesn't go further than 90 degrees either way. 90 degrees is actually 16384, but can sometimes lead to issues, so I just leave it shy of 90. - u8 waterflag = 0; - if (newcam_modeflags & NC_FLAG_XTURN) - newcam_yaw += (ivrt(0)*(newcam_yaw_acc*(newcam_sensitivityX/10))); - if (((newcam_tilt < 12000 && newcam_tilt_acc*ivrt(1) > 0) || (newcam_tilt > -12000 && newcam_tilt_acc*ivrt(1) < 0)) && newcam_modeflags & NC_FLAG_YTURN) - newcam_tilt += (ivrt(1)*(newcam_tilt_acc*(newcam_sensitivityY/10))); - else - { - if (newcam_tilt > 12000) - newcam_tilt = 12000; - if (newcam_tilt < -12000) - newcam_tilt = -12000; - } - - if (newcam_turnwait > 0 && gMarioState->vel[1] == 0) - { - newcam_turnwait -= 1; - if (newcam_turnwait < 0) - newcam_turnwait = 0; - } - else - { - if (gMarioState->intendedMag > 0 && gMarioState->vel[1] == 0 && newcam_modeflags & NC_FLAG_XTURN) - newcam_yaw = (approach_s16_symmetric(newcam_yaw,-gMarioState->faceAngle[1]-0x4000,((newcam_aggression*(ABS(gPlayer1Controller->rawStickX/10)))*(gMarioState->forwardVel/32)))); - else - newcam_turnwait = 10; - } - - if (newcam_modeflags & NC_FLAG_SLIDECORRECT) - { - switch (gMarioState->action) - { - case ACT_BUTT_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; - case ACT_STOMACH_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; - case ACT_HOLD_BUTT_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; - case ACT_HOLD_STOMACH_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; - } - } - switch (gMarioState->action) - { - case ACT_SHOT_FROM_CANNON: waterflag = 1; break; - case ACT_FLYING: waterflag = 1; break; - } - - if (gMarioState->action & ACT_FLAG_SWIMMING) - { - if (gMarioState->forwardVel > 2) - waterflag = 1; - } - - if (waterflag && newcam_modeflags & NC_FLAG_XTURN) - { - newcam_yaw = (approach_s16_symmetric(newcam_yaw,-gMarioState->faceAngle[1]-0x4000,(gMarioState->forwardVel*128))); - if ((signed)gMarioState->forwardVel > 1) - newcam_tilt = (approach_s16_symmetric(newcam_tilt,(-gMarioState->faceAngle[0]*0.8)+3000,(gMarioState->forwardVel*32))); - else - newcam_tilt = (approach_s16_symmetric(newcam_tilt,3000,32)); - } -} - -static void newcam_collision(void) -{ - struct Surface *surf; - Vec3f camdir; - Vec3f hitpos; - - camdir[0] = newcam_pos[0]-newcam_lookat[0]; - camdir[1] = newcam_pos[1]-newcam_lookat[1]; - camdir[2] = newcam_pos[2]-newcam_lookat[2]; - - - - find_surface_on_ray(newcam_pos_target, camdir, &surf, &hitpos); - - if (surf) - { - newcam_pos[0] = hitpos[0]; - newcam_pos[1] = approach_f32(hitpos[1],newcam_pos[1],25,-25); - newcam_pos[2] = hitpos[2]; - newcam_pan_x = 0; - newcam_pan_z = 0; - } -} - -static void newcam_set_pan(void) -{ - //Apply panning values based on Mario's direction. - if (gMarioState->action != ACT_HOLDING_BOWSER && gMarioState->action != ACT_SLEEPING && gMarioState->action != ACT_START_SLEEPING) - { - approach_f32_asymptotic_bool(&newcam_pan_x, lengthdir_x((160*newcam_panlevel)/100, -gMarioState->faceAngle[1]-0x4000), 0.05); - approach_f32_asymptotic_bool(&newcam_pan_z, lengthdir_y((160*newcam_panlevel)/100, -gMarioState->faceAngle[1]-0x4000), 0.05); - } - else - { - approach_f32_asymptotic_bool(&newcam_pan_x, 0, 0.05); - approach_f32_asymptotic_bool(&newcam_pan_z, 0, 0.05); - } - - newcam_pan_x = newcam_pan_x*(min(newcam_distance/newcam_distance_target,1)); - newcam_pan_z = newcam_pan_z*(min(newcam_distance/newcam_distance_target,1)); -} - -static void newcam_position_cam(void) -{ - f32 floorY = 0; - f32 floorY2 = 0; - s16 shakeX; - s16 shakeY; - - if (!(gMarioState->action & ACT_FLAG_SWIMMING)) - calc_y_to_curr_floor(&floorY, 1.f, 200.f, &floorY2, 0.9f, 200.f); - - newcam_update_values(); - shakeX = gLakituState.shakeMagnitude[1]; - shakeY = gLakituState.shakeMagnitude[0]; - //Fetch Mario's current position. Not hardcoded just for the sake of flexibility, though this specific bit is temp, because it won't always want to be focusing on Mario. - newcam_pos_target[0] = gMarioState->pos[0]; - newcam_pos_target[1] = gMarioState->pos[1]+newcam_extheight; - newcam_pos_target[2] = gMarioState->pos[2]; - //These will set the position of the camera to where Mario is supposed to be, minus adjustments for where the camera should be, on top of. - if (newcam_modeflags & NC_FLAG_POSX) - newcam_pos[0] = newcam_pos_target[0]+lengthdir_x(lengthdir_x(newcam_distance,newcam_tilt+shakeX),newcam_yaw+shakeY); - if (newcam_modeflags & NC_FLAG_POSY) - newcam_pos[2] = newcam_pos_target[2]+lengthdir_y(lengthdir_x(newcam_distance,newcam_tilt+shakeX),newcam_yaw+shakeY); - if (newcam_modeflags & NC_FLAG_POSZ) - newcam_pos[1] = newcam_pos_target[1]+lengthdir_y(newcam_distance,newcam_tilt+gLakituState.shakeMagnitude[0])+floorY; - if ((newcam_modeflags & NC_FLAG_FOCUSX) && (newcam_modeflags & NC_FLAG_FOCUSY) && (newcam_modeflags & NC_FLAG_FOCUSZ)) - newcam_set_pan(); - //Set where the camera wants to be looking at. This is almost always the place it's based off, too. - if (newcam_modeflags & NC_FLAG_FOCUSX) - newcam_lookat[0] = newcam_pos_target[0]-newcam_pan_x; - if (newcam_modeflags & NC_FLAG_FOCUSY) - newcam_lookat[1] = newcam_pos_target[1]+floorY2; - if (newcam_modeflags & NC_FLAG_FOCUSZ) - newcam_lookat[2] = newcam_pos_target[2]-newcam_pan_z; - - if (newcam_modeflags & NC_FLAG_COLLISION) - newcam_collision(); - -} - -//Nested if's baybeeeee -static void newcam_find_fixed(void) -{ - u8 i = 0; - newcam_mode = newcam_intendedmode; - newcam_modeflags = newcam_mode; - for (i = 0; i < sizeof(newcam_fixedcam); i++) - { - if (newcam_fixedcam[i].newcam_hard_levelID == gCurrLevelNum && newcam_fixedcam[i].newcam_hard_areaID == gCurrAreaIndex) - {//I didn't wanna just obliterate the horizontal plane of the IDE with a beefy if statement, besides, I think this runs slightly better anyway? - if (newcam_pos_target[0] > newcam_fixedcam[i].newcam_hard_X1) - if (newcam_pos_target[0] < newcam_fixedcam[i].newcam_hard_X2) - if (newcam_pos_target[1] > newcam_fixedcam[i].newcam_hard_Y1) - if (newcam_pos_target[1] < newcam_fixedcam[i].newcam_hard_Y2) - if (newcam_pos_target[2] > newcam_fixedcam[i].newcam_hard_Z1) - if (newcam_pos_target[2] < newcam_fixedcam[i].newcam_hard_Z2) - { - if (newcam_fixedcam[i].newcam_hard_permaswap) - newcam_intendedmode = newcam_fixedcam[i].newcam_hard_modeset; - newcam_mode = newcam_fixedcam[i].newcam_hard_modeset; - newcam_modeflags = newcam_mode; - - if (newcam_fixedcam[i].newcam_hard_camX != 32767 && !(newcam_modeflags & NC_FLAG_POSX)) - newcam_pos[0] = newcam_fixedcam[i].newcam_hard_camX; - if (newcam_fixedcam[i].newcam_hard_camY != 32767 && !(newcam_modeflags & NC_FLAG_POSY)) - newcam_pos[1] = newcam_fixedcam[i].newcam_hard_camY; - if (newcam_fixedcam[i].newcam_hard_camZ != 32767 && !(newcam_modeflags & NC_FLAG_POSZ)) - newcam_pos[2] = newcam_fixedcam[i].newcam_hard_camZ; - - if (newcam_fixedcam[i].newcam_hard_lookX != 32767 && !(newcam_modeflags & NC_FLAG_FOCUSX)) - newcam_lookat[0] = newcam_fixedcam[i].newcam_hard_lookX; - if (newcam_fixedcam[i].newcam_hard_lookY != 32767 && !(newcam_modeflags & NC_FLAG_FOCUSY)) - newcam_lookat[1] = newcam_fixedcam[i].newcam_hard_lookY; - if (newcam_fixedcam[i].newcam_hard_lookZ != 32767 && !(newcam_modeflags & NC_FLAG_FOCUSZ)) - newcam_lookat[2] = newcam_fixedcam[i].newcam_hard_lookZ; - - newcam_yaw = atan2s(newcam_pos[0]-newcam_pos_target[0],newcam_pos[2]-newcam_pos_target[2]); - } - } - } -} - -static void newcam_apply_values(struct Camera *c) -{ - - c->pos[0] = newcam_pos[0]; - c->pos[1] = newcam_pos[1]; - c->pos[2] = newcam_pos[2]; - - c->focus[0] = newcam_lookat[0]; - c->focus[1] = newcam_lookat[1]; - c->focus[2] = newcam_lookat[2]; - - gLakituState.pos[0] = newcam_pos[0]; - gLakituState.pos[1] = newcam_pos[1]; - gLakituState.pos[2] = newcam_pos[2]; - - gLakituState.focus[0] = newcam_lookat[0]; - gLakituState.focus[1] = newcam_lookat[1]; - gLakituState.focus[2] = newcam_lookat[2]; - - c->yaw = -newcam_yaw+0x4000; - gLakituState.yaw = -newcam_yaw+0x4000; - - //Adds support for wing mario tower - if (gMarioState->floor->type == SURFACE_LOOK_UP_WARP) { - if (save_file_get_total_star_count(gCurrSaveFileNum - 1, 0, 0x18) >= 10) { - if (newcam_tilt < -8000 && gMarioState->forwardVel == 0) { - level_trigger_warp(gMarioState, 1); - } - } - } - -} - -//The ingame cutscene system is such a spaghetti mess I actually have to resort to something as stupid as this to cover every base. -void newcam_apply_outside_values(struct Camera *c, u8 bit) -{ - if (newcam_modeflags == NC_FLAG_XTURN) - { - if (bit) - newcam_yaw = -gMarioState->faceAngle[1]-0x4000; - else - newcam_yaw = -c->yaw+0x4000; - } -} - -//Main loop. -void newcam_loop(struct Camera *c) -{ - newcam_rotate_button(); - newcam_zoom_button(); - newcam_position_cam(); - newcam_find_fixed(); - if (gMarioObject) - newcam_apply_values(c); - - //Just some visual information on the values of the camera. utilises ifdef because it's better at runtime. - #ifdef NEWCAM_DEBUG - newcam_diagnostics(); - #endif // NEWCAM_DEBUG -} - - - -//Displays a box. -void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) -{ - gDPPipeSync(gDisplayListHead++); - gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); - gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); - gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255)); - gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1); - gDPPipeSync(gDisplayListHead++); - gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); -} - -//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :( -void newcam_change_setting(u8 toggle) -{ - switch (newcam_option_selection) - { - case 0: - newcam_analogue ^= 1; - break; - case 1: - if (newcam_sensitivityX > 10 && newcam_sensitivityX < 250) - newcam_sensitivityX += toggle; - break; - case 2: - if (newcam_sensitivityY > 10 && newcam_sensitivityY < 250) - newcam_sensitivityY += toggle; - break; - case 3: - newcam_invertX ^= 1; - break; - case 4: - newcam_invertY ^= 1; - break; - case 5: - if (newcam_aggression > 0 && newcam_aggression < 100) - newcam_aggression += toggle; - break; - case 6: - if (newcam_panlevel > 0 && newcam_panlevel < 100) - newcam_panlevel += toggle; - break; - } -} - -void newcam_text(s16 x, s16 y, u8 str[], u8 col) -{ - u8 textX; - textX = get_str_x_pos_from_center(x,str,10.0f); - gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); - print_generic_string(textX+1,y-1,str); - if (col != 0) - { - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - } - else - { - gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255); - } - print_generic_string(textX,y,str); -} - -//Options menu -void newcam_display_options() -{ - u8 i = 0; - u8 newstring[32]; - s16 scroll; - s16 scrollpos; - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - print_hud_lut_string(HUD_LUT_GLOBAL, 118, 40, newcam_strings[2]); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); - - if (newcam_total>4) - { - newcam_display_box(272,90,280,208,0x80,0x80,0x80); - scrollpos = (54)*((f32)newcam_option_scroll/(newcam_total-4)); - newcam_display_box(272,90+scrollpos,280,154+scrollpos,0xFF,0xFF,0xFF); - } - - - gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT); - for (i = 0; i < newcam_total; i++) - { - scroll = 140-(32*i)+(newcam_option_scroll*32); - if (scroll <= 140 && scroll > 32) - { - newcam_text(160,scroll,newcam_options[i],newcam_option_selection-i); - switch (i) - { - case 0: - newcam_text(160,scroll-12,newcam_flags[newcam_analogue],newcam_option_selection-i); - break; - case 1: - int_to_str(newcam_sensitivityX,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 2: - int_to_str(newcam_sensitivityY,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 3: - newcam_text(160,scroll-12,newcam_flags[newcam_invertX],newcam_option_selection-i); - break; - case 4: - newcam_text(160,scroll-12,newcam_flags[newcam_invertY],newcam_option_selection-i); - break; - case 5: - int_to_str(newcam_aggression,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - case 6: - int_to_str(newcam_panlevel,newstring); - newcam_text(160,scroll-12,newstring,newcam_option_selection-i); - break; - } - } - } - gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - gSPDisplayList(gDisplayListHead++, dl_ia_text_end); - gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); - print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); - print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); - gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); -} - -//This has been separated for interesting reasons. Don't question it. -void newcam_render_option_text(void) -{ - gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); - newcam_text(278,212,newcam_strings[newcam_option_open],1); - gSPDisplayList(gDisplayListHead++, dl_ia_text_end); -} - -void newcam_check_pause_buttons() -{ - if (gPlayer1Controller->buttonPressed & R_TRIG) - { - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (newcam_option_open == 0) - newcam_option_open = 1; - else - { - newcam_option_open = 0; - save_file_set_setting(); - } - } - - if (newcam_option_open) - { - if (ABS(gPlayer1Controller->rawStickY) > 60) - { - newcam_option_timer -= 1; - if (newcam_option_timer <= 0) - { - switch (newcam_option_index) - { - case 0: newcam_option_index++; newcam_option_timer += 10; break; - default: newcam_option_timer += 5; break; - } - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (gPlayer1Controller->rawStickY >= 60) - { - newcam_option_selection--; - if (newcam_option_selection < 0) - newcam_option_selection = newcam_total-1; - } - else - { - newcam_option_selection++; - if (newcam_option_selection >= newcam_total) - newcam_option_selection = 0; - } - } - } - else - if (ABS(gPlayer1Controller->rawStickX) > 60) - { - newcam_option_timer -= 1; - if (newcam_option_timer <= 0) - { - switch (newcam_option_index) - { - case 0: newcam_option_index++; newcam_option_timer += 10; break; - default: newcam_option_timer += 5; break; - } - #ifndef nosound - play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); - #endif - if (gPlayer1Controller->rawStickX >= 60) - newcam_change_setting(1); - else - newcam_change_setting(-1); - } - } - else - { - newcam_option_timer = 0; - newcam_option_index = 0; - } - - while (newcam_option_scroll - newcam_option_selection < -3 && newcam_option_selection > newcam_option_scroll) - newcam_option_scroll +=1; - while (newcam_option_scroll + newcam_option_selection > 0 && newcam_option_selection < newcam_option_scroll) - newcam_option_scroll -=1; - } -} diff --git a/enhancements/bettercamera.txt b/enhancements/bettercamera.txt deleted file mode 100644 index 9cdad73..0000000 --- a/enhancements/bettercamera.txt +++ /dev/null @@ -1,7 +0,0 @@ -Puppycam 2.0 release notes: - -- Reworked Camera mode system, utilising behaviour flags, allowing for new camera modes to be created easier than ever before. bettercamera.h contains all modes, with their appropriate flags. - -- Reworked Collision. Puppycam now utilises CuckyDev's Raycasting system, which offers far more reliable collision checking as well as increased performance. The major change that comes with this however, is that the game must now be compiled with -O2 optimisation. This shouldn't be a problem for most people however, because if you're doing anything remotely creative, you want this enabled anyways. - -- Improved Code inside bettercamera.inc.c. A lot of the code inside this file has been cleaned up, which cuts down on bloat, and improves readability, as well as performance. Most of it's stuff you'd generally leave alone anyway, but it's good to not write YandereDev tier code. \ No newline at end of file diff --git a/include/segments.h b/include/segments.h index 9491b6f..ccc989a 100644 --- a/include/segments.h +++ b/include/segments.h @@ -1,8 +1,6 @@ #ifndef _SEGMENTS_H #define _SEGMENTS_H -#define USE_EXT_RAM - /* * Memory addresses for segments. Ideally, this header file would not be * needed, and the addresses would be defined in sm64.ld and linker-inserted @@ -35,10 +33,10 @@ */ #define SEG_BUFFERS 0x8005C000 // 0x0085000 in size -#define SEG_MAIN 0x800F1000 // 0x1328000 in size -#define SEG_ENGINE 0x80223800 // 0x0017000 in size -#define SEG_FRAMEBUFFERS 0x8023A800 // 0x0070800 in size -#define SEG_POOL_START 0x802AB000 // 0x0165000 in size +#define SEG_MAIN 0x800E1000 // 0x1328000 in size +#define SEG_ENGINE 0x80213800 // 0x0017000 in size +#define SEG_FRAMEBUFFERS 0x8022A800 // 0x0070800 in size +#define SEG_POOL_START 0x8029B000 // 0x0165000 in size #define SEG_POOL_END 0x80800000 #define SEG_POOL_END_4MB 0x80400000 // For the error message screen enhancement. #define SEG_GODDARD SEG_POOL_START + 0x113000 diff --git a/include/text_strings.h.in b/include/text_strings.h.in index 15706d3..2fda11d 100644 --- a/include/text_strings.h.in +++ b/include/text_strings.h.in @@ -3,20 +3,6 @@ #include "text_menu_strings.h" -#define NC_CAMX _("Camera X Sensitivity") - #define NC_CAMY _("Camera Y Sensitivity") - #define NC_INVERTX _("Invert X Axis") - #define NC_INVERTY _("Invert Y Axis") - #define NC_CAMC _("Camera Centre Aggression") - #define NC_CAMP _("Camera Pan Level") - #define NC_ENABLED _("Enabled") - #define NC_DISABLED _("Disabled") - #define NC_BUTTON _("[R]: Options") - #define NC_BUTTON2 _("[R]: Return") - #define NC_OPTION _("OPTIONS") - #define NC_HIGHLIGHT _("O") - #define NC_ANALOGUE _("Analogue Camera") - /** * Global Symbols */ diff --git a/src/engine/math_util.c b/src/engine/math_util.c index 19e676a..beb60c5 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -49,15 +49,6 @@ void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b) { return &dest; //! warning: function returns address of local variable } -/// Multiply vector 'dest' by a -void *vec3f_mul(Vec3f dest, f32 a) -{ - dest[0] *= a; - dest[1] *= a; - dest[2] *= a; - return &dest; //! warning: function returns address of local variable -} - /// Copy vector src to dest void *vec3s_copy(Vec3s dest, Vec3s src) { dest[0] = src[0]; @@ -90,11 +81,11 @@ void *vec3s_sum(Vec3s dest, Vec3s a, Vec3s b) { return &dest; //! warning: function returns address of local variable } -/// Make 'dest' the difference of vectors a and b. -void *vec3f_dif(Vec3f dest, Vec3f a, Vec3f b) { - dest[0] = a[0] - b[0]; - dest[1] = a[1] - b[1]; - dest[2] = a[2] - b[2]; +/// Subtract vector a from 'dest' +void *vec3s_sub(Vec3s dest, Vec3s a) { + dest[0] -= a[0]; + dest[1] -= a[1]; + dest[2] -= a[2]; return &dest; //! warning: function returns address of local variable } @@ -149,18 +140,6 @@ void *vec3f_normalize(Vec3f dest) { return &dest; //! warning: function returns address of local variable } -/// Get length of vector 'a' -f32 vec3f_length(Vec3f a) -{ - return sqrtf(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); -} - -/// Get dot product of vectors 'a' and 'b' -f32 vec3f_dot(Vec3f a, Vec3f b) -{ - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} - #pragma GCC diagnostic pop /// Copy matrix 'src' to 'dest' diff --git a/src/engine/math_util.h b/src/engine/math_util.h index 650fe97..b36498c 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -29,14 +29,10 @@ extern f32 gCosineTable[]; #define sqr(x) ((x) * (x)) -#define abs(x) ((x) < 0 ? -(x) : (x)) - void *vec3f_copy(Vec3f dest, Vec3f src); void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z); void *vec3f_add(Vec3f dest, Vec3f a); void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b); -void *vec3f_dif(Vec3f dest, Vec3f a, Vec3f b); -void *vec3f_mul(Vec3f dest, f32 a); void *vec3s_copy(Vec3s dest, Vec3s src); void *vec3s_set(Vec3s dest, s16 x, s16 y, s16 z); void *vec3s_add(Vec3s dest, Vec3s a); @@ -47,8 +43,6 @@ void *vec3f_to_vec3s(Vec3s dest, Vec3f a); void *find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c); void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b); void *vec3f_normalize(Vec3f dest); -f32 vec3f_length(Vec3f a); -f32 vec3f_dot(Vec3f a, Vec3f b); void mtxf_copy(f32 dest[4][4], f32 src[4][4]); void mtxf_identity(f32 mtx[4][4]); void mtxf_translate(f32 a[4][4], Vec3f b); diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index cd52d44..f746373 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -10,7 +10,6 @@ #include "surface_load.h" #include "game/object_list_processor.h" #include "game/room.h" -#include "math_util.h" /************************************************** * WALLS * @@ -787,167 +786,3 @@ static s32 unused_resolve_floor_or_ceil_collisions(s32 checkCeil, f32 *px, f32 * return 0; } - -/** - * Raycast functions - */ -s32 ray_surface_intersect(Vec3f orig, Vec3f dir, f32 dir_length, struct Surface *surface, Vec3f hit_pos, f32 *length) -{ - Vec3f v0, v1, v2, e1, e2, h, s, q; - f32 a, f, u, v; - Vec3f add_dir; - - // Get surface normal and some other stuff - vec3s_to_vec3f(v0, surface->vertex1); - vec3s_to_vec3f(v1, surface->vertex2); - vec3s_to_vec3f(v2, surface->vertex3); - - vec3f_dif(e1, v1, v0); - vec3f_dif(e2, v2, v0); - - vec3f_cross(h, dir, e2); - - // Check if we're perpendicular from the surface - a = vec3f_dot(e1, h); - if (a > -0.00001f && a < 0.00001f) - return FALSE; - - // Check if we're making contact with the surface - f = 1.0f / a; - - vec3f_dif(s, orig, v0); - u = f * vec3f_dot(s, h); - if (u < 0.0f || u > 1.0f) - return FALSE; - - vec3f_cross(q, s, e1); - v = f * vec3f_dot(dir, q); - if (v < 0.0f || u + v > 1.0f) - return FALSE; - - // Get the length between our origin and the surface contact point - *length = f * vec3f_dot(e2, q); - if (*length <= 0.00001 || *length > dir_length) - return FALSE; - - // Successful contact - vec3f_copy(add_dir, dir); - vec3f_mul(add_dir, *length); - vec3f_sum(hit_pos, orig, add_dir); - return TRUE; -} - -void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length) -{ - s32 hit; - f32 length; - Vec3f chk_hit_pos; - f32 top, bottom; - - // Get upper and lower bounds of ray - if (dir[1] >= 0.0f) - { - top = orig[1] + dir[1] * dir_length; - bottom = orig[1]; - } - else - { - top = orig[1]; - bottom = orig[1] + dir[1] * dir_length; - } - - // Iterate through every surface of the list - for (; list != NULL; list = list->next) - { - // Reject surface if out of vertical bounds - if (list->surface->lowerY > top || list->surface->upperY < bottom) - continue; - - // Check intersection between the ray and this surface - if ((hit = ray_surface_intersect(orig, dir, dir_length, list->surface, chk_hit_pos, &length)) != 0) - { - if (length <= *max_length) - { - *hit_surface = list->surface; - vec3f_copy(hit_pos, chk_hit_pos); - *max_length = length; - } - } - } -} - - -void find_surface_on_ray_cell(s16 cellX, s16 cellZ, Vec3f orig, Vec3f normalized_dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length) -{ - // Skip if OOB - if (cellX >= 0 && cellX <= 0xF && cellZ >= 0 && cellZ <= 0xF) - { - // Iterate through each surface in this partition - if (normalized_dir[1] > -0.99f) - { - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } - if (normalized_dir[1] < 0.99f) - { - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } - find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); - } -} - -void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos) -{ - f32 max_length; - s16 cellZ, cellX; - f32 fCellZ, fCellX; - f32 dir_length; - Vec3f normalized_dir; - f32 step, dx, dz; - u32 i; - - // Set that no surface has been hit - *hit_surface = NULL; - vec3f_sum(hit_pos, orig, dir); - - // Get normalized direction - dir_length = vec3f_length(dir); - max_length = dir_length; - vec3f_copy(normalized_dir, dir); - vec3f_normalize(normalized_dir); - - // Get our cell coordinate - fCellX = (orig[0] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; - fCellZ = (orig[2] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; - cellX = (s16)fCellX; - cellZ = (s16)fCellZ; - - // Don't do DDA if straight down - if (normalized_dir[1] >= 1.0f || normalized_dir[1] <= -1.0f) - { - find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length); - return; - } - - // Get cells we cross using DDA - if (abs(dir[0]) >= abs(dir[2])) - step = abs(dir[0]) / CELL_SIZE; - else - step = abs(dir[2]) / CELL_SIZE; - - dx = dir[0] / step / CELL_SIZE; - dz = dir[2] / step / CELL_SIZE; - - for (i = 0; i < step && *hit_surface == NULL; i++) - { - find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length); - - // Move cell coordinate - fCellX += dx; - fCellZ += dz; - cellX = (s16)fCellX; - cellZ = (s16)fCellZ; - } -} diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h index 6e79dca..82ba862 100644 --- a/src/engine/surface_collision.h +++ b/src/engine/surface_collision.h @@ -32,6 +32,6 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor); f32 find_water_level(f32 x, f32 z); f32 find_poison_gas_level(f32 x, f32 z); void debug_surface_list_info(f32 xPos, f32 zPos); -void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos); + #endif /* _SURFACE_COLLISION_H */ diff --git a/src/game/camera.c b/src/game/camera.c index 31e2390..af8dc34 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -638,8 +638,6 @@ void calc_y_to_curr_floor(f32 *posOff, f32 posMul, f32 posBound, f32 *focOff, f3 *focOff = -focBound; } } -//Compiler gets mad if I put this any further above. thanks refresh 7 -#include "../../enhancements/bettercamera.inc.c" void focus_on_mario(Vec3f focus, Vec3f pos, f32 posYOff, f32 focYOff, f32 dist, s16 pitch, s16 yaw) { Vec3f marioPos; @@ -2793,8 +2791,6 @@ void set_camera_mode(struct Camera *c, s16 mode, s16 frames) { struct LinearTransitionPoint *start = &sModeInfo.transitionStart; struct LinearTransitionPoint *end = &sModeInfo.transitionEnd; - if (mode != CAM_MODE_NEWCAM && gLakituState.mode != CAM_MODE_NEWCAM) - { if (mode == CAMERA_MODE_WATER_SURFACE && gCurrLevelArea == AREA_TTM_OUTSIDE) { } else { // Clear movement flags that would affect the transition @@ -2838,7 +2834,6 @@ void set_camera_mode(struct Camera *c, s16 mode, s16 frames) { vec3f_get_dist_and_angle(start->focus, start->pos, &start->dist, &start->pitch, &start->yaw); vec3f_get_dist_and_angle(end->focus, end->pos, &end->dist, &end->pitch, &end->yaw); } - } } /** @@ -2923,7 +2918,7 @@ void update_lakitu(struct Camera *c) { gLakituState.roll += sHandheldShakeRoll; gLakituState.roll += gLakituState.keyDanceRoll; - if (c->mode != CAMERA_MODE_C_UP && c->cutscene == 0 && c->mode != CAM_MODE_NEWCAM) { + if (c->mode != CAMERA_MODE_C_UP && c->cutscene == 0) { gCheckingSurfaceCollisionsForCamera = TRUE; distToFloor = find_floor(gLakituState.pos[0], gLakituState.pos[1] + 20.0f, @@ -2956,7 +2951,7 @@ void update_camera(struct Camera *c) { update_camera_hud_status(c); if (c->cutscene == 0) { // Only process R_TRIG if 'fixed' is not selected in the menu - if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO && c->mode != CAM_MODE_NEWCAM) { + if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO) { if (gPlayer1Controller->buttonPressed & R_TRIG) { if (set_cam_angle(0) == CAM_ANGLE_LAKITU) { set_cam_angle(CAM_ANGLE_MARIO); @@ -2994,12 +2989,10 @@ void update_camera(struct Camera *c) { c->mode = gLakituState.mode; c->defMode = gLakituState.defMode; - if (c->mode != CAM_MODE_NEWCAM) { - camera_course_processing(c); - dummy_802877EC(c); - sCButtonsPressed = find_c_buttons_pressed(sCButtonsPressed, gPlayer1Controller->buttonPressed, - gPlayer1Controller->buttonDown); - } + camera_course_processing(c); + dummy_802877EC(c); + sCButtonsPressed = find_c_buttons_pressed(sCButtonsPressed, gPlayer1Controller->buttonPressed, + gPlayer1Controller->buttonDown); if (c->cutscene != 0) { sYawSpeed = 0; @@ -3037,10 +3030,6 @@ void update_camera(struct Camera *c) { mode_cannon_camera(c); break; - case CAM_MODE_NEWCAM: - newcam_loop(c); - break; - default: mode_mario_camera(c); } @@ -3100,10 +3089,6 @@ void update_camera(struct Camera *c) { case CAMERA_MODE_SPIRAL_STAIRS: mode_spiral_stairs_camera(c); break; - - case CAM_MODE_NEWCAM: - newcam_loop(c); - break; } } } @@ -3379,13 +3364,6 @@ void init_camera(struct Camera *c) { gLakituState.nextYaw = gLakituState.yaw; c->yaw = gLakituState.yaw; c->nextYaw = gLakituState.yaw; - - if (newcam_active == 1) - { - gLakituState.mode = CAM_MODE_NEWCAM; - gLakituState.defMode = CAM_MODE_NEWCAM; - newcam_init(c, 0); - } } /** @@ -5474,8 +5452,6 @@ void set_camera_mode_8_directions(struct Camera *c) { s8DirModeBaseYaw = 0; s8DirModeYawOffset = 0; } - if (newcam_active == 1) - c->mode = CAM_MODE_NEWCAM; } /** @@ -5494,8 +5470,6 @@ void set_camera_mode_close_cam(u8 *mode) { sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; *mode = CAMERA_MODE_CLOSE; } - if (newcam_active == 1) - *mode = CAM_MODE_NEWCAM; } /** @@ -5520,8 +5494,6 @@ void set_camera_mode_radial(struct Camera *c, s16 transitionTime) { } sModeOffsetYaw = 0; } - if (newcam_active == 1) - c->mode = CAM_MODE_NEWCAM; } /** @@ -6886,7 +6858,6 @@ s16 cutscene_object(u8 cutscene, struct Object *o) { void update_camera_yaw(struct Camera *c) { c->nextYaw = calculate_yaw(c->focus, c->pos); c->yaw = c->nextYaw; - newcam_apply_outside_values(c,0); } void cutscene_reset_spline(void) { @@ -9150,12 +9121,7 @@ CmdRet cutscene_non_painting_end(struct Camera *c) { if (c->defMode == CAMERA_MODE_CLOSE) { c->mode = CAMERA_MODE_CLOSE; - } else - if (c->defMode == CAM_MODE_NEWCAM) { - c->mode = CAM_MODE_NEWCAM; - } - else - { + } else { c->mode = CAMERA_MODE_FREE_ROAM; } @@ -9829,7 +9795,6 @@ CmdRet cutscene_sliding_doors_follow_mario(struct Camera *c) { Vec3f pos; UNUSED u32 pad[4]; - newcam_apply_outside_values(c, 1); vec3f_copy(pos, c->pos); // Update cvar1 with mario's position (the y value doesn't change) sCutsceneVars[1].point[0] = sMarioCamState->pos[0]; @@ -10053,11 +10018,6 @@ CmdRet cutscene_unused_exit_focus_mario(struct Camera *c) { */ CmdRet cutscene_exit_painting_end(struct Camera *c) { c->mode = CAMERA_MODE_CLOSE; - if (newcam_active == 1) { - c->mode = CAM_MODE_NEWCAM; - } else { - c->mode = CAMERA_MODE_CLOSE; - } c->cutscene = 0; gCutsceneTimer = CUTSCENE_STOP; sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT; @@ -10217,12 +10177,10 @@ CmdRet cutscene_door_follow_mario(struct Camera *c) { * Ends the door cutscene. Sets the camera mode to close mode unless the default is free roam. */ CmdRet cutscene_door_end(struct Camera *c) { - if (c->defMode == CAMERA_MODE_CLOSE) { - c->mode = CAMERA_MODE_CLOSE; - } else if (c->defMode == CAM_MODE_NEWCAM) { - c->mode = CAM_MODE_NEWCAM; - } else { + if (c->defMode == CAMERA_MODE_FREE_ROAM) { c->mode = CAMERA_MODE_FREE_ROAM; + } else { + c->mode = CAMERA_MODE_CLOSE; } c->cutscene = 0; diff --git a/src/game/camera.h b/src/game/camera.h index 393fa10..ee48f91 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -110,7 +110,6 @@ #define CAMERA_MODE_8_DIRECTIONS 0x0E // AKA Parallel Camera, Bowser Courses & Rainbow Road #define CAMERA_MODE_FREE_ROAM 0x10 #define CAMERA_MODE_SPIRAL_STAIRS 0x11 -#define CAM_MODE_NEWCAM 0x12 #define CAM_MOVE_RETURN_TO_MIDDLE 0x0001 #define CAM_MOVE_ZOOMED_OUT 0x0002 diff --git a/src/game/game.c b/src/game/game.c index 42c6a7f..cc512f0 100644 --- a/src/game/game.c +++ b/src/game/game.c @@ -17,7 +17,6 @@ #include "main_entry.h" #include #include "game.h" -#include "../../enhancements/bettercamera.h" // FIXME: I'm not sure all of these variables belong in this file, but I don't // know of a good way to split them diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index 2abe687..14499ab 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -18,7 +18,6 @@ #include "ingame_menu.h" #include "print.h" #include "engine/math_util.h" -#include "../../enhancements/bettercamera.h" extern Gfx *gDisplayListHead; extern s32 gGlobalTimer; @@ -2628,8 +2627,7 @@ s16 render_pause_courses_and_castle(void) { #ifdef VERSION_EU gInGameLanguage = eu_get_language(); #endif - if (newcam_option_open == 0) - { + switch (gDialogBoxState) { case DIALOG_STATE_OPENING: gDialogLineNum = 1; @@ -2705,14 +2703,6 @@ s16 render_pause_courses_and_castle(void) { if (gDialogTextAlpha < 250) { gDialogTextAlpha += 25; } - } - else - { - shade_screen(); - newcam_display_options(); - } - newcam_check_pause_buttons(); - newcam_render_option_text(); return 0; } diff --git a/src/game/mario.c b/src/game/mario.c index 89f5562..a64a46c 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -31,7 +31,6 @@ #include "sound_init.h" #include "engine/surface_collision.h" #include "level_table.h" -#include "../../enhancements/bettercamera.h" u32 unused80339F10; s8 filler80339F1C[20]; @@ -1306,10 +1305,7 @@ void update_mario_joystick_inputs(struct MarioState *m) { } if (m->intendedMag > 0.0f) { - if (gLakituState.mode != CAM_MODE_NEWCAM) - m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + m->area->camera->yaw; - else - m->intendedYaw = atan2s(-controller->stickY, controller->stickX)-newcam_yaw+0x4000; + m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + m->area->camera->yaw; m->input |= INPUT_NONZERO_ANALOG; } else { m->intendedYaw = m->faceAngle[1]; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index 47db03b..c151ce6 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -12,7 +12,6 @@ #include "save_file.h" #include "audio/external.h" #include "engine/graph_node.h" -#include "../../enhancements/bettercamera.h" void play_flip_sounds(struct MarioState *m, s16 frame1, s16 frame2, s16 frame3) { s32 animFrame = m->marioObj->header.gfx.unk38.animFrame; @@ -1620,12 +1619,7 @@ s32 act_shot_from_cannon(struct MarioState *m) { case AIR_STEP_LANDED: set_mario_action(m, ACT_DIVE_SLIDE, 0); m->faceAngle[0] = 0; - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); break; case AIR_STEP_HIT_WALL: @@ -1638,13 +1632,7 @@ s32 act_shot_from_cannon(struct MarioState *m) { m->particleFlags |= PARTICLE_1; set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } - + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); break; case AIR_STEP_HIT_LAVA_WALL: @@ -1671,37 +1659,20 @@ s32 act_flying(struct MarioState *m) { if (m->input & INPUT_Z_PRESSED) { if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); } return set_mario_action(m, ACT_GROUND_POUND, 1); } if (!(m->flags & MARIO_WING_CAP)) { if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); } return set_mario_action(m, ACT_FREEFALL, 0); } if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { - if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } - } + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); } if (m->actionState == 0) { @@ -1741,12 +1712,7 @@ s32 act_flying(struct MarioState *m) { set_anim_to_frame(m, 7); m->faceAngle[0] = 0; - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); break; case AIR_STEP_HIT_WALL: @@ -1764,12 +1730,7 @@ s32 act_flying(struct MarioState *m) { m->particleFlags |= PARTICLE_1; set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); } else { if (m->actionTimer++ == 0) { play_sound(SOUND_ACTION_HIT, m->marioObj->header.gfx.cameraToObject); @@ -1844,12 +1805,7 @@ s32 act_flying_triple_jump(struct MarioState *m) { #ifndef VERSION_JP if (m->input & (INPUT_B_PRESSED | INPUT_Z_PRESSED)) { if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); } if (m->input & INPUT_B_PRESSED) { return set_mario_action(m, ACT_DIVE, 0); @@ -1887,12 +1843,7 @@ s32 act_flying_triple_jump(struct MarioState *m) { if (m->vel[1] < 4.0f) { if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { - if (newcam_active == 0) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); - } else { - m->area->camera->mode = CAM_MODE_NEWCAM; - gLakituState.mode = CAM_MODE_NEWCAM; - } + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); } if (m->forwardVel < 32.0f) { @@ -1902,6 +1853,10 @@ s32 act_flying_triple_jump(struct MarioState *m) { set_mario_action(m, ACT_FLYING, 1); } + if (m->actionTimer++ == 10 && m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + } + update_air_without_turn(m); switch (perform_air_step(m, 0)) { diff --git a/src/game/save_file.c b/src/game/save_file.c index cae3c1b..15ae6a4 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -9,13 +9,11 @@ #include "save_file.h" #include "sound_init.h" #include "level_table.h" -#include "../../enhancements/bettercamera.h" #define MENU_DATA_MAGIC 0x4849 #define SAVE_FILE_MAGIC 0x4441 -STATIC_ASSERT(sizeof(struct SaveBuffer) <= EEPROM_SIZE, "eeprom buffer size higher than intended"); -STATIC_ASSERT(sizeof(struct SaveBuffer) >= EEPROM_SIZE, "eeprom buffer size lower than intended"); +STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match"); extern struct SaveBuffer gSaveBuffer; @@ -552,48 +550,6 @@ u16 save_file_get_sound_mode(void) { return gSaveBuffer.menuData[0].soundMode; } -void save_file_set_setting(void) { - - gSaveBuffer.menuData[0].camx = newcam_sensitivityX; - gSaveBuffer.menuData[0].camy = newcam_sensitivityY; - gSaveBuffer.menuData[0].invertx = newcam_invertX; - gSaveBuffer.menuData[0].inverty = newcam_invertY; - gSaveBuffer.menuData[0].camc = newcam_aggression; - gSaveBuffer.menuData[0].camp = newcam_panlevel; - gSaveBuffer.menuData[0].analogue = newcam_analogue; - - gSaveBuffer.menuData[0].firsttime = 1; - - - gMainMenuDataModified = TRUE; - save_main_menu_data(); -} - -void save_file_get_setting(void) { - newcam_sensitivityX = gSaveBuffer.menuData[0].camx; - newcam_sensitivityY = gSaveBuffer.menuData[0].camy; - newcam_invertX = gSaveBuffer.menuData[0].invertx; - newcam_invertY = gSaveBuffer.menuData[0].inverty; - newcam_aggression = gSaveBuffer.menuData[0].camc; - newcam_panlevel = gSaveBuffer.menuData[0].camp; - newcam_analogue = gSaveBuffer.menuData[0].analogue; - -} - -u8 save_check_firsttime(void) -{ - return gSaveBuffer.menuData[0].firsttime; -} - - -void save_set_firsttime(void) -{ - gSaveBuffer.menuData[0].firsttime = 1; - - gMainMenuDataModified = TRUE; - save_main_menu_data(); -} - void save_file_move_cap_to_default_location(void) { if (save_file_get_flags() & SAVE_FLAG_CAP_ON_GROUND) { switch (gSaveBuffer.files[gCurrSaveFileNum - 1][0].capLevel) { diff --git a/src/game/save_file.h b/src/game/save_file.h index 3930c1f..18eafc2 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -6,7 +6,7 @@ #include "course_table.h" -#define EEPROM_SIZE 0x800 +#define EEPROM_SIZE 0x200 #define NUM_SAVE_FILES 4 struct SaveBlockSignature @@ -50,24 +50,16 @@ struct MainMenuSaveData // on the high score screen. u32 coinScoreAges[NUM_SAVE_FILES]; u16 soundMode; - u8 camx; - u8 camy; - u8 analogue; - u8 invertx; - u8 inverty; - u8 camc; - u8 camp; - u8 firsttime; + #ifdef VERSION_EU u16 language; #define SUBTRAHEND 8 #else -#define SUBTRAHEND 15 +#define SUBTRAHEND 6 #endif - // Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU) - //u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; + u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; struct SaveBlockSignature signature; }; @@ -78,7 +70,6 @@ struct SaveBuffer struct SaveFile files[NUM_SAVE_FILES][2]; // The main menu data has two copies. If one is bad, the other is used as a backup. struct MainMenuSaveData menuData[2]; - u8 filler[1535]; //!I still haven't done an algorithm for this yet lol }; struct WarpNode; @@ -153,10 +144,6 @@ s32 save_file_get_cap_pos(Vec3s capPos); void save_file_set_sound_mode(u16 mode); u16 save_file_get_sound_mode(void); void save_file_move_cap_to_default_location(void); -void save_set_firsttime(void); -u8 save_check_firsttime(void); -void save_file_get_setting(void); -void save_file_set_setting(void); void disable_warp_checkpoint(void); void check_if_should_set_warp_checkpoint(struct WarpNode *a); diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index c2034d3..07a5169 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -141,7 +141,6 @@ void main_func(void) { audio_init(); sound_init(); - newcam_init_settings(); thread5_game_loop(NULL); #ifdef TARGET_WEB From a88fd9aa8215d8b1c277b5394f26ef3203e4a600 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 09:16:19 -0400 Subject: [PATCH 22/28] lerp all inputs with a lerper --- gamebridge/src/au.rs | 99 +++++++++++++++++++++++ gamebridge/src/controller.rs | 45 ----------- gamebridge/src/main.rs | 153 ++++++++++++++++++----------------- gamebridge/src/twitch.rs | 59 +++++--------- 4 files changed, 199 insertions(+), 157 deletions(-) create mode 100644 gamebridge/src/au.rs diff --git a/gamebridge/src/au.rs b/gamebridge/src/au.rs new file mode 100644 index 0000000..4a71221 --- /dev/null +++ b/gamebridge/src/au.rs @@ -0,0 +1,99 @@ +#[derive(Copy, Clone)] +pub(crate) struct Lerper { + extended_tick: u64, + lerp_time: f64, + goal: i64, + pub(crate) scalar: i64, + max: i64, + min: i64, +} + +impl Lerper { + pub(crate) fn init(lerp_time: f64, max: i64, min: i64, goal: i64) -> Lerper { + Lerper { + extended_tick: 0, + lerp_time: lerp_time, + goal: goal, + scalar: 0, // I hope to GOD that 0 is the resting point + max: max, + min: min, + } + } + + pub(crate) fn add(&mut self, new_scalar: i64) { + self.scalar += new_scalar; + } + + pub(crate) fn update(&mut self, new_scalar: i64) { + self.scalar = new_scalar; + } + + pub(crate) fn apply(&mut self, now: u64) -> i64 { + let scalar = self.scalar; + self.scalar = match scalar { + _ if scalar == self.goal => self.goal, + _ if scalar == self.max => { + self.extended_tick = now; + scalar -1 + }, + _ if scalar == self.min => { + self.extended_tick = now; + scalar - 1 + }, + _ => { + let t = (now - self.extended_tick) as f64 / self.lerp_time; + lerp(self.scalar, 0, t) + }, + }; + + if self.scalar >= self.max { + return self.max; + } + + if self.scalar <= self.min { + return self.min; + } + + log::info!("before: {}, after: {}", scalar, self.scalar); + self.scalar + } + + pub(crate) fn pressed(&mut self, threshold: i64) -> bool { + if self.scalar <= threshold { + self.scalar = 0; + } + + self.scalar >= threshold + } +} + +fn lerp(start: i64, end: i64, t: f64) -> i64 { + (start as f64 * (1.0 - t) + (end as f64) * t) as i64 +} + +#[cfg(test)] +mod test { + #[test] + fn lerp_scale() { + for case in [(0.1, 10), (0.5, 31)].iter() { + let t = case.0; + let start = 127.0 * t; + assert_eq!(super::lerp(start as i64, 0, t), case.1); + } + } + + #[test] + fn lerper() { + use super::Lerper; + let mut lerper = Lerper::init(15.0, 127, -128, 0); + + for case in [(127, 3, 126), (100, 8, 66)].iter() { + let scalar = case.0; + let now = case.1; + let want = case.2; + + lerper.update(scalar); + assert_eq!(lerper.apply(now), want); + } + } +} diff --git a/gamebridge/src/controller.rs b/gamebridge/src/controller.rs index 3daa1d4..5458d39 100644 --- a/gamebridge/src/controller.rs +++ b/gamebridge/src/controller.rs @@ -20,12 +20,6 @@ bitflags! { } } -impl HiButtons { - pub(crate) fn clear(&mut self) { - self.bits = 0; - } -} - bitflags! { // 0x0001 C-Right // 0x0002 C-Left @@ -45,42 +39,3 @@ bitflags! { const L_BUTTON = 0x20; } } - -impl LoButtons { - pub(crate) fn clear(&mut self) { - self.bits = 0; - } -} - -pub(crate) fn test(st: crate::MTState) { - let mut lo: LoButtons = LoButtons::NONE; - let mut hi: HiButtons = HiButtons::NONE; - - loop { - use std::{thread::sleep, time::Duration}; - let one_second = Duration::new(1, 0); - - hi = HiButtons::A_BUTTON | HiButtons::START; - - { - println!("pressing a + start"); - let mut data = st.write().unwrap(); - data.controller[0] = hi.bits as u8; - data.controller[1] = lo.bits as u8; - } - - sleep(one_second); - - hi.clear(); - lo.clear(); - - { - println!("releasing a + start"); - let mut data = st.write().unwrap(); - data.controller[0] = hi.bits as u8; - data.controller[1] = lo.bits as u8; - } - - sleep(one_second); - } -} diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 48a5543..38c1e25 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -1,9 +1,12 @@ #[macro_use] extern crate bitflags; +pub(crate) mod au; pub(crate) mod controller; pub(crate) mod twitch; +use crate::au::Lerper; + use anyhow::{anyhow, Result}; use log::{debug, error, info, warn}; use std::{ @@ -12,15 +15,22 @@ use std::{ str::from_utf8, sync::{Arc, RwLock}, thread::spawn, - time::Instant, }; -#[derive(Debug)] pub(crate) struct State { - controller: [u8; 4], - last_got: Box, - ok: bool, frame: u64, + + stickx: Lerper, + sticky: Lerper, + a_button: Lerper, + b_button: Lerper, + z_button: Lerper, + r_button: Lerper, + start: Lerper, + c_left: Lerper, + c_right: Lerper, + c_up: Lerper, + c_down: Lerper, } pub(crate) type MTState = Arc>; @@ -32,12 +42,24 @@ fn main() -> Result<()> { let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; + const STICK_LERP_TIME: f64 = 330.0; // 330 frames to lerp stick positions down to 0 + const BUTTON_LERP_TIME: f64 = 20.0; // 20 frames to lerp button inputs down to 0 + let st = { let st = State { - controller: [0; 4], - last_got: Box::new(Instant::now()), - ok: true, frame: 0, + + stickx: Lerper::init(STICK_LERP_TIME, 127, -128, 0), + sticky: Lerper::init(STICK_LERP_TIME, 127, -128, 0), + a_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), + b_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), + z_button: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special + r_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), + start: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special + c_left: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), + c_right: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), + c_up: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), + c_down: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), }; Arc::new(RwLock::new(st)) @@ -50,10 +72,6 @@ fn main() -> Result<()> { spawn(move || twitch::run(st)); } - const LERP_TIME: f64 = 330.0; // 330 frames to lerp stick positions down to 0 - let mut xmax_frame: u64 = 0; - let mut ymax_frame: u64 = 0; - loop { let mut data = [0; 3]; debug!("waiting for vblank"); @@ -61,59 +79,63 @@ fn main() -> Result<()> { let str = from_utf8(&data)?; debug!("got data: {}", str); + let mut controller = [0; 4]; + match str { "OK\n" => { - let mut data = st.write().unwrap(); - data.frame += 1; - - let mut stickx = data.controller[2] as i8; - let mut sticky = data.controller[3] as i8; - - let dist = stick_distance(stickx, sticky); - if dist <= 10 { - stickx = 0; - sticky = 0; - xmax_frame = 0; - ymax_frame = 0; + { + let mut data = st.write().unwrap(); + data.frame += 1; } - stickx = match stickx { - 0 => stickx, - 127 => { - xmax_frame = data.frame; - stickx - 10 - }, - -128 => { - xmax_frame = data.frame; - stickx + 10 - }, - _ => { - let t = (data.frame - xmax_frame) as f64 / (LERP_TIME as f64); - lerp(stickx, 0, t) - }, - }; + let mut data = st.write().unwrap(); + let frame = data.frame + 1; - sticky = match sticky { - 0 => sticky, - 127 => { - ymax_frame = data.frame; - sticky - 10 - }, - -128 => { - ymax_frame = data.frame; - sticky + 10 - }, - _ => { - let t = (data.frame - ymax_frame) as f64 / (LERP_TIME as f64); - lerp(sticky, 0, t) - }, - }; + //data.stickx.update(data.controller[2] as i64); + //data.sticky.update(data.controller[3] as i64); + let mut stickx_scalar = data.stickx.apply(frame) as i8; + let mut sticky_scalar = data.sticky.apply(frame) as i8; - input.write(&data.controller)?; - data.controller[0] = 0; - data.controller[1] = 0; - data.controller[2] = stickx as u8; - data.controller[3] = sticky as u8; + let dist = stick_distance(stickx_scalar, sticky_scalar); + if dist <= 10 { + stickx_scalar = 0; + sticky_scalar = 0; + } + + use controller::{HiButtons, LoButtons}; + + let mut hi = HiButtons::NONE; + let mut lo = LoButtons::NONE; + const BUTTON_PUSH_THRESHOLD: i64 = 16; + + // high buttons + data.a_button.apply(frame); + if data.a_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::A_BUTTON; } + data.b_button.apply(frame); + if data.b_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::B_BUTTON; } + data.z_button.apply(frame); + if data.z_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::Z_BUTTON; } + data.start.apply(frame); + if data.start.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::START; } + info!("start: {}", data.start.scalar); + + data.r_button.apply(frame); + if data.r_button.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::R_BUTTON; } + data.c_up.apply(frame); + if data.c_up.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_UP; } + data.c_down.apply(frame); + if data.c_down.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_DOWN; } + data.c_left.apply(frame); + if data.c_left.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_LEFT; } + data.c_right.apply(frame); + if data.c_right.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_RIGHT; } + + controller[0] = hi.bits() as u8; + controller[1] = lo.bits() as u8; + controller[2] = stickx_scalar as u8; + controller[3] = sticky_scalar as u8; + + input.write(&controller)?; } "BYE" => { warn!("asked to exit by the game"); @@ -127,10 +149,6 @@ fn main() -> Result<()> { } } -fn lerp(start: i8, end: i8, t: f64) -> i8 { - (start as f64 * (1.0 - t) + (end as f64) * t) as i8 -} - fn stick_distance(x: i8, y: i8) -> i8 { let x = (x as f64).powi(2); let y = (y as f64).powi(2); @@ -139,15 +157,6 @@ fn stick_distance(x: i8, y: i8) -> i8 { #[cfg(test)] mod test { - #[test] - fn lerp_scale() { - for case in [(0.1, 10), (0.5, 31)].iter() { - let t = case.0; - let start = 127.0 * t; - assert_eq!(crate::lerp(start as i8, 0, t), case.1); - } - } - #[test] fn stick_distance() { for case in [ diff --git a/gamebridge/src/twitch.rs b/gamebridge/src/twitch.rs index 795e1bc..068173b 100644 --- a/gamebridge/src/twitch.rs +++ b/gamebridge/src/twitch.rs @@ -1,7 +1,4 @@ -use crate::{ - controller::{HiButtons, LoButtons}, - MTState, -}; +use crate::MTState; use tokio::stream::StreamExt as _; use twitchchat::{events, Control, Dispatcher, Runner, Status}; @@ -80,46 +77,28 @@ async fn run_loop( eprintln!("{} left {}", msg.name, msg.channel); } Some(msg) = pmsg.next() => { - let mut hi = HiButtons::NONE; - let mut lo = LoButtons::NONE; - let mut stickx: i8 = 0; - let mut sticky: i8 = 0; + let chatline = msg.data.to_string(); + let chatline = chatline.to_ascii_lowercase(); + let mut data = st.write().unwrap(); - { - let data = st.read().unwrap(); - stickx = data.controller[2] as i8; - sticky = data.controller[3] as i8; - } - - let mut data = msg.data.to_string(); - let data = data.to_ascii_lowercase(); - - match data.as_str() { - "a" => hi = hi | HiButtons::A_BUTTON, - "b" => hi = hi | HiButtons::B_BUTTON, - "z" => hi = hi | HiButtons::Z_BUTTON, - "r" => lo = lo | LoButtons::R_BUTTON, - "cup" => lo = lo | LoButtons::C_UP, - "cdown" => lo = lo | LoButtons::C_DOWN, - "cleft" => lo = lo | LoButtons::C_LEFT, - "cright" => lo = lo | LoButtons::C_RIGHT, - "start" => hi = hi | HiButtons::START, - "up" => sticky = 127, - "down" => sticky = -128, - "left" => stickx = -128, - "right" => stickx = 127, - "stop" => {stickx = 0; sticky = 0;}, + match chatline.as_str() { + "a" => data.a_button.add(1024), + "b" => data.b_button.add(1024), + "z" => data.z_button.add(1024), + "r" => data.r_button.add(1024), + "cup" => data.c_up.add(1024), + "cdown" => data.c_down.add(1024), + "cleft" => data.c_left.add(1024), + "cright" => data.c_right.add(1024), + "start" => data.start.add(1024), + "up" => data.stickx.add(127), + "down" => data.sticky.add(-128), + "left" => data.stickx.add(-128), + "right" => data.stickx.add(127), + "stop" => {data.stickx.update(0); data.sticky.update(0);}, _ => {}, } - { - let mut data = st.write().unwrap(); - data.controller[0] = hi.bits() as u8; - data.controller[1] = lo.bits() as u8; - data.controller[2] = stickx as u8; - data.controller[3] = sticky as u8; - } - eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data); } From e1a0d912fe314bb76e5118605904040975c897ab Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 09:43:14 -0400 Subject: [PATCH 23/28] Revert "Revert "integrate puppycam patch"" This reverts commit b9fd048aad800d559a244cb16e0e5394af136465. --- Makefile | 3 + enhancements/bettercamera.h | 48 ++ enhancements/bettercamera.inc.c | 882 ++++++++++++++++++++++++++++++ enhancements/bettercamera.txt | 7 + include/segments.h | 10 +- include/text_strings.h.in | 14 + src/engine/math_util.c | 31 +- src/engine/math_util.h | 6 + src/engine/surface_collision.c | 165 ++++++ src/engine/surface_collision.h | 2 +- src/game/camera.c | 62 ++- src/game/camera.h | 1 + src/game/game.c | 1 + src/game/ingame_menu.c | 12 +- src/game/mario.c | 6 +- src/game/mario_actions_airborne.c | 71 ++- src/game/save_file.c | 46 +- src/game/save_file.h | 21 +- src/pc/pc_main.c | 1 + 19 files changed, 1349 insertions(+), 40 deletions(-) create mode 100644 enhancements/bettercamera.h create mode 100644 enhancements/bettercamera.inc.c create mode 100644 enhancements/bettercamera.txt diff --git a/Makefile b/Makefile index ccf81a9..1d6ba20 100644 --- a/Makefile +++ b/Makefile @@ -471,6 +471,9 @@ asm/boot.s: $(BUILD_DIR)/lib/bin/ipl3_font.bin $(BUILD_DIR)/lib/bin/ipl3_font.bin: lib/ipl3_font.png $(IPLFONTUTIL) e $< $@ +#Required so the compiler doesn't complain about this not existing. +$(BUILD_DIR)/src/game/camera.o: $(BUILD_DIR)/include/text_strings.h + $(BUILD_DIR)/include/text_strings.h: include/text_strings.h.in $(TEXTCONV) charmap.txt $< $@ diff --git a/enhancements/bettercamera.h b/enhancements/bettercamera.h new file mode 100644 index 0000000..2896103 --- /dev/null +++ b/enhancements/bettercamera.h @@ -0,0 +1,48 @@ +enum newcam_flagvalues +{ + NC_FLAG_XTURN = 0x0001,//If this flag is set, the camera's yaw can be moved by the player. + NC_FLAG_YTURN = 0x0002, //If this flag is set, the camera's pitch can be moved by the player. + NC_FLAG_ZOOM = 0x0004, //If this flag is set, the camera's distance can be set by the player. + NC_FLAG_8D = 0x0008, //If this flag is set, the camera will snap to an 8 directional axis + NC_FLAG_4D = 0x0010, //If this flag is set, the camera will snap to a 4 directional axis + NC_FLAG_2D = 0x0020, //If this flag is set, the camera will stick to 2D. + NC_FLAG_FOCUSX = 0x0040, //If this flag is set, the camera will point towards its focus on the X axis. + NC_FLAG_FOCUSY = 0x0080, //If this flag is set, the camera will point towards its focus on the Y axis. + NC_FLAG_FOCUSZ = 0x0100, //If this flag is set, the camera will point towards its focus on the Z axis. + NC_FLAG_POSX = 0x0200, //If this flag is set, the camera will move along the X axis. + NC_FLAG_POSY = 0x0400, //If this flag is set, the camera will move along the Y axis. + NC_FLAG_POSZ = 0x0800, //If this flag is set, the camera will move along the Z axis. + NC_FLAG_COLLISION = 0x1000, //If this flag is set, the camera will collide and correct itself with terrain. + NC_FLAG_SLIDECORRECT = 0x2000, //If this flag is set, the camera will attempt to centre itself behind Mario whenever he's sliding. + + NC_MODE_NORMAL = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, + NC_MODE_SLIDE = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION | NC_FLAG_SLIDECORRECT, + NC_MODE_FIXED = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ, + NC_MODE_2D = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, + NC_MODE_8D = NC_FLAG_XTURN | NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_8D | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, + NC_MODE_FIXED_NOMOVE = 0x0000, + NC_MODE_NOTURN = NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION, + NC_MODE_NOROTATE = NC_FLAG_YTURN | NC_FLAG_ZOOM | NC_FLAG_FOCUSX | NC_FLAG_FOCUSY | NC_FLAG_FOCUSZ | NC_FLAG_POSX | NC_FLAG_POSY | NC_FLAG_POSZ | NC_FLAG_COLLISION + +}; + +extern void newcam_display_options(void); +extern void newcam_check_pause_buttons(void); +extern void newcam_init_settings(void); +extern void newcam_render_option_text(void); +extern void newcam_diagnostics(void); + +extern u8 newcam_option_open; + +extern u8 newcam_sensitivityX; //How quick the camera works. +extern u8 newcam_sensitivityY; +extern u8 newcam_invertX; +extern u8 newcam_invertY; +extern u8 newcam_panlevel; //How much the camera sticks out a bit in the direction you're looking. +extern u8 newcam_aggression; //How much the camera tries to centre itself to Mario's facing and movement. +extern u8 newcam_active; // basically the thing that governs if newcam is on. +extern u8 newcam_analogue; +extern u16 newcam_intendedmode; + +extern u16 newcam_mode; +extern s16 newcam_yaw; diff --git a/enhancements/bettercamera.inc.c b/enhancements/bettercamera.inc.c new file mode 100644 index 0000000..7320aa9 --- /dev/null +++ b/enhancements/bettercamera.inc.c @@ -0,0 +1,882 @@ +#include "sm64.h" +#include "game/camera.h" +#include "game/level_update.h" +#include "game/print.h" +#include "engine/math_util.h" +#include "game/segment2.h" +#include "game/save_file.h" +#include "bettercamera.h" +#include "include/text_strings.h" +#include "engine/surface_collision.h" + + + +/** +Quick explanation of the camera modes + +NC_MODE_NORMAL: Standard mode, allows dualaxial movement and free control of the camera. +NC_MODE_FIXED: Disables control of camera, and the actual position of the camera doesn't update. +NC_MODE_2D: Disables horizontal control of the camera and locks Mario's direction to the X axis. NYI though. +NC_MODE_8D: 8 directional movement. Similar to standard, except the camera direction snaps to 8 directions. +NC_MODE_FIXED_NOMOVE: Disables control and movement of the camera. +NC_MODE_NOTURN: Disables horizontal and vertical control of the camera. +**/ + +//!A bunch of developer intended options, to cover every base, really. +//#define NEWCAM_DEBUG //Some print values for puppycam. Not useful anymore, but never hurts to keep em around. +//#define nosound //If for some reason you hate the concept of audio, you can disable it. +//#define noaccel //Disables smooth movement of the camera with the C buttons. +#define DEGRADE 0.1f //What percent of the remaining camera movement is degraded. Default is 10% + + +//!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes. +///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons. +struct newcam_hardpos +{ + u8 newcam_hard_levelID; + u8 newcam_hard_areaID; + u8 newcam_hard_permaswap; + u16 newcam_hard_modeset; + s16 newcam_hard_X1; + s16 newcam_hard_Y1; + s16 newcam_hard_Z1; + s16 newcam_hard_X2; + s16 newcam_hard_Y2; + s16 newcam_hard_Z2; + s16 newcam_hard_camX; + s16 newcam_hard_camY; + s16 newcam_hard_camZ; + s16 newcam_hard_lookX; + s16 newcam_hard_lookY; + s16 newcam_hard_lookZ; +}; + +///This is the bit that defines where the angles happen. They're basically environment boxes that dictate camera behaviour. +//Permaswap is a boolean that simply determines wether or not when the camera changes at this point it stays changed. 0 means it resets when you leave, and 1 means it stays changed. +//The camera position fields accept "32767" as an ignore flag. +struct newcam_hardpos newcam_fixedcam[] = +{ +{/*Level ID*/ 16,/*Area ID*/ 1,/*Permaswap*/ 0,/*Mode*/ NC_MODE_FIXED_NOMOVE, //Standard params. +/*X begin*/ -540,/*Y begin*/ 800,/*Z begin*/ -3500, //Where the activation box begins +/*X end*/ 540,/*Y end*/ 2000,/*Z end*/ -1500, //Where the activation box ends. +/*Cam X*/ 0,/*Cam Y*/ 1500,/*Cam Z*/ -1000, //The position the camera gets placed for NC_MODE_FIXED and NC_MODE_FIXED_NOMOVE +/*Look X*/ 0,/*Look Y*/ 800,/*Look Z*/ -2500}, //The position the camera looks at for NC_MODE_FIXED_NOMOVE +}; + + +#ifdef noaccel + u8 accel = 255; + #else + u8 accel = 10; +#endif // noaccel + +s16 newcam_yaw; //Z axis rotation +s8 newcam_yaw_acc; +s16 newcam_tilt = 1500; //Y axis rotation +s8 newcam_tilt_acc; +u16 newcam_distance = 750; //The distance the camera stays from the player +u16 newcam_distance_target = 750; //The distance the player camera tries to reach. +f32 newcam_pos_target[3]; //The position the camera is basing calculations off. *usually* Mario. +f32 newcam_pos[3]; //Position the camera is in the world +f32 newcam_lookat[3]; //Position the camera is looking at +f32 newcam_framessincec[2]; +f32 newcam_extheight = 125; +u8 newcam_centering = 0; // The flag that depicts wether the camera's goin gto try centering. +s16 newcam_yaw_target; // The yaw value the camera tries to set itself to when the centre flag is active. Is set to Mario's face angle. +f32 newcam_turnwait; // The amount of time to wait after landing before allowing the camera to turn again +f32 newcam_pan_x; +f32 newcam_pan_z; +u8 newcam_cstick_down = 0; //Just a value that triggers true when the player 2 stick is moved in 8 direction move to prevent holding it down. +u8 newcam_target; + +u8 newcam_sensitivityX; //How quick the camera works. +u8 newcam_sensitivityY; +u8 newcam_invertX; //Reverses movement of the camera axis. +u8 newcam_invertY; +u8 newcam_panlevel; //How much the camera sticks out a bit in the direction you're looking. +u8 newcam_aggression; //How much the camera tries to centre itself to Mario's facing and movement. +u8 newcam_analogue; //Wether to accept inputs from a player 2 joystick, and then disables C button input. +s16 newcam_distance_values[] = {750,1250,2000}; +u8 newcam_active = 1; // basically the thing that governs if newcam is on. +u16 newcam_mode; +u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another. +u16 newcam_modeflags; + +u8 newcam_option_open = 0; +s8 newcam_option_selection = 0; +f32 newcam_option_timer = 0; +u8 newcam_option_index = 0; +u8 newcam_option_scroll = 0; +u8 newcam_option_scroll_last = 0; +u8 newcam_total = 7; //How many options there are in newcam_uptions. + +u8 newcam_options[][64] = {{NC_ANALOGUE}, {NC_CAMX}, {NC_CAMY}, {NC_INVERTX}, {NC_INVERTY}, {NC_CAMC}, {NC_CAMP}}; +u8 newcam_flags[][64] = {{NC_DISABLED}, {NC_ENABLED}}; +u8 newcam_strings[][64] = {{NC_BUTTON}, {NC_BUTTON2}, {NC_OPTION}, {NC_HIGHLIGHT}}; + +///This is called at every level initialisation. +void newcam_init(struct Camera *c, u8 dv) +{ + newcam_tilt = 1500; + newcam_distance_target = newcam_distance_values[dv]; + newcam_yaw = -c->yaw+0x4000; //Mario and the camera's yaw have this offset between them. + newcam_mode = NC_MODE_NORMAL; + ///This here will dictate what modes the camera will start in at the beginning of a level. Below are some examples. + switch (gCurrLevelNum) + { + case LEVEL_BITDW: newcam_yaw = 0x4000; newcam_mode = NC_MODE_8D; newcam_tilt = 4000; newcam_distance_target = newcam_distance_values[2]; break; + case LEVEL_BITFS: newcam_yaw = 0x4000; newcam_mode = NC_MODE_8D; newcam_tilt = 4000; newcam_distance_target = newcam_distance_values[2]; break; + case LEVEL_BITS: newcam_yaw = 0x4000; newcam_mode = NC_MODE_8D; newcam_tilt = 4000; newcam_distance_target = newcam_distance_values[2]; break; + case LEVEL_WF: newcam_yaw = 0x4000; newcam_tilt = 2000; newcam_distance_target = newcam_distance_values[1]; break; + case LEVEL_RR: newcam_yaw = 0x6000; newcam_tilt = 2000; newcam_distance_target = newcam_distance_values[2]; break; + case LEVEL_CCM: if (gCurrAreaIndex == 1) {newcam_yaw = -0x4000; newcam_tilt = 2000; newcam_distance_target = newcam_distance_values[1];} else newcam_mode = NC_MODE_SLIDE; break; + case LEVEL_WDW: newcam_yaw = 0x2000; newcam_tilt = 3000; newcam_distance_target = newcam_distance_values[1]; break; + case 27: newcam_mode = NC_MODE_SLIDE; break; + case LEVEL_THI: if (gCurrAreaIndex == 2) newcam_mode = NC_MODE_SLIDE; break; + } + + newcam_distance = newcam_distance_target; + newcam_intendedmode = newcam_mode; + newcam_modeflags = newcam_mode; +} +static f32 newcam_clamp(f32 value, f32 max, f32 min) +{ + if (value > max) + value = max; + if (value < min) + value = min; + return value; +} +///These are the default settings for Puppycam. You may change them to change how they'll be set for first timers. +void newcam_init_settings() +{ + if (save_check_firsttime()) + { + save_file_get_setting(); + newcam_clamp(newcam_sensitivityX, 10, 250); + newcam_clamp(newcam_sensitivityY, 10, 250); + newcam_clamp(newcam_aggression, 0, 100); + newcam_clamp(newcam_panlevel, 0, 100); + newcam_clamp(newcam_invertX, 0, 1); + newcam_clamp(newcam_invertY, 0, 1); + } + else + { + newcam_sensitivityX = 75; + newcam_sensitivityY = 75; + newcam_aggression = 0; + newcam_panlevel = 75; + newcam_invertX = 0; + newcam_invertY = 0; + save_set_firsttime(); + } +} + +/** Mathematic calculations. This stuffs so basic even *I* understand it lol +Basically, it just returns a position based on angle */ +static s16 lengthdir_x(f32 length, s16 dir) +{ + return (s16) (length * coss(dir)); +} +static s16 lengthdir_y(f32 length, s16 dir) +{ + return (s16) (length * sins(dir)); +} + +void newcam_diagnostics(void) +{ + print_text_fmt_int(32,192,"Lv %d",gCurrLevelNum); + print_text_fmt_int(32,176,"Area %d",gCurrAreaIndex); + print_text_fmt_int(32,160,"X %d",gMarioState->pos[0]); + print_text_fmt_int(32,144,"Y %d",gMarioState->pos[1]); + print_text_fmt_int(32,128,"Z %d",gMarioState->pos[2]); + print_text_fmt_int(32,112,"FLAGS %d",newcam_modeflags); + print_text_fmt_int(180,112,"INTM %d",newcam_intendedmode); + print_text_fmt_int(32,96,"TILT UP %d",newcam_tilt_acc); + print_text_fmt_int(32,80,"YAW UP %d",newcam_yaw_acc); + print_text_fmt_int(32,64,"YAW %d",newcam_yaw); + print_text_fmt_int(32,48,"TILT %d",newcam_tilt); + print_text_fmt_int(32,32,"DISTANCE %d",newcam_distance); +} + +static s16 newcam_adjust_value(s16 var, s16 val) +{ + var += val; + if (var > 100) + var = 100; + if (var < -100) + var = -100; + + return var; +} + +static f32 newcam_approach_float(f32 var, f32 val, f32 inc) +{ + if (var < val) + return min(var + inc, val); + else + return max(var - inc, val); +} + +static s16 newcam_approach_s16(s16 var, s16 val, s16 inc) +{ + if (var < val) + return max(var + inc, val); + else + return min(var - inc, val); +} + +static u8 ivrt(u8 axis) +{ + if (axis == 0) + { + if (newcam_invertX == 0) + return 1; + else + return -1; + } + else + { + if (newcam_invertY == 0) + return 1; + else + return -1; + } +} + +static void newcam_rotate_button(void) +{ + if ((newcam_modeflags & NC_FLAG_8D || newcam_modeflags & NC_FLAG_4D) && newcam_modeflags & NC_FLAG_XTURN) //8 directional camera rotation input for buttons. + { + if ((gPlayer1Controller->buttonPressed & L_CBUTTONS) && newcam_analogue == 0) + { + #ifndef nosound + play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); + #endif + if (newcam_modeflags & NC_FLAG_8D) + newcam_yaw_target = newcam_yaw_target+0x2000; + else + newcam_yaw_target = newcam_yaw_target+0x4000; + newcam_centering = 1; + } + else + if ((gPlayer1Controller->buttonPressed & R_CBUTTONS) && newcam_analogue == 0) + { + #ifndef nosound + play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); + #endif + if (newcam_modeflags & NC_FLAG_8D) + newcam_yaw_target = newcam_yaw_target-0x2000; + else + newcam_yaw_target = newcam_yaw_target-0x4000; + newcam_centering = 1; + } + } + else //Standard camera movement + if (newcam_modeflags & NC_FLAG_XTURN) + { + if ((gPlayer1Controller->buttonDown & L_CBUTTONS) && newcam_analogue == 0) + newcam_yaw_acc = newcam_adjust_value(newcam_yaw_acc,accel); + else if ((gPlayer1Controller->buttonDown & R_CBUTTONS) && newcam_analogue == 0) + newcam_yaw_acc = newcam_adjust_value(newcam_yaw_acc,-accel); + else + #ifdef noaccel + newcam_yaw_acc = 0; + #else + newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); + #endif + } + + if (gPlayer1Controller->buttonDown & U_CBUTTONS && newcam_modeflags & NC_FLAG_YTURN && newcam_analogue == 0) + newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,accel); + else if (gPlayer1Controller->buttonDown & D_CBUTTONS && newcam_modeflags & NC_FLAG_YTURN && newcam_analogue == 0) + newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,-accel); + else + #ifdef noaccel + newcam_tilt_acc = 0; + #else + newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); + #endif + + newcam_framessincec[0] += 1; + newcam_framessincec[1] += 1; + if ((gPlayer1Controller->buttonPressed & L_CBUTTONS) && newcam_modeflags & NC_FLAG_XTURN && !(newcam_modeflags & NC_FLAG_8D) && newcam_analogue == 0) + { + if (newcam_framessincec[0] < 6) + { + newcam_yaw_target = newcam_yaw+0x3000; + newcam_centering = 1; + #ifndef nosound + play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); + #endif + } + newcam_framessincec[0] = 0; + } + if ((gPlayer1Controller->buttonPressed & R_CBUTTONS) && newcam_modeflags & NC_FLAG_XTURN && !(newcam_modeflags & NC_FLAG_8D) && newcam_analogue == 0) + { + if (newcam_framessincec[1] < 6) + { + newcam_yaw_target = newcam_yaw-0x3000; + newcam_centering = 1; + #ifndef nosound + play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); + #endif + } + newcam_framessincec[1] = 0; + } + + + if (newcam_analogue == 1) //There's not much point in keeping this behind a check, but it wouldn't hurt, just incase any 2player shenanigans ever happen, it makes it easy to disable. + { //The joystick values cap at 80, so divide by 8 to get the same net result at maximum turn as the button + if (ABS(gPlayer2Controller->rawStickX) > 20 && newcam_modeflags & NC_FLAG_XTURN) + { + if (newcam_modeflags & NC_FLAG_8D) + { + if (newcam_cstick_down == 0) + { + newcam_cstick_down = 1; + newcam_centering = 1; + #ifndef nosound + play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gDefaultSoundArgs); + #endif + if (gPlayer2Controller->rawStickX > 20) + { + if (newcam_modeflags & NC_FLAG_8D) + newcam_yaw_target = newcam_yaw_target+0x2000; + else + newcam_yaw_target = newcam_yaw_target+0x4000; + } + else + { + if (newcam_modeflags & NC_FLAG_8D) + newcam_yaw_target = newcam_yaw_target-0x2000; + else + newcam_yaw_target = newcam_yaw_target-0x4000; + } + } + } + else + newcam_yaw_acc = newcam_adjust_value(newcam_yaw_acc,(-gPlayer2Controller->rawStickX/4)); + } + else + { + newcam_cstick_down = 0; + newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); + } + + if (ABS(gPlayer2Controller->rawStickY) > 20 && newcam_modeflags & NC_FLAG_YTURN) + newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->rawStickY/4)); + else + newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); + } +} + +static void newcam_zoom_button(void) +{ + //Smoothly move the camera to the new spot. + if (newcam_distance > newcam_distance_target) + { + newcam_distance -= 250; + if (newcam_distance < newcam_distance_target) + newcam_distance = newcam_distance_target; + } + if (newcam_distance < newcam_distance_target) + { + newcam_distance += 250; + if (newcam_distance > newcam_distance_target) + newcam_distance = newcam_distance_target; + } + + //When you press L and R together, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. + if (gPlayer1Controller->buttonDown & L_TRIG && gPlayer1Controller->buttonDown & R_TRIG && newcam_modeflags & NC_FLAG_ZOOM) + { + newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000; + newcam_centering = 1; + } + else //Each time the player presses R, but NOT L the camera zooms out more, until it hits the limit and resets back to close view. + if (gPlayer1Controller->buttonPressed & R_TRIG && newcam_modeflags & NC_FLAG_XTURN) + { + #ifndef nosound + play_sound(SOUND_MENU_CLICK_CHANGE_VIEW, gDefaultSoundArgs); + #endif + + if (newcam_distance_target == newcam_distance_values[0]) + newcam_distance_target = newcam_distance_values[1]; + else + if (newcam_distance_target == newcam_distance_values[1]) + newcam_distance_target = newcam_distance_values[2]; + else + newcam_distance_target = newcam_distance_values[0]; + + } + if (newcam_centering && newcam_modeflags & NC_FLAG_XTURN) + { + newcam_yaw = approach_s16_symmetric(newcam_yaw,newcam_yaw_target,0x800); + if (newcam_yaw = newcam_yaw_target) + newcam_centering = 0; + } + else + newcam_yaw_target = newcam_yaw; +} + +static void newcam_update_values(void) +{//For tilt, this just limits it so it doesn't go further than 90 degrees either way. 90 degrees is actually 16384, but can sometimes lead to issues, so I just leave it shy of 90. + u8 waterflag = 0; + if (newcam_modeflags & NC_FLAG_XTURN) + newcam_yaw += (ivrt(0)*(newcam_yaw_acc*(newcam_sensitivityX/10))); + if (((newcam_tilt < 12000 && newcam_tilt_acc*ivrt(1) > 0) || (newcam_tilt > -12000 && newcam_tilt_acc*ivrt(1) < 0)) && newcam_modeflags & NC_FLAG_YTURN) + newcam_tilt += (ivrt(1)*(newcam_tilt_acc*(newcam_sensitivityY/10))); + else + { + if (newcam_tilt > 12000) + newcam_tilt = 12000; + if (newcam_tilt < -12000) + newcam_tilt = -12000; + } + + if (newcam_turnwait > 0 && gMarioState->vel[1] == 0) + { + newcam_turnwait -= 1; + if (newcam_turnwait < 0) + newcam_turnwait = 0; + } + else + { + if (gMarioState->intendedMag > 0 && gMarioState->vel[1] == 0 && newcam_modeflags & NC_FLAG_XTURN) + newcam_yaw = (approach_s16_symmetric(newcam_yaw,-gMarioState->faceAngle[1]-0x4000,((newcam_aggression*(ABS(gPlayer1Controller->rawStickX/10)))*(gMarioState->forwardVel/32)))); + else + newcam_turnwait = 10; + } + + if (newcam_modeflags & NC_FLAG_SLIDECORRECT) + { + switch (gMarioState->action) + { + case ACT_BUTT_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; + case ACT_STOMACH_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; + case ACT_HOLD_BUTT_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; + case ACT_HOLD_STOMACH_SLIDE: if (gMarioState->forwardVel > 8) waterflag = 1; break; + } + } + switch (gMarioState->action) + { + case ACT_SHOT_FROM_CANNON: waterflag = 1; break; + case ACT_FLYING: waterflag = 1; break; + } + + if (gMarioState->action & ACT_FLAG_SWIMMING) + { + if (gMarioState->forwardVel > 2) + waterflag = 1; + } + + if (waterflag && newcam_modeflags & NC_FLAG_XTURN) + { + newcam_yaw = (approach_s16_symmetric(newcam_yaw,-gMarioState->faceAngle[1]-0x4000,(gMarioState->forwardVel*128))); + if ((signed)gMarioState->forwardVel > 1) + newcam_tilt = (approach_s16_symmetric(newcam_tilt,(-gMarioState->faceAngle[0]*0.8)+3000,(gMarioState->forwardVel*32))); + else + newcam_tilt = (approach_s16_symmetric(newcam_tilt,3000,32)); + } +} + +static void newcam_collision(void) +{ + struct Surface *surf; + Vec3f camdir; + Vec3f hitpos; + + camdir[0] = newcam_pos[0]-newcam_lookat[0]; + camdir[1] = newcam_pos[1]-newcam_lookat[1]; + camdir[2] = newcam_pos[2]-newcam_lookat[2]; + + + + find_surface_on_ray(newcam_pos_target, camdir, &surf, &hitpos); + + if (surf) + { + newcam_pos[0] = hitpos[0]; + newcam_pos[1] = approach_f32(hitpos[1],newcam_pos[1],25,-25); + newcam_pos[2] = hitpos[2]; + newcam_pan_x = 0; + newcam_pan_z = 0; + } +} + +static void newcam_set_pan(void) +{ + //Apply panning values based on Mario's direction. + if (gMarioState->action != ACT_HOLDING_BOWSER && gMarioState->action != ACT_SLEEPING && gMarioState->action != ACT_START_SLEEPING) + { + approach_f32_asymptotic_bool(&newcam_pan_x, lengthdir_x((160*newcam_panlevel)/100, -gMarioState->faceAngle[1]-0x4000), 0.05); + approach_f32_asymptotic_bool(&newcam_pan_z, lengthdir_y((160*newcam_panlevel)/100, -gMarioState->faceAngle[1]-0x4000), 0.05); + } + else + { + approach_f32_asymptotic_bool(&newcam_pan_x, 0, 0.05); + approach_f32_asymptotic_bool(&newcam_pan_z, 0, 0.05); + } + + newcam_pan_x = newcam_pan_x*(min(newcam_distance/newcam_distance_target,1)); + newcam_pan_z = newcam_pan_z*(min(newcam_distance/newcam_distance_target,1)); +} + +static void newcam_position_cam(void) +{ + f32 floorY = 0; + f32 floorY2 = 0; + s16 shakeX; + s16 shakeY; + + if (!(gMarioState->action & ACT_FLAG_SWIMMING)) + calc_y_to_curr_floor(&floorY, 1.f, 200.f, &floorY2, 0.9f, 200.f); + + newcam_update_values(); + shakeX = gLakituState.shakeMagnitude[1]; + shakeY = gLakituState.shakeMagnitude[0]; + //Fetch Mario's current position. Not hardcoded just for the sake of flexibility, though this specific bit is temp, because it won't always want to be focusing on Mario. + newcam_pos_target[0] = gMarioState->pos[0]; + newcam_pos_target[1] = gMarioState->pos[1]+newcam_extheight; + newcam_pos_target[2] = gMarioState->pos[2]; + //These will set the position of the camera to where Mario is supposed to be, minus adjustments for where the camera should be, on top of. + if (newcam_modeflags & NC_FLAG_POSX) + newcam_pos[0] = newcam_pos_target[0]+lengthdir_x(lengthdir_x(newcam_distance,newcam_tilt+shakeX),newcam_yaw+shakeY); + if (newcam_modeflags & NC_FLAG_POSY) + newcam_pos[2] = newcam_pos_target[2]+lengthdir_y(lengthdir_x(newcam_distance,newcam_tilt+shakeX),newcam_yaw+shakeY); + if (newcam_modeflags & NC_FLAG_POSZ) + newcam_pos[1] = newcam_pos_target[1]+lengthdir_y(newcam_distance,newcam_tilt+gLakituState.shakeMagnitude[0])+floorY; + if ((newcam_modeflags & NC_FLAG_FOCUSX) && (newcam_modeflags & NC_FLAG_FOCUSY) && (newcam_modeflags & NC_FLAG_FOCUSZ)) + newcam_set_pan(); + //Set where the camera wants to be looking at. This is almost always the place it's based off, too. + if (newcam_modeflags & NC_FLAG_FOCUSX) + newcam_lookat[0] = newcam_pos_target[0]-newcam_pan_x; + if (newcam_modeflags & NC_FLAG_FOCUSY) + newcam_lookat[1] = newcam_pos_target[1]+floorY2; + if (newcam_modeflags & NC_FLAG_FOCUSZ) + newcam_lookat[2] = newcam_pos_target[2]-newcam_pan_z; + + if (newcam_modeflags & NC_FLAG_COLLISION) + newcam_collision(); + +} + +//Nested if's baybeeeee +static void newcam_find_fixed(void) +{ + u8 i = 0; + newcam_mode = newcam_intendedmode; + newcam_modeflags = newcam_mode; + for (i = 0; i < sizeof(newcam_fixedcam); i++) + { + if (newcam_fixedcam[i].newcam_hard_levelID == gCurrLevelNum && newcam_fixedcam[i].newcam_hard_areaID == gCurrAreaIndex) + {//I didn't wanna just obliterate the horizontal plane of the IDE with a beefy if statement, besides, I think this runs slightly better anyway? + if (newcam_pos_target[0] > newcam_fixedcam[i].newcam_hard_X1) + if (newcam_pos_target[0] < newcam_fixedcam[i].newcam_hard_X2) + if (newcam_pos_target[1] > newcam_fixedcam[i].newcam_hard_Y1) + if (newcam_pos_target[1] < newcam_fixedcam[i].newcam_hard_Y2) + if (newcam_pos_target[2] > newcam_fixedcam[i].newcam_hard_Z1) + if (newcam_pos_target[2] < newcam_fixedcam[i].newcam_hard_Z2) + { + if (newcam_fixedcam[i].newcam_hard_permaswap) + newcam_intendedmode = newcam_fixedcam[i].newcam_hard_modeset; + newcam_mode = newcam_fixedcam[i].newcam_hard_modeset; + newcam_modeflags = newcam_mode; + + if (newcam_fixedcam[i].newcam_hard_camX != 32767 && !(newcam_modeflags & NC_FLAG_POSX)) + newcam_pos[0] = newcam_fixedcam[i].newcam_hard_camX; + if (newcam_fixedcam[i].newcam_hard_camY != 32767 && !(newcam_modeflags & NC_FLAG_POSY)) + newcam_pos[1] = newcam_fixedcam[i].newcam_hard_camY; + if (newcam_fixedcam[i].newcam_hard_camZ != 32767 && !(newcam_modeflags & NC_FLAG_POSZ)) + newcam_pos[2] = newcam_fixedcam[i].newcam_hard_camZ; + + if (newcam_fixedcam[i].newcam_hard_lookX != 32767 && !(newcam_modeflags & NC_FLAG_FOCUSX)) + newcam_lookat[0] = newcam_fixedcam[i].newcam_hard_lookX; + if (newcam_fixedcam[i].newcam_hard_lookY != 32767 && !(newcam_modeflags & NC_FLAG_FOCUSY)) + newcam_lookat[1] = newcam_fixedcam[i].newcam_hard_lookY; + if (newcam_fixedcam[i].newcam_hard_lookZ != 32767 && !(newcam_modeflags & NC_FLAG_FOCUSZ)) + newcam_lookat[2] = newcam_fixedcam[i].newcam_hard_lookZ; + + newcam_yaw = atan2s(newcam_pos[0]-newcam_pos_target[0],newcam_pos[2]-newcam_pos_target[2]); + } + } + } +} + +static void newcam_apply_values(struct Camera *c) +{ + + c->pos[0] = newcam_pos[0]; + c->pos[1] = newcam_pos[1]; + c->pos[2] = newcam_pos[2]; + + c->focus[0] = newcam_lookat[0]; + c->focus[1] = newcam_lookat[1]; + c->focus[2] = newcam_lookat[2]; + + gLakituState.pos[0] = newcam_pos[0]; + gLakituState.pos[1] = newcam_pos[1]; + gLakituState.pos[2] = newcam_pos[2]; + + gLakituState.focus[0] = newcam_lookat[0]; + gLakituState.focus[1] = newcam_lookat[1]; + gLakituState.focus[2] = newcam_lookat[2]; + + c->yaw = -newcam_yaw+0x4000; + gLakituState.yaw = -newcam_yaw+0x4000; + + //Adds support for wing mario tower + if (gMarioState->floor->type == SURFACE_LOOK_UP_WARP) { + if (save_file_get_total_star_count(gCurrSaveFileNum - 1, 0, 0x18) >= 10) { + if (newcam_tilt < -8000 && gMarioState->forwardVel == 0) { + level_trigger_warp(gMarioState, 1); + } + } + } + +} + +//The ingame cutscene system is such a spaghetti mess I actually have to resort to something as stupid as this to cover every base. +void newcam_apply_outside_values(struct Camera *c, u8 bit) +{ + if (newcam_modeflags == NC_FLAG_XTURN) + { + if (bit) + newcam_yaw = -gMarioState->faceAngle[1]-0x4000; + else + newcam_yaw = -c->yaw+0x4000; + } +} + +//Main loop. +void newcam_loop(struct Camera *c) +{ + newcam_rotate_button(); + newcam_zoom_button(); + newcam_position_cam(); + newcam_find_fixed(); + if (gMarioObject) + newcam_apply_values(c); + + //Just some visual information on the values of the camera. utilises ifdef because it's better at runtime. + #ifdef NEWCAM_DEBUG + newcam_diagnostics(); + #endif // NEWCAM_DEBUG +} + + + +//Displays a box. +void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) +{ + gDPPipeSync(gDisplayListHead++); + gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + gDPSetCycleType(gDisplayListHead++, G_CYC_FILL); + gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255)); + gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1); + gDPPipeSync(gDisplayListHead++); + gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE); +} + +//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :( +void newcam_change_setting(u8 toggle) +{ + switch (newcam_option_selection) + { + case 0: + newcam_analogue ^= 1; + break; + case 1: + if (newcam_sensitivityX > 10 && newcam_sensitivityX < 250) + newcam_sensitivityX += toggle; + break; + case 2: + if (newcam_sensitivityY > 10 && newcam_sensitivityY < 250) + newcam_sensitivityY += toggle; + break; + case 3: + newcam_invertX ^= 1; + break; + case 4: + newcam_invertY ^= 1; + break; + case 5: + if (newcam_aggression > 0 && newcam_aggression < 100) + newcam_aggression += toggle; + break; + case 6: + if (newcam_panlevel > 0 && newcam_panlevel < 100) + newcam_panlevel += toggle; + break; + } +} + +void newcam_text(s16 x, s16 y, u8 str[], u8 col) +{ + u8 textX; + textX = get_str_x_pos_from_center(x,str,10.0f); + gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); + print_generic_string(textX+1,y-1,str); + if (col != 0) + { + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + } + else + { + gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255); + } + print_generic_string(textX,y,str); +} + +//Options menu +void newcam_display_options() +{ + u8 i = 0; + u8 newstring[32]; + s16 scroll; + s16 scrollpos; + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + print_hud_lut_string(HUD_LUT_GLOBAL, 118, 40, newcam_strings[2]); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); + + if (newcam_total>4) + { + newcam_display_box(272,90,280,208,0x80,0x80,0x80); + scrollpos = (54)*((f32)newcam_option_scroll/(newcam_total-4)); + newcam_display_box(272,90+scrollpos,280,154+scrollpos,0xFF,0xFF,0xFF); + } + + + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT); + for (i = 0; i < newcam_total; i++) + { + scroll = 140-(32*i)+(newcam_option_scroll*32); + if (scroll <= 140 && scroll > 32) + { + newcam_text(160,scroll,newcam_options[i],newcam_option_selection-i); + switch (i) + { + case 0: + newcam_text(160,scroll-12,newcam_flags[newcam_analogue],newcam_option_selection-i); + break; + case 1: + int_to_str(newcam_sensitivityX,newstring); + newcam_text(160,scroll-12,newstring,newcam_option_selection-i); + break; + case 2: + int_to_str(newcam_sensitivityY,newstring); + newcam_text(160,scroll-12,newstring,newcam_option_selection-i); + break; + case 3: + newcam_text(160,scroll-12,newcam_flags[newcam_invertX],newcam_option_selection-i); + break; + case 4: + newcam_text(160,scroll-12,newcam_flags[newcam_invertY],newcam_option_selection-i); + break; + case 5: + int_to_str(newcam_aggression,newstring); + newcam_text(160,scroll-12,newstring,newcam_option_selection-i); + break; + case 6: + int_to_str(newcam_panlevel,newstring); + newcam_text(160,scroll-12,newstring,newcam_option_selection-i); + break; + } + } + } + gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); + print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); + print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]); + gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); +} + +//This has been separated for interesting reasons. Don't question it. +void newcam_render_option_text(void) +{ + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + newcam_text(278,212,newcam_strings[newcam_option_open],1); + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); +} + +void newcam_check_pause_buttons() +{ + if (gPlayer1Controller->buttonPressed & R_TRIG) + { + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + if (newcam_option_open == 0) + newcam_option_open = 1; + else + { + newcam_option_open = 0; + save_file_set_setting(); + } + } + + if (newcam_option_open) + { + if (ABS(gPlayer1Controller->rawStickY) > 60) + { + newcam_option_timer -= 1; + if (newcam_option_timer <= 0) + { + switch (newcam_option_index) + { + case 0: newcam_option_index++; newcam_option_timer += 10; break; + default: newcam_option_timer += 5; break; + } + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + if (gPlayer1Controller->rawStickY >= 60) + { + newcam_option_selection--; + if (newcam_option_selection < 0) + newcam_option_selection = newcam_total-1; + } + else + { + newcam_option_selection++; + if (newcam_option_selection >= newcam_total) + newcam_option_selection = 0; + } + } + } + else + if (ABS(gPlayer1Controller->rawStickX) > 60) + { + newcam_option_timer -= 1; + if (newcam_option_timer <= 0) + { + switch (newcam_option_index) + { + case 0: newcam_option_index++; newcam_option_timer += 10; break; + default: newcam_option_timer += 5; break; + } + #ifndef nosound + play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs); + #endif + if (gPlayer1Controller->rawStickX >= 60) + newcam_change_setting(1); + else + newcam_change_setting(-1); + } + } + else + { + newcam_option_timer = 0; + newcam_option_index = 0; + } + + while (newcam_option_scroll - newcam_option_selection < -3 && newcam_option_selection > newcam_option_scroll) + newcam_option_scroll +=1; + while (newcam_option_scroll + newcam_option_selection > 0 && newcam_option_selection < newcam_option_scroll) + newcam_option_scroll -=1; + } +} diff --git a/enhancements/bettercamera.txt b/enhancements/bettercamera.txt new file mode 100644 index 0000000..9cdad73 --- /dev/null +++ b/enhancements/bettercamera.txt @@ -0,0 +1,7 @@ +Puppycam 2.0 release notes: + +- Reworked Camera mode system, utilising behaviour flags, allowing for new camera modes to be created easier than ever before. bettercamera.h contains all modes, with their appropriate flags. + +- Reworked Collision. Puppycam now utilises CuckyDev's Raycasting system, which offers far more reliable collision checking as well as increased performance. The major change that comes with this however, is that the game must now be compiled with -O2 optimisation. This shouldn't be a problem for most people however, because if you're doing anything remotely creative, you want this enabled anyways. + +- Improved Code inside bettercamera.inc.c. A lot of the code inside this file has been cleaned up, which cuts down on bloat, and improves readability, as well as performance. Most of it's stuff you'd generally leave alone anyway, but it's good to not write YandereDev tier code. \ No newline at end of file diff --git a/include/segments.h b/include/segments.h index ccc989a..9491b6f 100644 --- a/include/segments.h +++ b/include/segments.h @@ -1,6 +1,8 @@ #ifndef _SEGMENTS_H #define _SEGMENTS_H +#define USE_EXT_RAM + /* * Memory addresses for segments. Ideally, this header file would not be * needed, and the addresses would be defined in sm64.ld and linker-inserted @@ -33,10 +35,10 @@ */ #define SEG_BUFFERS 0x8005C000 // 0x0085000 in size -#define SEG_MAIN 0x800E1000 // 0x1328000 in size -#define SEG_ENGINE 0x80213800 // 0x0017000 in size -#define SEG_FRAMEBUFFERS 0x8022A800 // 0x0070800 in size -#define SEG_POOL_START 0x8029B000 // 0x0165000 in size +#define SEG_MAIN 0x800F1000 // 0x1328000 in size +#define SEG_ENGINE 0x80223800 // 0x0017000 in size +#define SEG_FRAMEBUFFERS 0x8023A800 // 0x0070800 in size +#define SEG_POOL_START 0x802AB000 // 0x0165000 in size #define SEG_POOL_END 0x80800000 #define SEG_POOL_END_4MB 0x80400000 // For the error message screen enhancement. #define SEG_GODDARD SEG_POOL_START + 0x113000 diff --git a/include/text_strings.h.in b/include/text_strings.h.in index 2fda11d..15706d3 100644 --- a/include/text_strings.h.in +++ b/include/text_strings.h.in @@ -3,6 +3,20 @@ #include "text_menu_strings.h" +#define NC_CAMX _("Camera X Sensitivity") + #define NC_CAMY _("Camera Y Sensitivity") + #define NC_INVERTX _("Invert X Axis") + #define NC_INVERTY _("Invert Y Axis") + #define NC_CAMC _("Camera Centre Aggression") + #define NC_CAMP _("Camera Pan Level") + #define NC_ENABLED _("Enabled") + #define NC_DISABLED _("Disabled") + #define NC_BUTTON _("[R]: Options") + #define NC_BUTTON2 _("[R]: Return") + #define NC_OPTION _("OPTIONS") + #define NC_HIGHLIGHT _("O") + #define NC_ANALOGUE _("Analogue Camera") + /** * Global Symbols */ diff --git a/src/engine/math_util.c b/src/engine/math_util.c index beb60c5..19e676a 100644 --- a/src/engine/math_util.c +++ b/src/engine/math_util.c @@ -49,6 +49,15 @@ void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b) { return &dest; //! warning: function returns address of local variable } +/// Multiply vector 'dest' by a +void *vec3f_mul(Vec3f dest, f32 a) +{ + dest[0] *= a; + dest[1] *= a; + dest[2] *= a; + return &dest; //! warning: function returns address of local variable +} + /// Copy vector src to dest void *vec3s_copy(Vec3s dest, Vec3s src) { dest[0] = src[0]; @@ -81,11 +90,11 @@ void *vec3s_sum(Vec3s dest, Vec3s a, Vec3s b) { return &dest; //! warning: function returns address of local variable } -/// Subtract vector a from 'dest' -void *vec3s_sub(Vec3s dest, Vec3s a) { - dest[0] -= a[0]; - dest[1] -= a[1]; - dest[2] -= a[2]; +/// Make 'dest' the difference of vectors a and b. +void *vec3f_dif(Vec3f dest, Vec3f a, Vec3f b) { + dest[0] = a[0] - b[0]; + dest[1] = a[1] - b[1]; + dest[2] = a[2] - b[2]; return &dest; //! warning: function returns address of local variable } @@ -140,6 +149,18 @@ void *vec3f_normalize(Vec3f dest) { return &dest; //! warning: function returns address of local variable } +/// Get length of vector 'a' +f32 vec3f_length(Vec3f a) +{ + return sqrtf(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); +} + +/// Get dot product of vectors 'a' and 'b' +f32 vec3f_dot(Vec3f a, Vec3f b) +{ + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + #pragma GCC diagnostic pop /// Copy matrix 'src' to 'dest' diff --git a/src/engine/math_util.h b/src/engine/math_util.h index b36498c..650fe97 100644 --- a/src/engine/math_util.h +++ b/src/engine/math_util.h @@ -29,10 +29,14 @@ extern f32 gCosineTable[]; #define sqr(x) ((x) * (x)) +#define abs(x) ((x) < 0 ? -(x) : (x)) + void *vec3f_copy(Vec3f dest, Vec3f src); void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z); void *vec3f_add(Vec3f dest, Vec3f a); void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b); +void *vec3f_dif(Vec3f dest, Vec3f a, Vec3f b); +void *vec3f_mul(Vec3f dest, f32 a); void *vec3s_copy(Vec3s dest, Vec3s src); void *vec3s_set(Vec3s dest, s16 x, s16 y, s16 z); void *vec3s_add(Vec3s dest, Vec3s a); @@ -43,6 +47,8 @@ void *vec3f_to_vec3s(Vec3s dest, Vec3f a); void *find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c); void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b); void *vec3f_normalize(Vec3f dest); +f32 vec3f_length(Vec3f a); +f32 vec3f_dot(Vec3f a, Vec3f b); void mtxf_copy(f32 dest[4][4], f32 src[4][4]); void mtxf_identity(f32 mtx[4][4]); void mtxf_translate(f32 a[4][4], Vec3f b); diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index f746373..cd52d44 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -10,6 +10,7 @@ #include "surface_load.h" #include "game/object_list_processor.h" #include "game/room.h" +#include "math_util.h" /************************************************** * WALLS * @@ -786,3 +787,167 @@ static s32 unused_resolve_floor_or_ceil_collisions(s32 checkCeil, f32 *px, f32 * return 0; } + +/** + * Raycast functions + */ +s32 ray_surface_intersect(Vec3f orig, Vec3f dir, f32 dir_length, struct Surface *surface, Vec3f hit_pos, f32 *length) +{ + Vec3f v0, v1, v2, e1, e2, h, s, q; + f32 a, f, u, v; + Vec3f add_dir; + + // Get surface normal and some other stuff + vec3s_to_vec3f(v0, surface->vertex1); + vec3s_to_vec3f(v1, surface->vertex2); + vec3s_to_vec3f(v2, surface->vertex3); + + vec3f_dif(e1, v1, v0); + vec3f_dif(e2, v2, v0); + + vec3f_cross(h, dir, e2); + + // Check if we're perpendicular from the surface + a = vec3f_dot(e1, h); + if (a > -0.00001f && a < 0.00001f) + return FALSE; + + // Check if we're making contact with the surface + f = 1.0f / a; + + vec3f_dif(s, orig, v0); + u = f * vec3f_dot(s, h); + if (u < 0.0f || u > 1.0f) + return FALSE; + + vec3f_cross(q, s, e1); + v = f * vec3f_dot(dir, q); + if (v < 0.0f || u + v > 1.0f) + return FALSE; + + // Get the length between our origin and the surface contact point + *length = f * vec3f_dot(e2, q); + if (*length <= 0.00001 || *length > dir_length) + return FALSE; + + // Successful contact + vec3f_copy(add_dir, dir); + vec3f_mul(add_dir, *length); + vec3f_sum(hit_pos, orig, add_dir); + return TRUE; +} + +void find_surface_on_ray_list(struct SurfaceNode *list, Vec3f orig, Vec3f dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length) +{ + s32 hit; + f32 length; + Vec3f chk_hit_pos; + f32 top, bottom; + + // Get upper and lower bounds of ray + if (dir[1] >= 0.0f) + { + top = orig[1] + dir[1] * dir_length; + bottom = orig[1]; + } + else + { + top = orig[1]; + bottom = orig[1] + dir[1] * dir_length; + } + + // Iterate through every surface of the list + for (; list != NULL; list = list->next) + { + // Reject surface if out of vertical bounds + if (list->surface->lowerY > top || list->surface->upperY < bottom) + continue; + + // Check intersection between the ray and this surface + if ((hit = ray_surface_intersect(orig, dir, dir_length, list->surface, chk_hit_pos, &length)) != 0) + { + if (length <= *max_length) + { + *hit_surface = list->surface; + vec3f_copy(hit_pos, chk_hit_pos); + *max_length = length; + } + } + } +} + + +void find_surface_on_ray_cell(s16 cellX, s16 cellZ, Vec3f orig, Vec3f normalized_dir, f32 dir_length, struct Surface **hit_surface, Vec3f hit_pos, f32 *max_length) +{ + // Skip if OOB + if (cellX >= 0 && cellX <= 0xF && cellZ >= 0 && cellZ <= 0xF) + { + // Iterate through each surface in this partition + if (normalized_dir[1] > -0.99f) + { + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_CEILS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } + if (normalized_dir[1] < 0.99f) + { + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_FLOORS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } + find_surface_on_ray_list(gStaticSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + find_surface_on_ray_list(gDynamicSurfacePartition[cellZ][cellX][SPATIAL_PARTITION_WALLS].next, orig, normalized_dir, dir_length, hit_surface, hit_pos, max_length); + } +} + +void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos) +{ + f32 max_length; + s16 cellZ, cellX; + f32 fCellZ, fCellX; + f32 dir_length; + Vec3f normalized_dir; + f32 step, dx, dz; + u32 i; + + // Set that no surface has been hit + *hit_surface = NULL; + vec3f_sum(hit_pos, orig, dir); + + // Get normalized direction + dir_length = vec3f_length(dir); + max_length = dir_length; + vec3f_copy(normalized_dir, dir); + vec3f_normalize(normalized_dir); + + // Get our cell coordinate + fCellX = (orig[0] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; + fCellZ = (orig[2] + LEVEL_BOUNDARY_MAX) / CELL_SIZE; + cellX = (s16)fCellX; + cellZ = (s16)fCellZ; + + // Don't do DDA if straight down + if (normalized_dir[1] >= 1.0f || normalized_dir[1] <= -1.0f) + { + find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length); + return; + } + + // Get cells we cross using DDA + if (abs(dir[0]) >= abs(dir[2])) + step = abs(dir[0]) / CELL_SIZE; + else + step = abs(dir[2]) / CELL_SIZE; + + dx = dir[0] / step / CELL_SIZE; + dz = dir[2] / step / CELL_SIZE; + + for (i = 0; i < step && *hit_surface == NULL; i++) + { + find_surface_on_ray_cell(cellX, cellZ, orig, normalized_dir, dir_length, hit_surface, hit_pos, &max_length); + + // Move cell coordinate + fCellX += dx; + fCellZ += dz; + cellX = (s16)fCellX; + cellZ = (s16)fCellZ; + } +} diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h index 82ba862..6e79dca 100644 --- a/src/engine/surface_collision.h +++ b/src/engine/surface_collision.h @@ -32,6 +32,6 @@ f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor); f32 find_water_level(f32 x, f32 z); f32 find_poison_gas_level(f32 x, f32 z); void debug_surface_list_info(f32 xPos, f32 zPos); - +void find_surface_on_ray(Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos); #endif /* _SURFACE_COLLISION_H */ diff --git a/src/game/camera.c b/src/game/camera.c index af8dc34..31e2390 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -638,6 +638,8 @@ void calc_y_to_curr_floor(f32 *posOff, f32 posMul, f32 posBound, f32 *focOff, f3 *focOff = -focBound; } } +//Compiler gets mad if I put this any further above. thanks refresh 7 +#include "../../enhancements/bettercamera.inc.c" void focus_on_mario(Vec3f focus, Vec3f pos, f32 posYOff, f32 focYOff, f32 dist, s16 pitch, s16 yaw) { Vec3f marioPos; @@ -2791,6 +2793,8 @@ void set_camera_mode(struct Camera *c, s16 mode, s16 frames) { struct LinearTransitionPoint *start = &sModeInfo.transitionStart; struct LinearTransitionPoint *end = &sModeInfo.transitionEnd; + if (mode != CAM_MODE_NEWCAM && gLakituState.mode != CAM_MODE_NEWCAM) + { if (mode == CAMERA_MODE_WATER_SURFACE && gCurrLevelArea == AREA_TTM_OUTSIDE) { } else { // Clear movement flags that would affect the transition @@ -2834,6 +2838,7 @@ void set_camera_mode(struct Camera *c, s16 mode, s16 frames) { vec3f_get_dist_and_angle(start->focus, start->pos, &start->dist, &start->pitch, &start->yaw); vec3f_get_dist_and_angle(end->focus, end->pos, &end->dist, &end->pitch, &end->yaw); } + } } /** @@ -2918,7 +2923,7 @@ void update_lakitu(struct Camera *c) { gLakituState.roll += sHandheldShakeRoll; gLakituState.roll += gLakituState.keyDanceRoll; - if (c->mode != CAMERA_MODE_C_UP && c->cutscene == 0) { + if (c->mode != CAMERA_MODE_C_UP && c->cutscene == 0 && c->mode != CAM_MODE_NEWCAM) { gCheckingSurfaceCollisionsForCamera = TRUE; distToFloor = find_floor(gLakituState.pos[0], gLakituState.pos[1] + 20.0f, @@ -2951,7 +2956,7 @@ void update_camera(struct Camera *c) { update_camera_hud_status(c); if (c->cutscene == 0) { // Only process R_TRIG if 'fixed' is not selected in the menu - if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO) { + if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO && c->mode != CAM_MODE_NEWCAM) { if (gPlayer1Controller->buttonPressed & R_TRIG) { if (set_cam_angle(0) == CAM_ANGLE_LAKITU) { set_cam_angle(CAM_ANGLE_MARIO); @@ -2989,10 +2994,12 @@ void update_camera(struct Camera *c) { c->mode = gLakituState.mode; c->defMode = gLakituState.defMode; - camera_course_processing(c); - dummy_802877EC(c); - sCButtonsPressed = find_c_buttons_pressed(sCButtonsPressed, gPlayer1Controller->buttonPressed, - gPlayer1Controller->buttonDown); + if (c->mode != CAM_MODE_NEWCAM) { + camera_course_processing(c); + dummy_802877EC(c); + sCButtonsPressed = find_c_buttons_pressed(sCButtonsPressed, gPlayer1Controller->buttonPressed, + gPlayer1Controller->buttonDown); + } if (c->cutscene != 0) { sYawSpeed = 0; @@ -3030,6 +3037,10 @@ void update_camera(struct Camera *c) { mode_cannon_camera(c); break; + case CAM_MODE_NEWCAM: + newcam_loop(c); + break; + default: mode_mario_camera(c); } @@ -3089,6 +3100,10 @@ void update_camera(struct Camera *c) { case CAMERA_MODE_SPIRAL_STAIRS: mode_spiral_stairs_camera(c); break; + + case CAM_MODE_NEWCAM: + newcam_loop(c); + break; } } } @@ -3364,6 +3379,13 @@ void init_camera(struct Camera *c) { gLakituState.nextYaw = gLakituState.yaw; c->yaw = gLakituState.yaw; c->nextYaw = gLakituState.yaw; + + if (newcam_active == 1) + { + gLakituState.mode = CAM_MODE_NEWCAM; + gLakituState.defMode = CAM_MODE_NEWCAM; + newcam_init(c, 0); + } } /** @@ -5452,6 +5474,8 @@ void set_camera_mode_8_directions(struct Camera *c) { s8DirModeBaseYaw = 0; s8DirModeYawOffset = 0; } + if (newcam_active == 1) + c->mode = CAM_MODE_NEWCAM; } /** @@ -5470,6 +5494,8 @@ void set_camera_mode_close_cam(u8 *mode) { sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; *mode = CAMERA_MODE_CLOSE; } + if (newcam_active == 1) + *mode = CAM_MODE_NEWCAM; } /** @@ -5494,6 +5520,8 @@ void set_camera_mode_radial(struct Camera *c, s16 transitionTime) { } sModeOffsetYaw = 0; } + if (newcam_active == 1) + c->mode = CAM_MODE_NEWCAM; } /** @@ -6858,6 +6886,7 @@ s16 cutscene_object(u8 cutscene, struct Object *o) { void update_camera_yaw(struct Camera *c) { c->nextYaw = calculate_yaw(c->focus, c->pos); c->yaw = c->nextYaw; + newcam_apply_outside_values(c,0); } void cutscene_reset_spline(void) { @@ -9121,7 +9150,12 @@ CmdRet cutscene_non_painting_end(struct Camera *c) { if (c->defMode == CAMERA_MODE_CLOSE) { c->mode = CAMERA_MODE_CLOSE; - } else { + } else + if (c->defMode == CAM_MODE_NEWCAM) { + c->mode = CAM_MODE_NEWCAM; + } + else + { c->mode = CAMERA_MODE_FREE_ROAM; } @@ -9795,6 +9829,7 @@ CmdRet cutscene_sliding_doors_follow_mario(struct Camera *c) { Vec3f pos; UNUSED u32 pad[4]; + newcam_apply_outside_values(c, 1); vec3f_copy(pos, c->pos); // Update cvar1 with mario's position (the y value doesn't change) sCutsceneVars[1].point[0] = sMarioCamState->pos[0]; @@ -10018,6 +10053,11 @@ CmdRet cutscene_unused_exit_focus_mario(struct Camera *c) { */ CmdRet cutscene_exit_painting_end(struct Camera *c) { c->mode = CAMERA_MODE_CLOSE; + if (newcam_active == 1) { + c->mode = CAM_MODE_NEWCAM; + } else { + c->mode = CAMERA_MODE_CLOSE; + } c->cutscene = 0; gCutsceneTimer = CUTSCENE_STOP; sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT; @@ -10177,10 +10217,12 @@ CmdRet cutscene_door_follow_mario(struct Camera *c) { * Ends the door cutscene. Sets the camera mode to close mode unless the default is free roam. */ CmdRet cutscene_door_end(struct Camera *c) { - if (c->defMode == CAMERA_MODE_FREE_ROAM) { - c->mode = CAMERA_MODE_FREE_ROAM; + if (c->defMode == CAMERA_MODE_CLOSE) { + c->mode = CAMERA_MODE_CLOSE; + } else if (c->defMode == CAM_MODE_NEWCAM) { + c->mode = CAM_MODE_NEWCAM; } else { - c->mode = CAMERA_MODE_CLOSE; + c->mode = CAMERA_MODE_FREE_ROAM; } c->cutscene = 0; diff --git a/src/game/camera.h b/src/game/camera.h index ee48f91..393fa10 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -110,6 +110,7 @@ #define CAMERA_MODE_8_DIRECTIONS 0x0E // AKA Parallel Camera, Bowser Courses & Rainbow Road #define CAMERA_MODE_FREE_ROAM 0x10 #define CAMERA_MODE_SPIRAL_STAIRS 0x11 +#define CAM_MODE_NEWCAM 0x12 #define CAM_MOVE_RETURN_TO_MIDDLE 0x0001 #define CAM_MOVE_ZOOMED_OUT 0x0002 diff --git a/src/game/game.c b/src/game/game.c index cc512f0..42c6a7f 100644 --- a/src/game/game.c +++ b/src/game/game.c @@ -17,6 +17,7 @@ #include "main_entry.h" #include #include "game.h" +#include "../../enhancements/bettercamera.h" // FIXME: I'm not sure all of these variables belong in this file, but I don't // know of a good way to split them diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index 14499ab..2abe687 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -18,6 +18,7 @@ #include "ingame_menu.h" #include "print.h" #include "engine/math_util.h" +#include "../../enhancements/bettercamera.h" extern Gfx *gDisplayListHead; extern s32 gGlobalTimer; @@ -2627,7 +2628,8 @@ s16 render_pause_courses_and_castle(void) { #ifdef VERSION_EU gInGameLanguage = eu_get_language(); #endif - + if (newcam_option_open == 0) + { switch (gDialogBoxState) { case DIALOG_STATE_OPENING: gDialogLineNum = 1; @@ -2703,6 +2705,14 @@ s16 render_pause_courses_and_castle(void) { if (gDialogTextAlpha < 250) { gDialogTextAlpha += 25; } + } + else + { + shade_screen(); + newcam_display_options(); + } + newcam_check_pause_buttons(); + newcam_render_option_text(); return 0; } diff --git a/src/game/mario.c b/src/game/mario.c index a64a46c..89f5562 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -31,6 +31,7 @@ #include "sound_init.h" #include "engine/surface_collision.h" #include "level_table.h" +#include "../../enhancements/bettercamera.h" u32 unused80339F10; s8 filler80339F1C[20]; @@ -1305,7 +1306,10 @@ void update_mario_joystick_inputs(struct MarioState *m) { } if (m->intendedMag > 0.0f) { - m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + m->area->camera->yaw; + if (gLakituState.mode != CAM_MODE_NEWCAM) + m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + m->area->camera->yaw; + else + m->intendedYaw = atan2s(-controller->stickY, controller->stickX)-newcam_yaw+0x4000; m->input |= INPUT_NONZERO_ANALOG; } else { m->intendedYaw = m->faceAngle[1]; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index c151ce6..47db03b 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -12,6 +12,7 @@ #include "save_file.h" #include "audio/external.h" #include "engine/graph_node.h" +#include "../../enhancements/bettercamera.h" void play_flip_sounds(struct MarioState *m, s16 frame1, s16 frame2, s16 frame3) { s32 animFrame = m->marioObj->header.gfx.unk38.animFrame; @@ -1619,7 +1620,12 @@ s32 act_shot_from_cannon(struct MarioState *m) { case AIR_STEP_LANDED: set_mario_action(m, ACT_DIVE_SLIDE, 0); m->faceAngle[0] = 0; - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } break; case AIR_STEP_HIT_WALL: @@ -1632,7 +1638,13 @@ s32 act_shot_from_cannon(struct MarioState *m) { m->particleFlags |= PARTICLE_1; set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } + break; case AIR_STEP_HIT_LAVA_WALL: @@ -1659,20 +1671,37 @@ s32 act_flying(struct MarioState *m) { if (m->input & INPUT_Z_PRESSED) { if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } } return set_mario_action(m, ACT_GROUND_POUND, 1); } if (!(m->flags & MARIO_WING_CAP)) { if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } } return set_mario_action(m, ACT_FREEFALL, 0); } if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { - set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } + } } if (m->actionState == 0) { @@ -1712,7 +1741,12 @@ s32 act_flying(struct MarioState *m) { set_anim_to_frame(m, 7); m->faceAngle[0] = 0; - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } break; case AIR_STEP_HIT_WALL: @@ -1730,7 +1764,12 @@ s32 act_flying(struct MarioState *m) { m->particleFlags |= PARTICLE_1; set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } } else { if (m->actionTimer++ == 0) { play_sound(SOUND_ACTION_HIT, m->marioObj->header.gfx.cameraToObject); @@ -1805,7 +1844,12 @@ s32 act_flying_triple_jump(struct MarioState *m) { #ifndef VERSION_JP if (m->input & (INPUT_B_PRESSED | INPUT_Z_PRESSED)) { if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { - set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } } if (m->input & INPUT_B_PRESSED) { return set_mario_action(m, ACT_DIVE, 0); @@ -1843,7 +1887,12 @@ s32 act_flying_triple_jump(struct MarioState *m) { if (m->vel[1] < 4.0f) { if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { - set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + if (newcam_active == 0) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + m->area->camera->mode = CAM_MODE_NEWCAM; + gLakituState.mode = CAM_MODE_NEWCAM; + } } if (m->forwardVel < 32.0f) { @@ -1853,10 +1902,6 @@ s32 act_flying_triple_jump(struct MarioState *m) { set_mario_action(m, ACT_FLYING, 1); } - if (m->actionTimer++ == 10 && m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { - set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); - } - update_air_without_turn(m); switch (perform_air_step(m, 0)) { diff --git a/src/game/save_file.c b/src/game/save_file.c index 15ae6a4..cae3c1b 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -9,11 +9,13 @@ #include "save_file.h" #include "sound_init.h" #include "level_table.h" +#include "../../enhancements/bettercamera.h" #define MENU_DATA_MAGIC 0x4849 #define SAVE_FILE_MAGIC 0x4441 -STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match"); +STATIC_ASSERT(sizeof(struct SaveBuffer) <= EEPROM_SIZE, "eeprom buffer size higher than intended"); +STATIC_ASSERT(sizeof(struct SaveBuffer) >= EEPROM_SIZE, "eeprom buffer size lower than intended"); extern struct SaveBuffer gSaveBuffer; @@ -550,6 +552,48 @@ u16 save_file_get_sound_mode(void) { return gSaveBuffer.menuData[0].soundMode; } +void save_file_set_setting(void) { + + gSaveBuffer.menuData[0].camx = newcam_sensitivityX; + gSaveBuffer.menuData[0].camy = newcam_sensitivityY; + gSaveBuffer.menuData[0].invertx = newcam_invertX; + gSaveBuffer.menuData[0].inverty = newcam_invertY; + gSaveBuffer.menuData[0].camc = newcam_aggression; + gSaveBuffer.menuData[0].camp = newcam_panlevel; + gSaveBuffer.menuData[0].analogue = newcam_analogue; + + gSaveBuffer.menuData[0].firsttime = 1; + + + gMainMenuDataModified = TRUE; + save_main_menu_data(); +} + +void save_file_get_setting(void) { + newcam_sensitivityX = gSaveBuffer.menuData[0].camx; + newcam_sensitivityY = gSaveBuffer.menuData[0].camy; + newcam_invertX = gSaveBuffer.menuData[0].invertx; + newcam_invertY = gSaveBuffer.menuData[0].inverty; + newcam_aggression = gSaveBuffer.menuData[0].camc; + newcam_panlevel = gSaveBuffer.menuData[0].camp; + newcam_analogue = gSaveBuffer.menuData[0].analogue; + +} + +u8 save_check_firsttime(void) +{ + return gSaveBuffer.menuData[0].firsttime; +} + + +void save_set_firsttime(void) +{ + gSaveBuffer.menuData[0].firsttime = 1; + + gMainMenuDataModified = TRUE; + save_main_menu_data(); +} + void save_file_move_cap_to_default_location(void) { if (save_file_get_flags() & SAVE_FLAG_CAP_ON_GROUND) { switch (gSaveBuffer.files[gCurrSaveFileNum - 1][0].capLevel) { diff --git a/src/game/save_file.h b/src/game/save_file.h index 18eafc2..3930c1f 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -6,7 +6,7 @@ #include "course_table.h" -#define EEPROM_SIZE 0x200 +#define EEPROM_SIZE 0x800 #define NUM_SAVE_FILES 4 struct SaveBlockSignature @@ -50,16 +50,24 @@ struct MainMenuSaveData // on the high score screen. u32 coinScoreAges[NUM_SAVE_FILES]; u16 soundMode; - + u8 camx; + u8 camy; + u8 analogue; + u8 invertx; + u8 inverty; + u8 camc; + u8 camp; + u8 firsttime; #ifdef VERSION_EU u16 language; #define SUBTRAHEND 8 #else -#define SUBTRAHEND 6 +#define SUBTRAHEND 15 #endif + // Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU) - u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; + //u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; struct SaveBlockSignature signature; }; @@ -70,6 +78,7 @@ struct SaveBuffer struct SaveFile files[NUM_SAVE_FILES][2]; // The main menu data has two copies. If one is bad, the other is used as a backup. struct MainMenuSaveData menuData[2]; + u8 filler[1535]; //!I still haven't done an algorithm for this yet lol }; struct WarpNode; @@ -144,6 +153,10 @@ s32 save_file_get_cap_pos(Vec3s capPos); void save_file_set_sound_mode(u16 mode); u16 save_file_get_sound_mode(void); void save_file_move_cap_to_default_location(void); +void save_set_firsttime(void); +u8 save_check_firsttime(void); +void save_file_get_setting(void); +void save_file_set_setting(void); void disable_warp_checkpoint(void); void check_if_should_set_warp_checkpoint(struct WarpNode *a); diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 07a5169..c2034d3 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -141,6 +141,7 @@ void main_func(void) { audio_init(); sound_init(); + newcam_init_settings(); thread5_game_loop(NULL); #ifdef TARGET_WEB From 298942850e5d903d7524cdc8aaee0561541c8395 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 10:12:06 -0400 Subject: [PATCH 24/28] shell.nix: cargo-watch --- shell.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/shell.nix b/shell.nix index 5334d8d..0980dad 100644 --- a/shell.nix +++ b/shell.nix @@ -27,6 +27,7 @@ in pkgs.mkShell { # gamebridge rustc cargo + cargo-watch rls rustfmt ]; From d05b4675786ad1f43d146e9804677d0225479329 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 10:12:13 -0400 Subject: [PATCH 25/28] lerp it good --- gamebridge/src/au.rs | 20 +++++++-------- gamebridge/src/main.rs | 55 ++++++++++++++++++++++++++++++---------- gamebridge/src/twitch.rs | 21 +++++++-------- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/gamebridge/src/au.rs b/gamebridge/src/au.rs index 4a71221..b47e19d 100644 --- a/gamebridge/src/au.rs +++ b/gamebridge/src/au.rs @@ -32,18 +32,18 @@ impl Lerper { let scalar = self.scalar; self.scalar = match scalar { _ if scalar == self.goal => self.goal, - _ if scalar == self.max => { - self.extended_tick = now; - scalar -1 - }, - _ if scalar == self.min => { + _ if scalar >= self.max => { self.extended_tick = now; scalar - 1 - }, + } + _ if scalar <= self.min => { + self.extended_tick = now; + scalar + 1 + } _ => { let t = (now - self.extended_tick) as f64 / self.lerp_time; lerp(self.scalar, 0, t) - }, + } }; if self.scalar >= self.max { @@ -54,7 +54,6 @@ impl Lerper { return self.min; } - log::info!("before: {}, after: {}", scalar, self.scalar); self.scalar } @@ -87,13 +86,14 @@ mod test { use super::Lerper; let mut lerper = Lerper::init(15.0, 127, -128, 0); - for case in [(127, 3, 126), (100, 8, 66)].iter() { + for case in [(127, 3, 126), (100, 8, 66), (-124, 8, -82)].iter() { let scalar = case.0; let now = case.1; let want = case.2; lerper.update(scalar); - assert_eq!(lerper.apply(now), want); + let result = lerper.apply(now); + assert_eq!(result, want); } } } diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 38c1e25..53acc5b 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -42,7 +42,7 @@ fn main() -> Result<()> { let mut vblank = File::open("vblank")?; let mut input = OpenOptions::new().write(true).open("input")?; - const STICK_LERP_TIME: f64 = 330.0; // 330 frames to lerp stick positions down to 0 + const STICK_LERP_TIME: f64 = 270.0; // 270 frames to lerp stick positions down to 0 const BUTTON_LERP_TIME: f64 = 20.0; // 20 frames to lerp button inputs down to 0 let st = { @@ -53,7 +53,7 @@ fn main() -> Result<()> { sticky: Lerper::init(STICK_LERP_TIME, 127, -128, 0), a_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), b_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), - z_button: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special + z_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), r_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), start: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special c_left: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0), @@ -93,8 +93,12 @@ fn main() -> Result<()> { //data.stickx.update(data.controller[2] as i64); //data.sticky.update(data.controller[3] as i64); + debug!("x before: {}", data.stickx.scalar); let mut stickx_scalar = data.stickx.apply(frame) as i8; + debug!("x after: {}", data.stickx.scalar); + debug!("y before: {}", data.sticky.scalar); let mut sticky_scalar = data.sticky.apply(frame) as i8; + debug!("y after: {}", data.sticky.scalar); let dist = stick_distance(stickx_scalar, sticky_scalar); if dist <= 10 { @@ -106,30 +110,53 @@ fn main() -> Result<()> { let mut hi = HiButtons::NONE; let mut lo = LoButtons::NONE; - const BUTTON_PUSH_THRESHOLD: i64 = 16; + const BUTTON_PUSH_THRESHOLD: i64 = 2; // high buttons data.a_button.apply(frame); - if data.a_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::A_BUTTON; } + if data.a_button.pressed(BUTTON_PUSH_THRESHOLD) { + hi = hi | HiButtons::A_BUTTON; + } data.b_button.apply(frame); - if data.b_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::B_BUTTON; } + if data.b_button.pressed(BUTTON_PUSH_THRESHOLD) { + hi = hi | HiButtons::B_BUTTON; + } data.z_button.apply(frame); - if data.z_button.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::Z_BUTTON; } + if data.z_button.pressed(BUTTON_PUSH_THRESHOLD) { + hi = hi | HiButtons::Z_BUTTON; + } data.start.apply(frame); - if data.start.pressed(BUTTON_PUSH_THRESHOLD) { hi = hi | HiButtons::START; } - info!("start: {}", data.start.scalar); - + if data.start.pressed(BUTTON_PUSH_THRESHOLD) { + hi = hi | HiButtons::START; + } data.r_button.apply(frame); - if data.r_button.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::R_BUTTON; } + if data.r_button.pressed(BUTTON_PUSH_THRESHOLD) { + lo = lo | LoButtons::R_BUTTON; + } data.c_up.apply(frame); - if data.c_up.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_UP; } + if data.c_up.pressed(BUTTON_PUSH_THRESHOLD) { + lo = lo | LoButtons::C_UP; + } data.c_down.apply(frame); - if data.c_down.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_DOWN; } + if data.c_down.pressed(BUTTON_PUSH_THRESHOLD) { + lo = lo | LoButtons::C_DOWN; + } data.c_left.apply(frame); - if data.c_left.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_LEFT; } + if data.c_left.pressed(BUTTON_PUSH_THRESHOLD) { + lo = lo | LoButtons::C_LEFT; + } data.c_right.apply(frame); - if data.c_right.pressed(BUTTON_PUSH_THRESHOLD) { lo = lo | LoButtons::C_RIGHT; } + if data.c_right.pressed(BUTTON_PUSH_THRESHOLD) { + lo = lo | LoButtons::C_RIGHT; + } + println!( + "[ rust] {:02x}{:02x} {:02x}{:02x}", + hi.bits(), + lo.bits(), + stickx_scalar as u8, + sticky_scalar as u8 + ); controller[0] = hi.bits() as u8; controller[1] = lo.bits() as u8; controller[2] = stickx_scalar as u8; diff --git a/gamebridge/src/twitch.rs b/gamebridge/src/twitch.rs index 068173b..620073e 100644 --- a/gamebridge/src/twitch.rs +++ b/gamebridge/src/twitch.rs @@ -80,18 +80,19 @@ async fn run_loop( let chatline = msg.data.to_string(); let chatline = chatline.to_ascii_lowercase(); let mut data = st.write().unwrap(); + const BUTTON_ADD_AMT: i64 = 64; match chatline.as_str() { - "a" => data.a_button.add(1024), - "b" => data.b_button.add(1024), - "z" => data.z_button.add(1024), - "r" => data.r_button.add(1024), - "cup" => data.c_up.add(1024), - "cdown" => data.c_down.add(1024), - "cleft" => data.c_left.add(1024), - "cright" => data.c_right.add(1024), - "start" => data.start.add(1024), - "up" => data.stickx.add(127), + "a" => data.a_button.add(BUTTON_ADD_AMT), + "b" => data.b_button.add(BUTTON_ADD_AMT), + "z" => data.z_button.add(BUTTON_ADD_AMT), + "r" => data.r_button.add(BUTTON_ADD_AMT), + "cup" => data.c_up.add(BUTTON_ADD_AMT), + "cdown" => data.c_down.add(BUTTON_ADD_AMT), + "cleft" => data.c_left.add(BUTTON_ADD_AMT), + "cright" => data.c_right.add(BUTTON_ADD_AMT), + "start" => data.start.add(BUTTON_ADD_AMT), + "up" => data.sticky.add(127), "down" => data.sticky.add(-128), "left" => data.stickx.add(-128), "right" => data.stickx.add(127), From 80cfdd2a116c1fbece6baeb20e1df8f1fbf9d1f9 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 10:53:02 -0400 Subject: [PATCH 26/28] patch out dying --- src/game/mario.c | 3 ++- src/game/mario_actions_cutscene.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/game/mario.c b/src/game/mario.c index 89f5562..d9bdb16 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1457,6 +1457,7 @@ void set_submerged_cam_preset_and_spawn_bubbles(struct MarioState *m) { * Both increments and decrements Mario's HP. */ void update_mario_health(struct MarioState *m) { + return; s32 terrainIsSnow; if (m->health >= 0x100) { @@ -1865,7 +1866,7 @@ void init_mario_from_save_file(void) { save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1); gMarioState->numKeys = 0; - gMarioState->numLives = 4; + gMarioState->numLives = 99; gMarioState->health = 0x880; gMarioState->unkB8 = gMarioState->numStars; diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 6f12575..8aa7b3e 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -1183,7 +1183,7 @@ s32 act_death_exit(struct MarioState *m) { #else play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); #endif - m->numLives--; + //m->numLives--; // restore 7.75 units of health m->healCounter = 31; } @@ -1199,7 +1199,7 @@ s32 act_unused_death_exit(struct MarioState *m) { #else play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); #endif - m->numLives--; + //m->numLives--; // restore 7.75 units of health m->healCounter = 31; } @@ -1215,7 +1215,7 @@ s32 act_falling_death_exit(struct MarioState *m) { #else play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); #endif - m->numLives--; + //m->numLives--; // restore 7.75 units of health m->healCounter = 31; } @@ -1259,7 +1259,7 @@ s32 act_special_death_exit(struct MarioState *m) { } if (launch_mario_until_land(m, ACT_HARD_BACKWARD_GROUND_KB, MARIO_ANIM_BACKWARD_AIR_KB, -24.0f)) { - m->numLives--; + //m->numLives--; m->healCounter = 31; } // show mario From 902c98451b3d73dcade6cf61c9110ecf535ddc0c Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 11:00:46 -0400 Subject: [PATCH 27/28] gamebridge: multiple commands per line --- gamebridge/src/main.rs | 2 +- gamebridge/src/twitch.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gamebridge/src/main.rs b/gamebridge/src/main.rs index 53acc5b..a4b973e 100644 --- a/gamebridge/src/main.rs +++ b/gamebridge/src/main.rs @@ -150,7 +150,7 @@ fn main() -> Result<()> { lo = lo | LoButtons::C_RIGHT; } - println!( + debug!( "[ rust] {:02x}{:02x} {:02x}{:02x}", hi.bits(), lo.bits(), diff --git a/gamebridge/src/twitch.rs b/gamebridge/src/twitch.rs index 620073e..e2a327e 100644 --- a/gamebridge/src/twitch.rs +++ b/gamebridge/src/twitch.rs @@ -82,7 +82,8 @@ async fn run_loop( let mut data = st.write().unwrap(); const BUTTON_ADD_AMT: i64 = 64; - match chatline.as_str() { + for cmd in chatline.to_string().split(" ").collect::>().iter() { + match *cmd { "a" => data.a_button.add(BUTTON_ADD_AMT), "b" => data.b_button.add(BUTTON_ADD_AMT), "z" => data.z_button.add(BUTTON_ADD_AMT), @@ -99,6 +100,7 @@ async fn run_loop( "stop" => {data.stickx.update(0); data.sticky.update(0);}, _ => {}, } + } eprintln!("[{}] {}: {}", msg.channel, msg.name, msg.data); } From 3e3be0dc1e293e10a1dbef5b32d83a68a44f39db Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Sat, 9 May 2020 11:20:58 -0400 Subject: [PATCH 28/28] start on longjump macro --- longjump.inputs | 46 +++++++++++++++++++++ src/pc/controller/controller_tas_recorder.c | 4 ++ 2 files changed, 50 insertions(+) create mode 100644 longjump.inputs diff --git a/longjump.inputs b/longjump.inputs new file mode 100644 index 0000000..1e3b254 --- /dev/null +++ b/longjump.inputs @@ -0,0 +1,46 @@ +0000f97f +0000f97f +0000f97f +0000f97f +0000f97f +2000fa7f +2000fa7f +2000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000fa7f +a000ff7f +a000177f +a000347f +a0005479 +a0005a74 +a0005a73 +a0006269 +a0006664 +a0006b5f +a0006b5e +20006b5e +20006b5e +20006b5e +2000576b +20003e6f +20002765 +20002042 +20001e24 +20000000 +20000000 +20000000 +20000000 +20000000 +20000000 diff --git a/src/pc/controller/controller_tas_recorder.c b/src/pc/controller/controller_tas_recorder.c index 6bd1c99..53464fe 100644 --- a/src/pc/controller/controller_tas_recorder.c +++ b/src/pc/controller/controller_tas_recorder.c @@ -30,12 +30,14 @@ static void tas_recorder_init(void) { memset(buf, 0, sizeof(buf)); fwrite(buf, 1, sizeof(buf), fout); atexit(tas_recorder_close); + counter = 0; } static void tas_recorder_read(OSContPad *pad) { if (fout == NULL) { return; } + counter += 4; uint8_t bytes[4] = {0}; int button1 = pad->button; @@ -45,6 +47,8 @@ static void tas_recorder_read(OSContPad *pad) { bytes[2] = pad->stick_x; bytes[3] = pad->stick_y; fwrite(bytes, 1, 4, fout); + + printf("[tas_recorder] %08x: %04x %02x%02x\n", (counter + OFFSET), pad->button, bytes[2], bytes[3]); } struct ControllerAPI controller_tas_recorder = {