diff --git a/Makefile b/Makefile index 0587363..60415fe 100644 --- a/Makefile +++ b/Makefile @@ -462,7 +462,7 @@ ifeq ($(ENABLE_OPENGL),1) GFX_LDFLAGS := ifeq ($(TARGET_WINDOWS),1) GFX_CFLAGS += $(shell sdl2-config --cflags) -DGLEW_STATIC - GFX_LDFLAGS += $(shell sdl2-config --libs) -lglew32 -lopengl32 -lwinmm -limm32 -lversion -loleaut32 -lsetupapi + GFX_LDFLAGS += $(shell sdl2-config --libs) -Llib -lpthread -lglew32 -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -static endif ifeq ($(TARGET_LINUX),1) GFX_CFLAGS += $(shell sdl2-config --cflags) diff --git a/README.md b/README.md index eda8a20..6f6783e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ -# Super Mario 64 Port +# Super Mario 64 Port Thingy -- This repo contains a full decompilation of Super Mario 64 (J), (U), and (E) with minor exceptions in the audio subsystem. -- Naming and documentation of the source code and data structures are in progress. -- Efforts to decompile the Shindou ROM steadily advance toward a matching build. -- Beyond Nintendo 64, it can also target Linux and Windows natively. +Stuff from my earlier Super Mario 64 ABE ported to the PC port with a ton of new features. This README file is going to be replaced eventually with something more proper. This repo does not include all assets necessary for compiling the game. A prior copy of the game is required to extract the assets. @@ -13,9 +10,9 @@ A prior copy of the game is required to extract the assets. ### Linux 1. Install prerequisites (Ubuntu): `sudo apt install -y git build-essential pkg-config libusb-1.0-0-dev libsdl2-dev`. -2. Clone the repo: `git clone https://github.com/sm64-port/sm64-port.git`. -3. Place a Super Mario 64 ROM called `baserom..z64` into the project folder for asset extraction, where `VERSION` can be `us`, `jp`, or `eu`. -4. Run `make` to build. Qualify the version through `make VERSION=`. Add `-j4` to improve build speed (hardware dependent based on the amount of CPU cores available). +2. Clone the repo: `git clone https://github.com/MorsGames/sm64-port.git`. +3. Place a Super Mario 64 ROM called `baserom..z64` into the project folder for asset extraction. For `VERSION` only `us` is properly supported. +4. Run `make` to build. Add `-j4` to improve build speed (hardware dependent based on the amount of CPU cores available). 5. The executable binary will be located at `build/_pc/sm64..f3dex2e`. ### Windows @@ -25,182 +22,7 @@ A prior copy of the game is required to extract the assets. a. 64-bit: Launch "MSYS2 MinGW 64-bit" and install: `pacman -S git make python3 mingw-w64-x86_64-gcc` b. 32-bit (will also work on 64-bit machines): Launch "MSYS2 MinGW 32-bit" and install: `pacman -S git make python3 mingw-w64-i686-gcc` * Do **NOT** install `gcc`. -3. Clone the repo: `git clone https://github.com/sm64-port/sm64-port.git` and enter it `cd sm64-port`. -4. Place a *Super Mario 64* ROM called `baserom..z64` into the project folder for asset extraction, where `VERSION` can be `us`, `jp`, or `eu`. -5. Run `make` to build. Qualify the version through `make VERSION=`. Add `-j4` to improve build speed (hardware dependent based on the amount of CPU cores available). +3. Clone the repo: `git clone https://github.com/MorsGames/sm64-port.git` and enter it `cd sm64-port`. +4. Place a *Super Mario 64* ROM called `baserom..z64` into the project folder for asset extraction. For `VERSION` only `us` is properly supported. +5. Run `make` to build. Add `-j4` to improve build speed (hardware dependent based on the amount of CPU cores available). 6. The executable binary will be located at `build/_pc/sm64..f3dex2e.exe`. - -### Debugging - -The code can be debugged using `gdb`. On Linux install the `gdb` package and execute `gdb `. On MSYS2 install by executing `pacman -S winpty gdb` and execute `winpty gdb `. The `winpty` program makes sure the keyboard works correctly in the terminal. In the Makefile, make sure you compile the sources using `-g` rather than `-O2` to include debugging symbols. See any online tutorial for how to use gdb. - -## Quick Start ROM building (for Ubuntu) - -1. Install prerequisites: `sudo apt install -y build-essential git binutils-mips-linux-gnu python3`. -2. Clone the repo from within Linux: `git clone https://github.com/n64decomp/sm64.git`. -3. Place a Super Mario 64 ROM called `baserom..z64` into the project folder for asset extraction, where `VERSION` can be `us`, `jp`, or `eu`. -4. Run `make` to build. Qualify the version through `make TARGET_N64=1 VERSION=`. Add `-j4` to improve build speed (hardware dependent based on the amount of CPU cores available). - -Ensure the repo path length does not exceed 255 characters. Long path names result in build errors. - -## Installation for ROM building - -### Windows - -Install WSL and a distro of your choice following -[Windows Subsystem for Linux Installation Guide for Windows 10.](https://docs.microsoft.com/en-us/windows/wsl/install-win10) -We recommend either Debian or Ubuntu 18.04 Linux distributions under WSL. -Note: WSL1 does not currently support Ubuntu 20.04. - -Next, clone the SM64 repo from within the Linux shell: -`git clone https://github.com/n64decomp/sm64.git` - -Then continue following the directions in the [Linux](#linux) installation section below. - -### Linux - -There are 3 steps to set up a working build. - -#### Step 1: Install dependencies - -The build system has the following package requirements: - * ``binutils-mips`` - * ``python3 >= 3.6`` - * ``qemu-irix`` (When building without GCC) - -Dependency installation instructions for common Linux distros are provided below: - -##### Debian / Ubuntu -To install build dependencies: -``` -sudo apt install -y build-essential git binutils-mips-linux-gnu python3 -``` - -Download latest package from [qemu-irix Releases.](https://github.com/n64decomp/qemu-irix/releases) - -Install this package with: -``` -sudo dpkg -i qemu-irix-2.11.0-2169-g32ab296eef_amd64.deb -``` - -##### Arch Linux -To install build dependencies: -``` -sudo pacman -S base-devel python -``` -Install the following AUR packages: -* [mips64-elf-binutils](https://aur.archlinux.org/packages/mips64-elf-binutils) (AUR) -* [qemu-irix-git](https://aur.archlinux.org/packages/qemu-irix-git) (AUR) - - -##### Other Linux distributions - -Most modern Linux distributions should have equivalent packages to the other two listed above. -You may have to use a different version of GNU binutils. Listed below are fully compatible binutils -distributions with support in the makefile, and examples of distros that offer them: - -* `mips64-elf-` (Arch AUR) -* `mips-linux-gnu-` (Ubuntu and other Debian-based distros) -* `mips64-linux-gnu-` (RHEL/CentOS/Fedora) - -You may also use [Docker](#docker-installation) to handle installing an image with minimal dependencies. - -#### Step 2: Copy baserom(s) for asset extraction - -For each version (jp/us/eu) for which you want to build a ROM, put an existing ROM at -`./baserom..z64` for asset extraction. - -##### Step 3: Build the ROM - -Run `make` to build the ROM (defaults to `VERSION=us`). -Other examples: -``` -make VERSION=jp -j4 # build (J) version instead with 4 jobs -make VERSION=eu COMPARE=0 # build (EU) version but do not compare ROM hashes -``` - -Resulting artifacts can be found in the `build` directory. - -The full list of configurable variables are listed below, with the default being the first listed: - -* ``VERSION``: ``us``, ``jp``, ``eu``, ``sh`` (WIP) -* ``GRUCODE``: ``f3d_old``, ``f3d_new``, ``f3dex``, ``f3dex2``, ``f3dzex`` -* ``COMPARE``: ``1`` (compare ROM hash), ``0`` (do not compare ROM hash) -* ``NON_MATCHING``: Use functionally equivalent C implementations for non-matchings. Also will avoid instances of undefined behavior. -* ``CROSS``: Cross-compiler tool prefix (Example: ``mips64-elf-``). -* ``QEMU_IRIX``: Path to a ``qemu-irix`` binary. -* ``TARGET_N64``: ``0`` If set to one, will build an N64 ROM. An unmodified repository will produce one of the following ROMs depending on what ``VERSION`` is set to: - - * sm64.jp.z64 `sha1: 8a20a5c83d6ceb0f0506cfc9fa20d8f438cafe51` - * sm64.us.z64 `sha1: 9bef1128717f958171a4afac3ed78ee2bb4e86ce` - * sm64.eu.z64 `sha1: 4ac5721683d0e0b6bbb561b58a71740845dceea9` - - -### macOS - -Installing Docker is the recommended avenue for macOS users. This project does not support macOS natively due to lack of macOS host support. - -### Docker Installation - -#### Create Docker image - -Create the docker image with `docker build -t sm64`. - -#### Build - -To build, mount the local filesystem into the Docker container and build the ROM with `docker run`. - -##### macOS example for (U): -``` -docker run --rm --mount type=bind,source="$(pwd)",destination=/sm64 sm64 make VERSION=us -j4 -``` - -##### Linux example for (U): -For a Linux host, Docker needs to be instructed which user should own the output files: -``` -docker run --rm --mount type=bind,source="$(pwd)",destination=/sm64 --user $UID:$UID sm64 make VERSION=us -j4 -``` - -Resulting artifacts can be found in the `build` directory. - -## Project Structure - -``` -sm64 -├── actors: object behaviors, geo layout, and display lists -├── asm: handwritten assembly code, rom header -│ └── non_matchings: asm for non-matching sections -├── assets: animation and demo data -│ ├── anims: animation data -│ └── demos: demo data -├── bin: C files for ordering display lists and textures -├── build: output directory -├── data: behavior scripts, misc. data -├── doxygen: documentation infrastructure -├── enhancements: example source modifications -├── include: header files -├── levels: level scripts, geo layout, and display lists -├── lib: SDK library code -├── rsp: audio and Fast3D RSP assembly code -├── sound: sequences, sound samples, and sound banks -├── src: C source code for game -│ ├── audio: audio code -│ ├── buffers: stacks, heaps, and task buffers -│ ├── engine: script processing engines and utils -│ ├── game: behaviors and rest of game source -│ ├── goddard: Mario intro screen -│ ├── menu: title screen and file, act, and debug level selection menus -│ └── pc: port code, audio and video renderer -├── text: dialog, level names, act names -├── textures: skybox and generic texture data -└── tools: build tools -``` - -## Contributing - -Pull requests are welcome. For major changes, please open an issue first to -discuss what you would like to change. - -Run `clang-format` on your code to ensure it meets the project's coding standards. - -Official Discord: https://discord.gg/7bcNTPK diff --git a/include/PR/os_cont.h b/include/PR/os_cont.h index 15ee60f..0ce764e 100644 --- a/include/PR/os_cont.h +++ b/include/PR/os_cont.h @@ -60,6 +60,8 @@ typedef struct { u16 button; s8 stick_x; /* -80 <= stick_x <= 80 */ s8 stick_y; /* -80 <= stick_y <= 80 */ + s8 stick2_x; /* -80 <= stick_x <= 80 */ + s8 stick2_y; /* -80 <= stick_y <= 80 */ u8 errnum; } OSContPad; diff --git a/include/dialog_ids.h b/include/dialog_ids.h index 6feadaf..8af6c35 100644 --- a/include/dialog_ids.h +++ b/include/dialog_ids.h @@ -172,6 +172,9 @@ enum DialogId { DIALOG_167, DIALOG_168, DIALOG_169, + DIALOG_170, + DIALOG_171, + DIALOG_172, DIALOG_COUNT }; diff --git a/include/sm64.h b/include/sm64.h index 632224e..23d5562 100644 --- a/include/sm64.h +++ b/include/sm64.h @@ -296,6 +296,7 @@ #define ACT_GETTING_BLOWN 0x010208B8 // (0x0B8 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) #define ACT_THROWN_FORWARD 0x010208BD // (0x0BD | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) #define ACT_THROWN_BACKWARD 0x010208BE // (0x0BE | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_WALL_SLIDE 0x000008BF // (0x0BF | ACT_FLAG_AIR) New addition! // group 0x0C0: submerged actions #define ACT_WATER_IDLE 0x380022C0 // (0x0C0 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_PAUSE_EXIT | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) diff --git a/include/text_strings.h.in b/include/text_strings.h.in index 749179b..7a465a0 100644 --- a/include/text_strings.h.in +++ b/include/text_strings.h.in @@ -140,6 +140,14 @@ #define TEXT_FILE_MARIO_B _("MARIO B") #define TEXT_FILE_MARIO_C _("MARIO C") #define TEXT_FILE_MARIO_D _("MARIO D") +#define TEXT_FILE_HARD_A _("HARD A") +#define TEXT_FILE_HARD_B _("HARD B") +#define TEXT_FILE_HARD_C _("HARD C") +#define TEXT_FILE_HARD_D _("HARD D") +#define TEXT_FILE_HARDCORE_A _("PERMADEATH A") +#define TEXT_FILE_HARDCORE_B _("PERMADEATH B") +#define TEXT_FILE_HARDCORE_C _("PERMADEATH C") +#define TEXT_FILE_HARDCORE_D _("PERMADEATH D") // Menu Options #define TEXT_SCORE _("SCORE") diff --git a/include/types.h b/include/types.h index 7120c25..734eaff 100644 --- a/include/types.h +++ b/include/types.h @@ -30,6 +30,11 @@ struct Controller /*0x12*/ u16 buttonPressed; /*0x14*/ OSContStatus *statusData; /*0x18*/ OSContPad *controllerData; + /*0x00*/ s16 rawStick2X; // + /*0x02*/ s16 rawStick2Y; // + /*0x04*/ float stick2X; // [-64, 64] positive is right + /*0x08*/ float stick2Y; // [-64, 64] positive is up + /*0x0C*/ float stick2Mag; // distance from center [0, 64] #ifdef VERSION_SH /*0x1C*/ int port; #endif diff --git a/levels/ending/script.c b/levels/ending/script.c index ad3d738..18ca722 100644 --- a/levels/ending/script.c +++ b/levels/ending/script.c @@ -15,6 +15,7 @@ #include "make_const_nonconst.h" #include "levels/ending/header.h" +#include "levels/intro/header.h" const LevelScript level_ending_entry[] = { /*0*/ INIT_LEVEL(), @@ -32,6 +33,8 @@ const LevelScript level_ending_entry[] = { /*14*/ SLEEP(/*frames*/ 120), /*15*/ CALL(/*arg*/ 0, /*func*/ lvl_play_the_end_screen_sound), // L1: - /*17*/ SLEEP(/*frames*/ 1), - /*18*/ JUMP(level_ending_entry + 17), + /*17*/ CALL_LOOP(/*arg*/ 1, /*func*/ credits_wait_for_reset), + /*18*/ TRANSITION(/*transType*/ WARP_TRANSITION_FADE_INTO_COLOR, /*time*/ 75, /*color*/ 0x00, 0x00, 0x00), + /*19*/ SLEEP(/*frames*/ 240), + /*20*/ EXECUTE(/*seg*/ 0x14, /*script*/ _introSegmentRomStart, /*scriptEnd*/ _introSegmentRomEnd, /*entry*/ level_intro_entry_2), }; diff --git a/levels/level_defines.h b/levels/level_defines.h index c14f252..2e341d8 100644 --- a/levels/level_defines.h +++ b/levels/level_defines.h @@ -20,38 +20,38 @@ STUB_LEVEL( "", LEVEL_UNKNOWN_1, COURSE_NONE, 20000, 0x00, 0x00, 0x00, _, _) STUB_LEVEL( "", LEVEL_UNKNOWN_2, COURSE_NONE, 20000, 0x00, 0x00, 0x00, _, _) STUB_LEVEL( "", LEVEL_UNKNOWN_3, COURSE_NONE, 20000, 0x00, 0x00, 0x00, _, _) -DEFINE_LEVEL("TERESA OBAKE", LEVEL_BBH, COURSE_BBH, bbh, spooky, 28000, 0x28, 0x28, 0x28, sDynBbh, sCamBBH) -DEFINE_LEVEL("YYAMA1 % YSLD1", LEVEL_CCM, COURSE_CCM, ccm, snow, 17000, 0x10, 0x38, 0x38, _, sCamCCM) -DEFINE_LEVEL("SELECT ROOM", LEVEL_CASTLE, COURSE_NONE, castle_inside, inside, 20000, 0x20, 0x20, 0x30, _, sCamCastle) -DEFINE_LEVEL("HORROR DUNGEON", LEVEL_HMC, COURSE_HMC, hmc, cave, 16000, 0x28, 0x28, 0x28, sDynHmc, sCamHMC) -DEFINE_LEVEL("SABAKU % PYRMD", LEVEL_SSL, COURSE_SSL, ssl, generic, 15000, 0x08, 0x30, 0x30, _, sCamSSL) -DEFINE_LEVEL("BATTLE FIELD", LEVEL_BOB, COURSE_BOB, bob, generic, 15000, 0x08, 0x08, 0x08, _, _) -DEFINE_LEVEL("YUKIYAMA2", LEVEL_SL, COURSE_SL, sl, snow, 14000, 0x10, 0x28, 0x28, _, sCamSL) -DEFINE_LEVEL("POOL KAI", LEVEL_WDW, COURSE_WDW, wdw, grass, 17000, 0x10, 0x18, 0x18, sDynWdw, _) -DEFINE_LEVEL("WTDG % TINBOTU", LEVEL_JRB, COURSE_JRB, jrb, water, 20000, 0x10, 0x18, 0x18, sDynJrb, _) -DEFINE_LEVEL("BIG WORLD", LEVEL_THI, COURSE_THI, thi, grass, 20000, 0x0c, 0x0c, 0x20, _, sCamTHI) -DEFINE_LEVEL("CLOCK TOWER", LEVEL_TTC, COURSE_TTC, ttc, machine, 18000, 0x18, 0x18, 0x18, _, _) -DEFINE_LEVEL("RAINBOW CRUISE", LEVEL_RR, COURSE_RR, rr, sky, 20000, 0x20, 0x20, 0x20, _, sCamRR) -DEFINE_LEVEL("MAIN MAP", LEVEL_CASTLE_GROUNDS, COURSE_NONE, castle_grounds, outside, 25000, 0x08, 0x08, 0x08, _, _) -DEFINE_LEVEL("EXT1 YOKO SCRL", LEVEL_BITDW, COURSE_BITDW, bitdw, sky, 16000, 0x28, 0x28, 0x28, _, _) -DEFINE_LEVEL("EXT7 HORI MINI", LEVEL_VCUTM, COURSE_VCUTM, vcutm, outside, 30000, 0x28, 0x28, 0x28, _, _) -DEFINE_LEVEL("EXT2 TIKA LAVA", LEVEL_BITFS, COURSE_BITFS, bitfs, sky, 16000, 0x28, 0x28, 0x28, _, _) -DEFINE_LEVEL("EXT9 SUISOU", LEVEL_SA, COURSE_SA, sa, inside, 20000, 0x10, 0x10, 0x10, _, _) -DEFINE_LEVEL("EXT3 HEAVEN", LEVEL_BITS, COURSE_BITS, bits, sky, 16000, 0x28, 0x28, 0x28, _, _) -DEFINE_LEVEL("FIREB1 % INVLC", LEVEL_LLL, COURSE_LLL, lll, fire, 22000, 0x08, 0x30, 0x30, _, _) -DEFINE_LEVEL("WATER LAND", LEVEL_DDD, COURSE_DDD, ddd, water, 17000, 0x10, 0x20, 0x20, sDynDdd, _) -DEFINE_LEVEL("MOUNTAIN", LEVEL_WF, COURSE_WF, wf, grass, 13000, 0x08, 0x08, 0x08, _, _) +DEFINE_LEVEL("BIG BOOS HAUNT", LEVEL_BBH, COURSE_BBH, bbh, spooky, 28000, 0x28, 0x28, 0x28, sDynBbh, sCamBBH) +DEFINE_LEVEL("CC MOUNTAIN", LEVEL_CCM, COURSE_CCM, ccm, snow, 17000, 0x10, 0x38, 0x38, _, sCamCCM) +DEFINE_LEVEL("CASTLE INSIDE", LEVEL_CASTLE, COURSE_NONE, castle_inside, inside, 20000, 0x20, 0x20, 0x30, _, sCamCastle) +DEFINE_LEVEL("HAY MAE CAE", LEVEL_HMC, COURSE_HMC, hmc, cave, 16000, 0x28, 0x28, 0x28, sDynHmc, sCamHMC) +DEFINE_LEVEL("S SAND LAND", LEVEL_SSL, COURSE_SSL, ssl, generic, 15000, 0x08, 0x30, 0x30, _, sCamSSL) +DEFINE_LEVEL("BO BATTLEFIELD", LEVEL_BOB, COURSE_BOB, bob, generic, 15000, 0x08, 0x08, 0x08, _, _) +DEFINE_LEVEL("SNOWMANS LAND", LEVEL_SL, COURSE_SL, sl, snow, 14000, 0x10, 0x28, 0x28, _, sCamSL) +DEFINE_LEVEL("WET DRY WORLD", LEVEL_WDW, COURSE_WDW, wdw, grass, 17000, 0x10, 0x18, 0x18, sDynWdw, _) +DEFINE_LEVEL("OLLY ROGER BAY", LEVEL_JRB, COURSE_JRB, jrb, water, 20000, 0x10, 0x18, 0x18, sDynJrb, _) +DEFINE_LEVEL("TH ISLAND", LEVEL_THI, COURSE_THI, thi, grass, 20000, 0x0c, 0x0c, 0x20, _, sCamTHI) +DEFINE_LEVEL("TICK TOCK CLOCK", LEVEL_TTC, COURSE_TTC, ttc, machine, 18000, 0x18, 0x18, 0x18, _, _) +DEFINE_LEVEL("RAINBOW RIDE", LEVEL_RR, COURSE_RR, rr, sky, 20000, 0x20, 0x20, 0x20, _, sCamRR) +DEFINE_LEVEL("CASTLE GROUNDS", LEVEL_CASTLE_GROUNDS, COURSE_NONE, castle_grounds, outside, 25000, 0x08, 0x08, 0x08, _, _) +DEFINE_LEVEL("B IN DARK WORLD", LEVEL_BITDW, COURSE_BITDW, bitdw, sky, 16000, 0x28, 0x28, 0x28, _, _) +DEFINE_LEVEL("ANISH CAP", LEVEL_VCUTM, COURSE_VCUTM, vcutm, outside, 30000, 0x28, 0x28, 0x28, _, _) +DEFINE_LEVEL("B IN FIRE SEA", LEVEL_BITFS, COURSE_BITFS, bitfs, sky, 16000, 0x28, 0x28, 0x28, _, _) +DEFINE_LEVEL("SECRET AUARIUM", LEVEL_SA, COURSE_SA, sa, inside, 20000, 0x10, 0x10, 0x10, _, _) +DEFINE_LEVEL("B IN THE SKY", LEVEL_BITS, COURSE_BITS, bits, sky, 16000, 0x28, 0x28, 0x28, _, _) +DEFINE_LEVEL("LETHAL LAA LAND", LEVEL_LLL, COURSE_LLL, lll, fire, 22000, 0x08, 0x30, 0x30, _, _) +DEFINE_LEVEL("DIRE DIRE DOCKS", LEVEL_DDD, COURSE_DDD, ddd, water, 17000, 0x10, 0x20, 0x20, sDynDdd, _) +DEFINE_LEVEL("WHOMPS FORTRESS", LEVEL_WF, COURSE_WF, wf, grass, 13000, 0x08, 0x08, 0x08, _, _) DEFINE_LEVEL("ENDING", LEVEL_ENDING, COURSE_CAKE_END, ending, generic, 20000, 0x00, 0x00, 0x00, _, _) -DEFINE_LEVEL("URANIWA", LEVEL_CASTLE_COURTYARD, COURSE_NONE, castle_courtyard, outside, 20000, 0x08, 0x08, 0x08, _, _) -DEFINE_LEVEL("EXT4 MINI SLID", LEVEL_PSS, COURSE_PSS, pss, mountain, 20000, 0x28, 0x28, 0x28, _, _) -DEFINE_LEVEL("IN THE FALL", LEVEL_COTMC, COURSE_COTMC, cotmc, cave, 18000, 0x28, 0x28, 0x28, _, sCamCotMC) -DEFINE_LEVEL("EXT6 MARIO FLY", LEVEL_TOTWC, COURSE_TOTWC, totwc, sky, 20000, 0x20, 0x20, 0x20, _, _) -DEFINE_LEVEL("KUPPA1", LEVEL_BOWSER_1, COURSE_BITDW, bowser_1, generic, VAL_DIFF, 0x40, 0x40, 0x40, _, _) -DEFINE_LEVEL("EXT8 BLUE SKY", LEVEL_WMOTR, COURSE_WMOTR, wmotr, generic, 20000, 0x28, 0x28, 0x28, _, _) +DEFINE_LEVEL("CASTLE COURTYARD", LEVEL_CASTLE_COURTYARD, COURSE_NONE, castle_courtyard, outside, 20000, 0x08, 0x08, 0x08, _, _) +DEFINE_LEVEL("SECRET SLIDE", LEVEL_PSS, COURSE_PSS, pss, mountain, 20000, 0x28, 0x28, 0x28, _, _) +DEFINE_LEVEL("C OF METAL CAP", LEVEL_COTMC, COURSE_COTMC, cotmc, cave, 18000, 0x28, 0x28, 0x28, _, sCamCotMC) +DEFINE_LEVEL("T OF WING CAP", LEVEL_TOTWC, COURSE_TOTWC, totwc, sky, 20000, 0x20, 0x20, 0x20, _, _) +DEFINE_LEVEL("BOWSER 1", LEVEL_BOWSER_1, COURSE_BITDW, bowser_1, generic, VAL_DIFF, 0x40, 0x40, 0x40, _, _) +DEFINE_LEVEL("WMOTR", LEVEL_WMOTR, COURSE_WMOTR, wmotr, generic, 20000, 0x28, 0x28, 0x28, _, _) STUB_LEVEL( "", LEVEL_UNKNOWN_32, COURSE_NONE, 20000, 0x70, 0x00, 0x00, _, _) -DEFINE_LEVEL("KUPPA2", LEVEL_BOWSER_2, COURSE_BITFS, bowser_2, fire, VAL_DIFF, 0x40, 0x40, 0x40, _, _) -DEFINE_LEVEL("KUPPA3", LEVEL_BOWSER_3, COURSE_BITS, bowser_3, generic, VAL_DIFF, 0x40, 0x40, 0x40, _, _) +DEFINE_LEVEL("BOWSER 2", LEVEL_BOWSER_2, COURSE_BITFS, bowser_2, fire, VAL_DIFF, 0x40, 0x40, 0x40, _, _) +DEFINE_LEVEL("BOWSER 3", LEVEL_BOWSER_3, COURSE_BITS, bowser_3, generic, VAL_DIFF, 0x40, 0x40, 0x40, _, _) STUB_LEVEL( "", LEVEL_UNKNOWN_35, COURSE_NONE, 20000, 0x00, 0x00, 0x00, _, _) -DEFINE_LEVEL("DONKEY % SLID2", LEVEL_TTM, COURSE_TTM, ttm, mountain, 15000, 0x08, 0x08, 0x08, _, _) +DEFINE_LEVEL("TT MOUNTAIN", LEVEL_TTM, COURSE_TTM, ttm, mountain, 15000, 0x08, 0x08, 0x08, _, _) STUB_LEVEL( "", LEVEL_UNKNOWN_37, COURSE_NONE, 20000, 0x00, 0x00, 0x00, _, _) STUB_LEVEL( "", LEVEL_UNKNOWN_38, COURSE_NONE, 20000, 0x00, 0x00, 0x00, sDynUnk38, _) diff --git a/levels/menu/script.c b/levels/menu/script.c index f6db65c..5a917fc 100644 --- a/levels/menu/script.c +++ b/levels/menu/script.c @@ -90,7 +90,7 @@ const LevelScript level_main_menu_entry_2[] = { /*37*/ TRANSITION(/*transType*/ WARP_TRANSITION_FADE_INTO_COLOR, /*time*/ 16, /*color*/ 0xFF, 0xFF, 0xFF), /*39*/ SLEEP(/*frames*/ 16), /*40*/ CLEAR_LEVEL(), - /*41*/ SLEEP_BEFORE_EXIT(/*frames*/ 1), + /*41*/ SLEEP_BEFORE_EXIT(/*frames*/ 9), //MORS NOTE: This was changed from 1 to simulate the load times. // L1: /*42*/ EXIT(), }; diff --git a/src/audio/external.c b/src/audio/external.c index 5d78747..4afcba9 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -250,7 +250,8 @@ u8 sDialogSpeaker[] = { /*13*/ _, _, TUXIE, _, _, _, _, _, _, _, /*14*/ _, _, _, _, _, _, _, _, _, _, /*15*/ WIGLR, WIGLR, WIGLR, _, _, _, _, _, _, _, - /*16*/ _, YOSHI, _, _, _, _, _, _, WIGLR, _ + /*16*/ _, YOSHI, _, _, _, _, _, _, WIGLR, _, + /*NW*/ _, TUXIE, TUXIE }; #undef _ STATIC_ASSERT(ARRAY_COUNT(sDialogSpeaker) == DIALOG_COUNT, "change this array if you are adding dialogs"); diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index cc39e5a..0ebd97a 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -14,6 +14,8 @@ #include "graph_node.h" #include "surface_collision.h" +#include "game/settings.h" + // Macros for retrieving arguments from behavior scripts. #define BHV_CMD_GET_1ST_U8(index) (u8)((gCurBhvCommand[index] >> 24) & 0xFF) // unused #define BHV_CMD_GET_2ND_U8(index) (u8)((gCurBhvCommand[index] >> 16) & 0xFF) @@ -988,15 +990,24 @@ void cur_obj_update(void) { cur_obj_enable_rendering_if_mario_in_room(); } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { - // If the object has a render distance, check if it should be shown. - if (distanceFromMario > gCurrentObject->oDrawingDistance) { - // Out of render distance, hide the object. - gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; - } else if (gCurrentObject->oHeldState == HELD_FREE) { - // In render distance (and not being held), show the object. - gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; - gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; + if (gDisableDrawDistance) { + if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) + { + gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; + } + } + else { + // If the object has a render distance, check if it should be shown. + if (distanceFromMario > gCurrentObject->oDrawingDistance) { + // Out of render distance, hide the object. + gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; + } else if (gCurrentObject->oHeldState == HELD_FREE) { + // In render distance (and not being held), show the object. + gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; + } } } } diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c index 9aff62f..9a6b77c 100644 --- a/src/engine/surface_collision.c +++ b/src/engine/surface_collision.c @@ -8,6 +8,8 @@ #include "surface_collision.h" #include "surface_load.h" +#include "game/settings.h" + /************************************************** * WALLS * **************************************************/ @@ -403,6 +405,7 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 f32 oo; f32 height; struct Surface *floor = NULL; + f32 peak = -11000.0f; // Iterate through the list of floors until there are no more floors. while (surfaceNode != NULL) { @@ -461,10 +464,15 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 *pheight = height; floor = surf; break; + + // floor cucking fix, shoutouts to kaze and whoever josh is + if (gCollisionFixes && peak < height) { + peak = height; + *pheight = height; + floor = surf; + } } - //! (Surface Cucking) Since only the first floor is returned and not the highest, - // higher floors can be "cucked" by lower floors. return floor; } diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c index 6936d8a..b1db02a 100644 --- a/src/engine/surface_load.c +++ b/src/engine/surface_load.c @@ -14,6 +14,9 @@ #include "game/mario.h" #include "game/object_list_processor.h" #include "surface_load.h" +#include "game/game_init.h" + +#include "game/settings.h" s32 unused8038BE90; @@ -787,7 +790,7 @@ void load_object_collision_model(void) { } } - if (marioDist < gCurrentObject->oDrawingDistance) { + if (marioDist < gCurrentObject->oDrawingDistance || gDisableDrawDistance) { gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; } else { gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; diff --git a/src/game/area.c b/src/game/area.c index d458bf7..9b1f9e9 100644 --- a/src/game/area.c +++ b/src/game/area.c @@ -22,6 +22,8 @@ #include "save_file.h" #include "level_table.h" +#include "settings.h" + struct SpawnInfo gPlayerSpawnInfos[1]; struct GraphNode *D_8033A160[0x100]; struct Area gAreaData[8]; @@ -238,6 +240,8 @@ void load_area(s32 index) { load_obj_warp_nodes(); geo_call_global_function_nodes(&gCurrentArea->unk04->node, GEO_CONTEXT_AREA_LOAD); + + gCanMirror = 0; } } @@ -256,6 +260,10 @@ void load_mario_area(void) { func_80320890(); load_area(gMarioSpawnInfo->areaIndex); + gCanMirror = 1; + if (gEncoreMode) + gReimportTextures = 1; + if (gCurrentArea->index == gMarioSpawnInfo->areaIndex) { gCurrentArea->flags |= 0x01; spawn_objects_from_info(0, gMarioSpawnInfo); @@ -369,7 +377,9 @@ void render_game(void) { gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - BORDER_HEIGHT); - render_hud(); + if (!gHideHud) { + render_hud(); + } gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); render_text_labels(); diff --git a/src/game/behaviors/boo.inc.c b/src/game/behaviors/boo.inc.c index 8525e28..bc9e49e 100644 --- a/src/game/behaviors/boo.inc.c +++ b/src/game/behaviors/boo.inc.c @@ -460,16 +460,21 @@ static void boo_act_4(void) { // If there are no remaining "minion" boos, show the dialog of the Big Boo if (cur_obj_nearest_object_with_behavior(bhvGhostHuntBoo) == NULL) { dialogID = DIALOG_108; + gBooDialogueWasSaid = 0; } else { dialogID = DIALOG_107; } - if (cur_obj_update_dialog(2, 2, dialogID, 0)) { - create_sound_spawner(SOUND_OBJ_DYING_ENEMY1); - obj_mark_for_deletion(o); + if (!gBooDialogueWasSaid) { + if (cur_obj_update_dialog(2, 2, dialogID, 0)) { + create_sound_spawner(SOUND_OBJ_DYING_ENEMY1); + obj_mark_for_deletion(o); - if (dialogID == DIALOG_108) { // If the Big Boo should spawn, play the jingle - play_puzzle_jingle(); + if (dialogID == DIALOG_108) { // If the Big Boo should spawn, play the jingle + play_puzzle_jingle(); + } + else if (gDisableBooDialogue) + gBooDialogueWasSaid = 1; } } } @@ -524,7 +529,12 @@ static void big_boo_act_0(void) { o->oBooTargetOpacity = 0xFF; o->oBooBaseScale = 3.0f; - o->oHealth = 3; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oHealth = 5; + } + else { + o->oHealth = 3; + } cur_obj_scale(3.0f); cur_obj_become_tangible(); @@ -540,12 +550,23 @@ static void big_boo_act_1(void) { s16 sp22; f32 sp1C; - if (o->oHealth == 3) { - sp22 = 0x180; sp1C = 0.5f; - } else if (o->oHealth == 2) { - sp22 = 0x240; sp1C = 0.6f; - } else { - sp22 = 0x300; sp1C = 0.8f; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + if (o->oHealth == 3) { + sp22 = 0x300; sp1C = 0.8f; + } else if (o->oHealth == 2) { + sp22 = 0x360; sp1C = 1.0f; + } else { + sp22 = 0x420; sp1C = 1.2f; + } + } + else { + if (o->oHealth == 3) { + sp22 = 0x180; sp1C = 0.5f; + } else if (o->oHealth == 2) { + sp22 = 0x240; sp1C = 0.6f; + } else { + sp22 = 0x300; sp1C = 0.8f; + } } boo_chase_mario(-100.0f, sp22, sp1C); diff --git a/src/game/behaviors/bowser.inc.c b/src/game/behaviors/bowser.inc.c index 357505a..cbe98d5 100644 --- a/src/game/behaviors/bowser.inc.c +++ b/src/game/behaviors/bowser.inc.c @@ -1006,6 +1006,7 @@ struct SoundState D_8032F5B8[] = { { 0, 0, 0, NO_SOUND }, { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR } }; s8 D_8032F690[4] = { 0, 0, 1, 0 }; s8 D_8032F694[4] = { 1, 1, 3, 0 }; +s8 D_8032F694_hard[4] = { 3, 4, 5, 0 }; extern u8 bowser_3_seg7_collision_07004B94[]; extern u8 bowser_3_seg7_collision_07004C18[]; extern u8 bowser_3_seg7_collision_07004C9C[]; @@ -1150,7 +1151,12 @@ void bhv_bowser_init(void) { level = 0; o->oBehParams2ndByte = level; o->oBowserUnk1B2 = D_8032F690[level]; - o->oHealth = D_8032F694[level]; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oHealth = D_8032F694_hard[level]; + } + else { + o->oHealth = D_8032F694[level]; + } cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_INIT); o->oAction = 5; o->oBowserUnk1AE = 0; diff --git a/src/game/behaviors/bub.inc.c b/src/game/behaviors/bub.inc.c index e8e6309..c0a4e78 100644 --- a/src/game/behaviors/bub.inc.c +++ b/src/game/behaviors/bub.inc.c @@ -3,12 +3,13 @@ // NOTE: These first set of functions spawn a school of bub depending on objF4's // value. The later action functions seem to check Y distance to Mario and proceed // to do nothing, which indicates this behavior set is incomplete. +#include "../settings.h" // TODO: Rename these. These have nothing to do with birds. void bub_spawner_act_0(void) { s32 i; s32 sp18 = o->oBirdChirpChirpUnkF4; - if (o->oDistanceToMario < 1500.0f) { + if (o->oDistanceToMario < 1500.0f && !gDisableDrawDistance) { for (i = 0; i < sp18; i++) spawn_object(o, MODEL_BUB, bhvBub); o->oAction = 1; diff --git a/src/game/behaviors/camera_lakitu.inc.c b/src/game/behaviors/camera_lakitu.inc.c index 0f70c33..616bdea 100644 --- a/src/game/behaviors/camera_lakitu.inc.c +++ b/src/game/behaviors/camera_lakitu.inc.c @@ -28,7 +28,8 @@ void bhv_camera_lakitu_init(void) { static void camera_lakitu_intro_act_trigger_cutscene(void) { //! These bounds are slightly smaller than the actual bridge bounds, allowing // the RTA speedrunning method of lakitu skip - if (gMarioObject->oPosX > -544.0f && gMarioObject->oPosX < 545.0f && gMarioObject->oPosY > 800.0f + if (!gSkipCutscenes + && gMarioObject->oPosX > -544.0f && gMarioObject->oPosX < 545.0f && gMarioObject->oPosY > 800.0f && gMarioObject->oPosZ > -2000.0f && gMarioObject->oPosZ < -177.0f && gMarioObject->oPosZ < -177.0f) // always double check your conditions { diff --git a/src/game/behaviors/castle_floor_trap.inc.c b/src/game/behaviors/castle_floor_trap.inc.c index 9eeddbb..3bf212f 100644 --- a/src/game/behaviors/castle_floor_trap.inc.c +++ b/src/game/behaviors/castle_floor_trap.inc.c @@ -1,4 +1,5 @@ // castle_floor_trap.c.inc +#include "../settings.h" void bhv_floor_trap_in_castle_loop(void) { if (gMarioObject->platform == o) @@ -25,8 +26,14 @@ void bhv_castle_floor_trap_open_detect(void) { } void bhv_castle_floor_trap_open(void) { - if (o->oTimer == 0) - cur_obj_play_sound_2(SOUND_GENERAL_CASTLE_TRAP_OPEN); + if (o->oTimer == 0) { + if (gTrapdoorSound) { + play_sound(SOUND_GENERAL_CASTLE_TRAP_OPEN, gDefaultSoundArgs); + } + else { + cur_obj_play_sound_2(SOUND_GENERAL_CASTLE_TRAP_OPEN); + } + } o->oAngleVelRoll -= 0x100; o->oFaceAngleRoll += o->oAngleVelRoll; if (o->oFaceAngleRoll < -0x4000) { diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c index a77c5d5..1b7a74b 100644 --- a/src/game/behaviors/chain_chomp.inc.c +++ b/src/game/behaviors/chain_chomp.inc.c @@ -8,6 +8,7 @@ * Processing order is bhvWoodenPost, bhvChainChompGate, bhvChainChomp, bhvChainChompChainPart. * The chain parts are processed starting at the post and ending at the chomp. */ +#include "../settings.h" /** * Hitbox for chain chomp. @@ -53,7 +54,7 @@ static void chain_chomp_act_uninitialized(void) { struct ChainSegment *segments; s32 i; - if (o->oDistanceToMario < 3000.0f) { + if (o->oDistanceToMario < 3000.0f || gDisableDrawDistance) { segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); if (segments != NULL) { // Each segment represents the offset of a chain part to the pivot. @@ -359,7 +360,7 @@ static void chain_chomp_act_move(void) { f32 maxDistToPivot; // Unload chain if mario is far enough - if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { + if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f && !gDisableDrawDistance) { o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; o->oForwardVel = o->oVelY = 0.0f; } else { diff --git a/src/game/behaviors/cloud.inc.c b/src/game/behaviors/cloud.inc.c index 527e582..110a8a9 100644 --- a/src/game/behaviors/cloud.inc.c +++ b/src/game/behaviors/cloud.inc.c @@ -8,6 +8,7 @@ * If spawned by a lakitu, its parent will be the lakitu. * Processing order is lakitu -> cloud -> its cloud parts. */ +#include "../settings.h" /** * The relative heights of each cloud part. @@ -47,7 +48,7 @@ static void cloud_act_spawn_parts(void) { * Wait for mario to approach, then unhide and enter the spawn parts action. */ static void cloud_act_fwoosh_hidden(void) { - if (o->oDistanceToMario < 2000.0f) { + if (o->oDistanceToMario < 2000.0f && !gDisableDrawDistance) { cur_obj_unhide(); o->oAction = CLOUD_ACT_SPAWN_PARTS; } @@ -58,7 +59,7 @@ static void cloud_act_fwoosh_hidden(void) { * long enough, blow wind at him. */ static void cloud_fwoosh_update(void) { - if (o->oDistanceToMario > 2500.0f) { + if (o->oDistanceToMario > 2500.0f && !gDisableDrawDistance) { o->oAction = CLOUD_ACT_UNLOAD; } else { if (o->oCloudBlowing) { diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c index 913c583..2b0f79d 100644 --- a/src/game/behaviors/coin.inc.c +++ b/src/game/behaviors/coin.inc.c @@ -1,4 +1,5 @@ // coin.c.inc +#include "../settings.h" struct ObjectHitbox sYellowCoinHitbox = { /* interactType: */ INTERACT_COIN, @@ -184,7 +185,7 @@ void bhv_coin_formation_loop(void) { s32 bitIndex; switch (o->oAction) { case 0: - if (o->oDistanceToMario < 2000.0f) { + if (o->oDistanceToMario < 2000.0f || gDisableDrawDistance) { for (bitIndex = 0; bitIndex < 8; bitIndex++) { if (!(o->oCoinUnkF4 & (1 << bitIndex))) spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte); @@ -193,7 +194,7 @@ void bhv_coin_formation_loop(void) { } break; case 1: - if (o->oDistanceToMario > 2100.0f) + if (o->oDistanceToMario > 2100.0f && !gDisableDrawDistance) o->oAction++; break; case 2: diff --git a/src/game/behaviors/enemy_lakitu.inc.c b/src/game/behaviors/enemy_lakitu.inc.c index 056c3f1..d77da31 100644 --- a/src/game/behaviors/enemy_lakitu.inc.c +++ b/src/game/behaviors/enemy_lakitu.inc.c @@ -4,6 +4,7 @@ * Lakitu comes before it spawned spinies in processing order. * TODO: bhvCloud processing oredr */ +#include "../settings.h" /** * Hitbox for evil lakitu. @@ -24,7 +25,7 @@ static struct ObjectHitbox sEnemyLakituHitbox = { * Wait for mario to approach, then spawn the cloud and become visible. */ static void enemy_lakitu_act_uninitialized(void) { - if (o->oDistanceToMario < 2000.0f) { + if (o->oDistanceToMario < 2000.0f && !gDisableDrawDistance) { spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud); cur_obj_unhide(); diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c index c771176..9cb2da4 100644 --- a/src/game/behaviors/fish.inc.c +++ b/src/game/behaviors/fish.inc.c @@ -2,6 +2,7 @@ * @file fish.inc.c * Implements behaviour and spawning for fish located in the Secret Aquarium and other levels. */ +#include "../settings.h" /** * Spawns fish with settings chosen by the field o->oBehParams2ndByte. @@ -42,14 +43,17 @@ void fish_act_spawn(void) { * If the current level is Secret Aquarium, ignore this requirement. * Fish moves at random with a max-range of 700.0f. */ - if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { - for (i = 0; i < schoolQuantity; i++) { - fishObject = spawn_object(o, model, bhvFish); - fishObject->oBehParams2ndByte = o->oBehParams2ndByte; - obj_init_animation_with_sound(fishObject, fishAnimation, 0); - obj_translate_xyz_random(fishObject, 700.0f); + if(!gDisableDrawDistance) + { + if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { + for (i = 0; i < schoolQuantity; i++) { + fishObject = spawn_object(o, model, bhvFish); + fishObject->oBehParams2ndByte = o->oBehParams2ndByte; + obj_init_animation_with_sound(fishObject, fishAnimation, 0); + obj_translate_xyz_random(fishObject, 700.0f); + } + o->oAction = FISH_ACT_ACTIVE; } - o->oAction = FISH_ACT_ACTIVE; } } diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c index 2dab2fe..d8163d8 100644 --- a/src/game/behaviors/goomba.inc.c +++ b/src/game/behaviors/goomba.inc.c @@ -4,6 +4,7 @@ * Goombas can either be spawned individually, or spawned by a triplet spawner. * The triplet spawner comes before its spawned goombas in processing order. */ +#include "../settings.h" /** * Hitbox for goomba. @@ -78,7 +79,7 @@ void bhv_goomba_triplet_spawner_update(void) { // If mario is close enough and the goombas aren't currently loaded, then // spawn them if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) { - if (o->oDistanceToMario < 3000.0f) { + if (o->oDistanceToMario < 3000.0f && !gDisableDrawDistance) { // The spawner is capable of spawning more than 3 goombas, but this // is not used in the game dAngle = @@ -99,7 +100,7 @@ void bhv_goomba_triplet_spawner_update(void) { o->oAction += 1; } - } else if (o->oDistanceToMario > 4000.0f) { + } else if (o->oDistanceToMario > 4000.0f && !gDisableDrawDistance) { // If mario is too far away, enter the unloaded action. The goombas // will detect this and unload themselves o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED; diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c index 662bb0b..4985388 100644 --- a/src/game/behaviors/heave_ho.inc.c +++ b/src/game/behaviors/heave_ho.inc.c @@ -1,4 +1,5 @@ // heave_ho.c.inc +#include "../settings.h" s16 D_8032F460[][2] = { { 30, 0 }, { 42, 1 }, { 52, 0 }, { 64, 1 }, { 74, 0 }, { 86, 1 }, { 96, 0 }, { 108, 1 }, { 118, 0 }, { -1, 0 }, }; @@ -73,7 +74,7 @@ void heave_ho_act_3(void) { void heave_ho_act_0(void) { cur_obj_set_pos_to_home(); - if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f) { + if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f || gDisableDrawDistance) { cur_obj_become_tangible(); cur_obj_unhide(); o->oAction = 1; diff --git a/src/game/behaviors/hidden_star.inc.c b/src/game/behaviors/hidden_star.inc.c index 03b7150..d954ed9 100644 --- a/src/game/behaviors/hidden_star.inc.c +++ b/src/game/behaviors/hidden_star.inc.c @@ -54,6 +54,10 @@ void bhv_hidden_star_trigger_loop(void) { o->activeFlags = ACTIVE_FLAG_DEACTIVATED; } + + if (gVisibleSecrets) { + spawn_object(o, MODEL_NONE, bhvSparkleSpawn); + } } void bhv_bowser_course_red_coin_star_loop(void) { diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c index e64230d..6ed5a8c 100644 --- a/src/game/behaviors/king_bobomb.inc.c +++ b/src/game/behaviors/king_bobomb.inc.c @@ -1,4 +1,5 @@ // king_bobomb.c.inc +#include "../settings.h" // Copy of geo_update_projectile_pos_from_parent Gfx *geo_update_held_mario_pos(s32 run, UNUSED struct GraphNode *node, Mat4 mtx) { @@ -30,7 +31,12 @@ void king_bobomb_act_0(void) { gSecondCameraFocus = o; cur_obj_init_animation_with_sound(5); cur_obj_set_pos_to_home(); - o->oHealth = 3; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oHealth = 5; + } + else { + o->oHealth = 3; + } if (cur_obj_can_mario_activate_textbox_2(500.0f, 100.0f)) { o->oSubAction++; func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40); @@ -66,8 +72,14 @@ void king_bobomb_act_2(void) { } else cur_obj_init_animation_with_sound(11); if (o->oKingBobombUnk108 == 0) { - o->oForwardVel = 3.0f; - cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x100); + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oForwardVel = 10.0f; + cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x200); + } + else { + o->oForwardVel = 3.0f; + cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x100); + } } else { o->oForwardVel = 0.0f; o->oKingBobombUnk108--; @@ -295,10 +307,12 @@ void king_bobomb_move(void) { cur_obj_move_using_fvel_and_gravity(); cur_obj_call_action_function(sKingBobombActions); exec_anim_sound_state(sKingBobombSoundStates); - if (o->oDistanceToMario < 5000.0f) - cur_obj_enable_rendering(); - else - cur_obj_disable_rendering(); + if (!gDisableDrawDistance) { + if (o->oDistanceToMario < 5000.0f) + cur_obj_enable_rendering(); + else + cur_obj_disable_rendering(); + } } void bhv_king_bobomb_loop(void) { diff --git a/src/game/behaviors/koopa.inc.c b/src/game/behaviors/koopa.inc.c index 2303906..babb4fd 100644 --- a/src/game/behaviors/koopa.inc.c +++ b/src/game/behaviors/koopa.inc.c @@ -614,15 +614,27 @@ static void koopa_the_quick_act_race(void) { case KOOPA_THE_QUICK_SUB_ACT_RUN: koopa_the_quick_animate_footsteps(); - if (o->parentObj->oKoopaRaceEndpointRaceStatus != 0 && o->oDistanceToMario > 1500.0f + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + if (o->parentObj->oKoopaRaceEndpointRaceStatus != 0 && o->oDistanceToMario > 1500.0f && (o->oPathedPrevWaypointFlags & WAYPOINT_MASK_00FF) < 28) { - // Move faster if mario has already finished the race or - // cheated by shooting from cannon - o->oKoopaAgility = 8.0f; - } else if (o->oKoopaTheQuickRaceIndex != KOOPA_THE_QUICK_BOB_INDEX) { - o->oKoopaAgility = 6.0f; - } else { - o->oKoopaAgility = 4.0f; + // Move faster if mario has already finished the race or + // cheated by shooting from cannon + o->oKoopaAgility = 12.0f; + } else { + o->oKoopaAgility = 8.0f; + } + } + else { + if (o->parentObj->oKoopaRaceEndpointRaceStatus != 0 && o->oDistanceToMario > 1500.0f + && (o->oPathedPrevWaypointFlags & WAYPOINT_MASK_00FF) < 28) { + // Move faster if mario has already finished the race or + // cheated by shooting from cannon + o->oKoopaAgility = 8.0f; + } else if (o->oKoopaTheQuickRaceIndex != KOOPA_THE_QUICK_BOB_INDEX) { + o->oKoopaAgility = 6.0f; + } else { + o->oKoopaAgility = 4.0f; + } } obj_forward_vel_approach(o->oKoopaAgility * 6.0f * downhillSteepness, diff --git a/src/game/behaviors/lll_floating_wood_piece.inc.c b/src/game/behaviors/lll_floating_wood_piece.inc.c index cb4dad4..2fb4323 100644 --- a/src/game/behaviors/lll_floating_wood_piece.inc.c +++ b/src/game/behaviors/lll_floating_wood_piece.inc.c @@ -1,4 +1,5 @@ // lll_floating_wood_piece.c.inc +#include "../settings.h" void bhv_lll_wood_piece_loop(void) { if (o->oTimer == 0) @@ -14,7 +15,7 @@ void bhv_lll_floating_wood_bridge_loop(void) { s32 i; switch (o->oAction) { case 0: - if (o->oDistanceToMario < 2500.0f) { + if (o->oDistanceToMario < 2500.0f && !gDisableDrawDistance) { for (i = 1; i < 4; i++) { sp3C = spawn_object_relative(0, (i - 2) * 300, 0, 0, o, MODEL_LLL_WOOD_BRIDGE, bhvLllWoodPiece); @@ -24,7 +25,7 @@ void bhv_lll_floating_wood_bridge_loop(void) { } break; case 1: - if (o->oDistanceToMario > 2600.0f) + if (o->oDistanceToMario > 2600.0f && !gDisableDrawDistance) o->oAction = 2; break; case 2: diff --git a/src/game/behaviors/mushroom_1up.inc.c b/src/game/behaviors/mushroom_1up.inc.c index 103534d..4977f75 100644 --- a/src/game/behaviors/mushroom_1up.inc.c +++ b/src/game/behaviors/mushroom_1up.inc.c @@ -5,7 +5,17 @@ void bhv_1up_interact(void) { if (obj_check_if_collided_with_object(o, gMarioObject) == 1) { play_sound(SOUND_GENERAL_COLLECT_1UP, gDefaultSoundArgs); - gMarioState->numLives++; + if (gGreenDemon != 0) { + gMarioState->health = 0; + } + else if (gLifeMode || save_file_get_flags() & SAVE_FLAG_HARDCORE_MODE) { + gMarioState->healCounter += 31.75; + if (gMarioState->healCounter > 31.75) + gMarioState->healCounter = 31.75; + } + else { + gMarioState->numLives++; + } o->activeFlags = ACTIVE_FLAG_DEACTIVATED; } } diff --git a/src/game/behaviors/piranha_plant.inc.c b/src/game/behaviors/piranha_plant.inc.c index e8abe08..02cbac6 100644 --- a/src/game/behaviors/piranha_plant.inc.c +++ b/src/game/behaviors/piranha_plant.inc.c @@ -3,6 +3,7 @@ * This controls Piranha Plants, which alternate between sleeping, attacking, * and dying, primarily depending on Mario's proximity and interaction state. */ +#include "../settings.h" /** * Reset the Piranha Plant back to a sleeping animation, no matter what state @@ -330,7 +331,7 @@ void bhv_piranha_plant_loop(void) { cur_obj_call_action_function(TablePiranhaPlantActions); // In WF, hide all Piranha Plants once high enough up. - if (gCurrLevelNum == LEVEL_WF) { + if (gCurrLevelNum == LEVEL_WF && !gDisableDrawDistance) { if (gMarioObject->oPosY > 3400.0f) cur_obj_hide(); else diff --git a/src/game/behaviors/pokey.inc.c b/src/game/behaviors/pokey.inc.c index df5d11f..4834b1b 100644 --- a/src/game/behaviors/pokey.inc.c +++ b/src/game/behaviors/pokey.inc.c @@ -6,6 +6,7 @@ * Pokey comes before its body parts in processing order, and the body parts * are processed top to bottom. */ +#include "../settings.h" /** * Hitbox for a single pokey body part. @@ -151,7 +152,7 @@ static void pokey_act_uninitialized(void) { s32 i; s16 partModel; - if (o->oDistanceToMario < 2000.0f) { + if (o->oDistanceToMario < 2000.0f && !gDisableDrawDistance) { partModel = MODEL_POKEY_HEAD; for (i = 0; i < 5; i++) { @@ -185,7 +186,7 @@ static void pokey_act_wander(void) { if (o->oPokeyNumAliveBodyParts == 0) { obj_mark_for_deletion(o); - } else if (o->oDistanceToMario > 2500.0f) { + } else if (o->oDistanceToMario > 2500.0f && !gDisableDrawDistance) { o->oAction = POKEY_ACT_UNLOAD_PARTS; o->oForwardVel = 0.0f; } else { diff --git a/src/game/behaviors/racing_penguin.inc.c b/src/game/behaviors/racing_penguin.inc.c index fea739b..de038a1 100644 --- a/src/game/behaviors/racing_penguin.inc.c +++ b/src/game/behaviors/racing_penguin.inc.c @@ -7,10 +7,22 @@ struct RacingPenguinData { static struct RacingPenguinData sRacingPenguinData[] = { { DIALOG_055, 200.0f, 200.0f }, { DIALOG_164, 350.0f, 250.0f }, + { DIALOG_171, 0.0f, 0.0f }, + { DIALOG_172, 0.0f, 0.0f }, }; void bhv_racing_penguin_init(void) { - if (gMarioState->numStars == 120) { + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + if (gMarioState->numStars == 120) { + cur_obj_scale(0.25f); + o->oBehParams2ndByte = 3; + } + else { + cur_obj_scale(0.5f); + o->oBehParams2ndByte = 2; + } + } + else if (gMarioState->numStars == 120) { cur_obj_scale(8.0f); o->header.gfx.scale[1] = 5.0f; o->oBehParams2ndByte = 1; @@ -67,7 +79,12 @@ static void racing_penguin_act_race(void) { o->oAction = RACING_PENGUIN_ACT_FINISH_RACE; } else { targetSpeed = o->oPosY - gMarioObject->oPosY; - minSpeed = 70.0f; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + minSpeed = 90.0f; + } + else { + minSpeed = 70.0f; + } cur_obj_play_sound_1(SOUND_AIR_ROUGH_SLIDE); diff --git a/src/game/behaviors/triplet_butterfly.inc.c b/src/game/behaviors/triplet_butterfly.inc.c index 1c2b926..93eced6 100644 --- a/src/game/behaviors/triplet_butterfly.inc.c +++ b/src/game/behaviors/triplet_butterfly.inc.c @@ -1,3 +1,5 @@ +#include "../settings.h" + struct TripletButterflyActivationData { s32 model; const BehaviorScript *behavior; @@ -54,7 +56,7 @@ static void triplet_butterfly_act_init(void) { } static void triplet_butterfly_act_wander(void) { - if (o->oDistanceToMario > 1500.0f) { + if (o->oDistanceToMario > 1500.0f && !gDisableDrawDistance) { obj_mark_for_deletion(o); } else { approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f); diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c index 8e9ba33..0d03504 100644 --- a/src/game/behaviors/water_bomb_cannon.inc.c +++ b/src/game/behaviors/water_bomb_cannon.inc.c @@ -1,4 +1,5 @@ // water_bomb_cannon.inc.c +#include "../settings.h" void bhv_bubble_cannon_barrel_loop(void) { struct Object *val04; @@ -38,7 +39,7 @@ void bhv_bubble_cannon_barrel_loop(void) { } void water_bomb_cannon_act_0(void) { - if (o->oDistanceToMario < 2000.0f) { + if (o->oDistanceToMario < 2000.0f || gDisableDrawDistance) { spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles); cur_obj_unhide(); @@ -48,7 +49,7 @@ void water_bomb_cannon_act_0(void) { } void water_bomb_cannon_act_1(void) { - if (o->oDistanceToMario > 2500.0f) { + if (o->oDistanceToMario > 2500.0f && !gDisableDrawDistance) { o->oAction = 2; } else if (o->oBehParams2ndByte == 0) { if (o->oWaterCannonUnkF4 != 0) { diff --git a/src/game/behaviors/whirlpool.inc.c b/src/game/behaviors/whirlpool.inc.c index 405e051..aca15d0 100644 --- a/src/game/behaviors/whirlpool.inc.c +++ b/src/game/behaviors/whirlpool.inc.c @@ -1,4 +1,5 @@ // whirlpool.c.inc +#include "../settings.h" static struct ObjectHitbox sWhirlpoolHitbox = { /* interactType: */ INTERACT_WHIRLPOOL, @@ -35,7 +36,7 @@ void whirpool_orient_graph(void) { } void bhv_whirlpool_loop(void) { - if (o->oDistanceToMario < 5000.0f) { + if (o->oDistanceToMario < 5000.0f && !gDisableDrawDistance) { o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; // not sure if actually an array diff --git a/src/game/behaviors/whomp.inc.c b/src/game/behaviors/whomp.inc.c index 93ace53..dbd8a3e 100644 --- a/src/game/behaviors/whomp.inc.c +++ b/src/game/behaviors/whomp.inc.c @@ -1,4 +1,5 @@ // whomp.c.inc +#include "../settings.h" void whomp_play_sfx_from_pound_animation(void) { UNUSED s32 sp2C = o->header.gfx.unk38.animFrame; @@ -26,7 +27,12 @@ void whomp_act_0(void) { func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40); } else { cur_obj_set_pos_to_home(); - o->oHealth = 3; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oHealth = 5; + } + else { + o->oHealth = 3; + } } } else if (cur_obj_update_dialog_with_cutscene(2, 1, CUTSCENE_DIALOG, DIALOG_114)) o->oAction = 2; @@ -85,7 +91,12 @@ void whomp_act_2(void) { sp1E = abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw); if (sp1E < 0x2000) { if (o->oDistanceToMario < 1500.0f) { - o->oForwardVel = 9.0f; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oForwardVel = 27.0f; + } + else { + o->oForwardVel = 9.0f; + } cur_obj_init_animation_with_accel_and_sound(0, 3.0f); } if (o->oDistanceToMario < 300.0f) @@ -101,7 +112,12 @@ void whomp_act_2(void) { void whomp_act_3(void) { o->oForwardVel = 0.0f; - cur_obj_init_animation_with_accel_and_sound(1, 1.0f); + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + cur_obj_init_animation_with_accel_and_sound(1, 3.0f); + } + else { + cur_obj_init_animation_with_accel_and_sound(1, 1.0f); + } if (cur_obj_check_if_near_animation_end()) o->oAction = 4; } @@ -111,7 +127,12 @@ void whomp_act_4(void) { o->oVelY = 40.0f; if (o->oTimer < 8) { } else { - o->oAngleVelPitch += 0x100; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oAngleVelPitch += 0x200; + } + else { + o->oAngleVelPitch += 0x100; + } o->oFaceAnglePitch += o->oAngleVelPitch; if (o->oFaceAnglePitch > 0x4000) { o->oAngleVelPitch = 0; @@ -245,11 +266,13 @@ void bhv_whomp_loop(void) { cur_obj_update_floor_and_walls(); cur_obj_call_action_function(sWhompActions); cur_obj_move_standard(-20); - if (o->oAction != 9) { - if (o->oBehParams2ndByte != 0) - cur_obj_hide_if_mario_far_away_y(2000.0f); - else - cur_obj_hide_if_mario_far_away_y(1000.0f); - load_object_collision_model(); + if (!gDisableDrawDistance) { + if (o->oAction != 9) { + if (o->oBehParams2ndByte != 0) + cur_obj_hide_if_mario_far_away_y(2000.0f); + else + cur_obj_hide_if_mario_far_away_y(1000.0f); + } } + load_object_collision_model(); } diff --git a/src/game/behaviors/wiggler.inc.c b/src/game/behaviors/wiggler.inc.c index 4c465f8..050766d 100644 --- a/src/game/behaviors/wiggler.inc.c +++ b/src/game/behaviors/wiggler.inc.c @@ -316,9 +316,17 @@ static void wiggler_act_jumped_on(void) { if (o->oHealth == 2) { cur_obj_play_sound_2(SOUND_OBJ_WIGGLER_JUMP); - o->oForwardVel = 10.0f; + if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oForwardVel = 40.0f; + } + else { + o->oForwardVel = 10.0f; + } o->oVelY = 70.0f; } + else if (save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + o->oForwardVel = 30.0f; + } } } } diff --git a/src/game/behaviors/yoshi.inc.c b/src/game/behaviors/yoshi.inc.c index 136a2bd..dd40207 100644 --- a/src/game/behaviors/yoshi.inc.c +++ b/src/game/behaviors/yoshi.inc.c @@ -132,8 +132,11 @@ void yoshi_finish_jumping_and_despawn_loop(void) { void yoshi_give_present_loop(void) { s32 sp1C = gGlobalTimer; - if (gHudDisplay.lives == 100) { - play_sound(SOUND_GENERAL_COLLECT_1UP, gDefaultSoundArgs); + if ((!gLifeMode && gHudDisplay.lives == 100) + || (gLifeMode && gHudDisplay.lives == 0)) { + if (gLifeMode) { + play_sound(SOUND_GENERAL_COLLECT_1UP, gDefaultSoundArgs); + } gSpecialTripleJump = 1; o->oAction = YOSHI_ACT_WALK_JUMP_OFF_ROOF; return; @@ -141,7 +144,12 @@ void yoshi_give_present_loop(void) { if ((sp1C & 0x03) == 0) { play_sound(SOUND_MENU_YOSHI_GAIN_LIVES, gDefaultSoundArgs); - gMarioState->numLives++; + if (gLifeMode) { + gMarioState->numLives--; + } + else { + gMarioState->numLives++; + } } } diff --git a/src/game/camera.c b/src/game/camera.c index 60bfb86..e7b7581 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -29,6 +29,8 @@ #include "engine/graph_node.h" #include "level_table.h" +#include "settings.h" + #define CBUTTON_MASK (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS) /** @@ -483,6 +485,10 @@ CameraTransition sModeTransitions[] = { extern u8 sDanceCutsceneIndexTable[][4]; extern u8 sZoomOutAreaMasks[]; +//Define the analog camera speed +#define ANALOG_AMOUNT 12 * (1 - gInvertedCamera * 2) +#define LROTATE_SPEED 0x400 + /** * Starts a camera shake triggered by an interaction */ @@ -906,7 +912,7 @@ s32 update_radial_camera(struct Camera *c, Vec3f focus, Vec3f pos) { UNUSED f32 unused2; UNUSED f32 unused3; f32 yOff = 125.f; - f32 baseDist = 1000.f; + f32 baseDist = 1000.f+gAdditionalCameraDistance; sAreaYaw = camYaw - sModeOffsetYaw; calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f); @@ -930,7 +936,7 @@ s32 update_8_directions_camera(struct Camera *c, Vec3f focus, Vec3f pos) { UNUSED f32 unused2; UNUSED f32 unused3; f32 yOff = 125.f; - f32 baseDist = 1000.f; + f32 baseDist = 1000.f+gAdditionalCameraDistance; sAreaYaw = camYaw; calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f); @@ -959,6 +965,7 @@ void radial_camera_move(struct Camera *c) { f32 areaDistX = sMarioCamState->pos[0] - c->areaCenX; f32 areaDistZ = sMarioCamState->pos[2] - c->areaCenZ; UNUSED s32 filler; + s16 lCamRotation = DEGREES(180); // How much the camera's yaw changed s16 yawOffset = calculate_yaw(sMarioCamState->pos, c->pos) - atan2s(areaDistZ, areaDistX); @@ -1032,7 +1039,7 @@ void radial_camera_move(struct Camera *c) { sModeOffsetYaw = avoidYaw; gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE); } - + // If it's the first time rotating, just rotate to +-60 degrees if (!(s2ndRotateFlags & CAM_MOVE_ROTATE_RIGHT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT) && camera_approach_s16_symmetric_bool(&sModeOffsetYaw, maxAreaYaw, rotateSpeed) == 0) { @@ -1054,6 +1061,30 @@ void radial_camera_move(struct Camera *c) { gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE); s2ndRotateFlags &= ~CAM_MOVE_ROTATE_LEFT; } + + // Analog camera code + if (gPlayer1Controller->stick2X != 0) { + if (gPlayer1Controller->stick2X > 0) { + gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE); + } + else { + gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE); + } + sModeOffsetYaw -= ANALOG_AMOUNT * (gPlayer1Controller->stick2X / 32.0f) * gCameraSpeed; + } + + if (gCenterCam) { + if (c->mode == CAMERA_MODE_OUTWARD_RADIAL) { + lCamRotation = 0; + } + if (gPlayer1Controller->buttonPressed & L_TRIG) { + sModeOffsetYaw = sMarioCamState->faceAngle[1]+lCamRotation-sAreaYaw; + play_sound_rbutton_changed(); + } + if (gPlayer1Controller->buttonDown & L_TRIG) { + camera_approach_s16_symmetric_bool(&sModeOffsetYaw, sMarioCamState->faceAngle[1]+lCamRotation-sAreaYaw, LROTATE_SPEED); + } + } } if (!(gCameraMovementFlags & CAM_MOVE_ROTATE)) { // If not rotating, rotate away from walls obscuring Mario from view @@ -1070,13 +1101,14 @@ void radial_camera_move(struct Camera *c) { } } } - - // Bound sModeOffsetYaw within (-120, 120) degrees - if (sModeOffsetYaw > 0x5554) { - sModeOffsetYaw = 0x5554; - } - if (sModeOffsetYaw < -0x5554) { - sModeOffsetYaw = -0x5554; + if (!gImprovedCamera) { + // Bound sModeOffsetYaw within (-120, 120) degrees + if (sModeOffsetYaw > 0x5554) { + sModeOffsetYaw = 0x5554; + } + if (sModeOffsetYaw < -0x5554) { + sModeOffsetYaw = -0x5554; + } } } @@ -1184,6 +1216,27 @@ void mode_8_directions_camera(struct Camera *c) { play_sound_cbutton_side(); } + // Analog camera code + if (gPlayer1Controller->stick2X != 0) { + if (gPlayer1Controller->stick2X > 0) { + gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE); + } + else { + gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE); + } + s8DirModeYawOffset -= ANALOG_AMOUNT * (gPlayer1Controller->stick2X / 32.0f) * gCameraSpeed; + } + + if (gCenterCam) { + if (gPlayer1Controller->buttonPressed & L_TRIG) { + s8DirModeYawOffset = sMarioCamState->faceAngle[1] + DEGREES(180); + play_sound_rbutton_changed(); + } + if (gPlayer1Controller->buttonDown & L_TRIG) { + camera_approach_s16_symmetric_bool(&s8DirModeYawOffset, sMarioCamState->faceAngle[1] + DEGREES(180), LROTATE_SPEED); + } + } + lakitu_zoom(400.f, 0x900); c->nextYaw = update_8_directions_camera(c, c->focus, pos); c->pos[0] = pos[0]; @@ -1201,7 +1254,7 @@ s32 update_outward_radial_camera(struct Camera *c, Vec3f focus, Vec3f pos) { f32 zDistFocToMario = sMarioCamState->pos[2] - c->areaCenZ; s16 camYaw = atan2s(zDistFocToMario, xDistFocToMario) + sModeOffsetYaw + DEGREES(180); s16 pitch = look_down_slopes(camYaw); - f32 baseDist = 1000.f; + f32 baseDist = 1000.f+gAdditionalCameraDistance; // A base offset of 125.f is ~= Mario's eye height f32 yOff = 125.f; f32 posY; @@ -2092,6 +2145,8 @@ s16 update_default_camera(struct Camera *c) { handle_c_button_movement(c); vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw); + gCameraZoomDist += gAdditionalCameraDistance; + // If C-Down is active, determine what distance the camera should be from Mario if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) { //! In Mario mode, the camera is zoomed out further than in Lakitu mode (1400 vs 1200) @@ -2372,6 +2427,17 @@ s16 update_default_camera(struct Camera *c) { if (gCurrLevelArea == AREA_WDW_TOWN) { yaw = clamp_positions_and_find_yaw(c->pos, c->focus, 2254.f, -3789.f, 3790.f, -2253.f); } + + if (gCenterCam) { + if (gPlayer1Controller->buttonPressed & L_TRIG) { + if (yaw != sMarioCamState->faceAngle[1] + DEGREES(180)) { + yaw = sMarioCamState->faceAngle[1] + DEGREES(180); + focus_on_mario(c->focus, c->pos, 0.0f, 0.0f, dist, pitch, yaw); + } + play_sound_rbutton_changed(); + } + } + return yaw; } @@ -5024,6 +5090,17 @@ void handle_c_button_movement(struct Camera *c) { sCSideButtonYaw = cSideYaw; } } + + // Analog camera code + if (gPlayer1Controller->stick2X != 0) { + if (gPlayer1Controller->stick2X > 0) { + gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE); + } + else { + gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE); + } + sCSideButtonYaw = ANALOG_AMOUNT * (gPlayer1Controller->stick2X / 32.0f) * gCameraSpeed; + } } } @@ -7055,8 +7132,8 @@ void player2_rotate_cam(struct Camera *c, s16 minPitch, s16 maxPitch, s16 minYaw s16 pitch, yaw, pitchCap; // Change the camera rotation to match the 2nd player's stick - approach_s16_asymptotic_bool(&sCreditsPlayer2Yaw, -(s16)(gPlayer2Controller->stickX * 250.f), 4); - approach_s16_asymptotic_bool(&sCreditsPlayer2Pitch, -(s16)(gPlayer2Controller->stickY * 265.f), 4); + approach_s16_asymptotic_bool(&sCreditsPlayer2Yaw, -(s16)(gPlayer1Controller->stick2X * 250.f), 4); + approach_s16_asymptotic_bool(&sCreditsPlayer2Pitch, -(s16)(gPlayer1Controller->stick2Y * 265.f), 4); vec3f_get_dist_and_angle(c->pos, c->focus, &distCamToFocus, &pitch, &yaw); pitchCap = 0x3800 - pitch; if (pitchCap < 0) { @@ -11443,7 +11520,7 @@ Gfx *geo_camera_fov(s32 callContext, struct GraphNode *g, UNUSED void *context) } } - perspective->fov = sFOVState.fov; + perspective->fov = sFOVState.fov + gAdditionalFOV; shake_camera_fov(perspective); return NULL; } diff --git a/src/game/game_init.c b/src/game/game_init.c index 3ce5f2d..27cd2f1 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -21,6 +21,8 @@ #include "thread6.h" #include +#include "settings.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 struct Controller gControllers[3]; @@ -45,6 +47,10 @@ struct MarioAnimation D_80339D10; struct MarioAnimation gDemo; UNUSED u8 filler80339D30[0x90]; +s8 gCanMirror = 0; +s8 gReimportTextures = 0; +s8 gBooDialogueWasSaid = 0; + int unused8032C690 = 0; u32 gGlobalTimer = 0; @@ -59,6 +65,26 @@ struct DemoInput *gCurrDemoInput = NULL; // demo input sequence u16 gDemoInputListID = 0; struct DemoInput gRecordedDemoInput = { 0 }; // possibly removed in EU. TODO: Check +s8 get_mirror() { + if (gEncoreMode == 0) { + return 0; + } + + if (gCurrCourseNum == COURSE_CAKE_END) { + return 0; + } + + return gCanMirror; +} + +s16 get_palette() { + if (!gEncoreMode || !gCanMirror) { + return 0; + } + + return gCurrCourseNum+1; +} + /** * Initializes the Reality Display Processor (RDP). * This function initializes settings such as texture filtering mode, @@ -76,7 +102,12 @@ void my_rdp_init(void) { gDPSetTextureLUT(gDisplayListHead++, G_TT_NONE); gDPSetTextureDetail(gDisplayListHead++, G_TD_CLAMP); gDPSetTexturePersp(gDisplayListHead++, G_TP_PERSP); - gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP); + if (gNearestNeighbor) { + gDPSetTextureFilter(gDisplayListHead++, G_TF_POINT); + } + else { + gDPSetTextureFilter(gDisplayListHead++, G_TF_BILERP); + } gDPSetTextureConvert(gDisplayListHead++, G_TC_FILT); gDPSetCombineKey(gDisplayListHead++, G_CK_NONE); @@ -378,6 +409,8 @@ void adjust_analog_stick(struct Controller *controller) { // reset the controller's x and y floats. controller->stickX = 0; controller->stickY = 0; + controller->stick2X = 0; + controller->stick2Y = 0; // modulate the rawStickX and rawStickY to be the new f32 values by adding/subtracting 6. if (controller->rawStickX <= -8) { @@ -396,9 +429,28 @@ void adjust_analog_stick(struct Controller *controller) { controller->stickY = controller->rawStickY - 6; } + if (controller->rawStick2X <= -8) { + controller->stick2X = controller->rawStick2X + 6; + } + + if (controller->rawStick2X >= 8) { + controller->stick2X = controller->rawStick2X - 6; + } + + if (controller->rawStick2Y <= -8) { + controller->stick2Y = controller->rawStick2Y + 6; + } + + if (controller->rawStick2Y >= 8) { + controller->stick2Y = controller->rawStick2Y - 6; + } + // calculate f32 magnitude from the center by vector length. controller->stickMag = sqrtf(controller->stickX * controller->stickX + controller->stickY * controller->stickY); + controller->stick2Mag = + sqrtf(controller->stick2X * controller->stick2X + controller->stick2Y * controller->stick2Y); + // magnitude cannot exceed 64.0f: if it does, modify the values appropriately to // flatten the values down to the allowed maximum value. @@ -407,6 +459,11 @@ void adjust_analog_stick(struct Controller *controller) { controller->stickY *= 64 / controller->stickMag; controller->stickMag = 64; } + if (controller->stick2Mag > 64) { + controller->stick2X *= 64 / controller->stick2Mag; + controller->stick2Y *= 64 / controller->stick2Mag; + controller->stick2Mag = 64; + } } // if a demo sequence exists, this will run the demo @@ -495,8 +552,23 @@ void read_controller_inputs(void) { // if we're receiving inputs, update the controller struct // with the new button info. if (controller->controllerData != NULL) { + controller->rawStickX = controller->controllerData->stick_x; controller->rawStickY = controller->controllerData->stick_y; + + if (gDpadInput) { + controller->rawStickX += (((controller->buttonDown & R_JPAD) > 0) - ((controller->buttonDown & L_JPAD) > 0))*80; + controller->rawStickY += (((controller->buttonDown & U_JPAD) > 0) - ((controller->buttonDown & D_JPAD) > 0))*80; + } + + controller->rawStick2X = controller->controllerData->stick2_x; + controller->rawStick2Y = controller->controllerData->stick2_y; + + if (get_mirror()) { + controller->rawStickX *= -1; + controller->rawStick2X *= -1; + } + controller->buttonPressed = controller->controllerData->button & (controller->controllerData->button ^ controller->buttonDown); // 0.5x A presses are a good meme @@ -506,10 +578,14 @@ void read_controller_inputs(void) { { controller->rawStickX = 0; controller->rawStickY = 0; + controller->rawStick2X = 0; + controller->rawStick2Y = 0; controller->buttonPressed = 0; controller->buttonDown = 0; controller->stickX = 0; controller->stickY = 0; + controller->stick2X = 0; + controller->stick2Y = 0; controller->stickMag = 0; } } diff --git a/src/game/game_init.h b/src/game/game_init.h index 4384cbc..b50a1b5 100644 --- a/src/game/game_init.h +++ b/src/game/game_init.h @@ -59,6 +59,10 @@ extern struct MarioAnimation gDemo; extern u8 gMarioAnims[]; extern u8 gDemoInputs[]; +extern s8 gCanMirror; +extern s8 gReimportTextures; +extern s8 gBooDialogueWasSaid; + extern u16 frameBufferIndex; extern u32 gGlobalTimer; diff --git a/src/game/hud.c b/src/game/hud.c index 8d4daa5..5f5f464 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -14,6 +14,8 @@ #include "save_file.h" #include "print.h" +#include "settings.h" + /* @file hud.c * This file implements HUD rendering and power meter animations. * That includes stars, lives, coins, camera status, power meter, timer @@ -53,6 +55,25 @@ static struct PowerMeterHUD sPowerMeterHUD = { // when the power meter is hidden. s32 sPowerMeterVisibleTimer = 0; +// Custom left and right snapping functions +s32 get_left(s32 value) { + if (gCenterHud || gDrawPillarbox) { + return value; + } + else { + return GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(value); + } +} +s32 get_right(s32 value) { + if (gCenterHud || gDrawPillarbox) { + return SCREEN_WIDTH-value; + } + else { + return GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(value); + } +} + + static struct UnusedHUDStruct sUnusedHUDValues = { 0x00, 0x0A, 0x00 }; static struct CameraHUD sCameraHUD = { CAM_STATUS_NONE }; @@ -116,7 +137,25 @@ void render_dl_power_meter(s16 numHealthWedges) { return; } - guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); + if (gNewHud == 2) { + if (get_mirror()) { + guTranslate(mtx, SCREEN_WIDTH - get_right(46), (f32) sPowerMeterHUD.y, 0); + } + else { + guTranslate(mtx, get_right(46), (f32) sPowerMeterHUD.y, 0); + } + } + else if (gNewHud == 1) { + guTranslate(mtx, SCREEN_WIDTH/2, (f32) sPowerMeterHUD.y, 0); + } + else { + if (get_mirror()) { + guTranslate(mtx, SCREEN_WIDTH - (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); + } + else { + guTranslate(mtx, (f32) sPowerMeterHUD.x, (f32) sPowerMeterHUD.y, 0); + } + } gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx++), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); @@ -229,31 +268,39 @@ void handle_power_meter_actions(s16 numHealthWedges) { void render_hud_power_meter(void) { s16 shownHealthWedges = gHudDisplay.wedges; - if (sPowerMeterHUD.animation != POWER_METER_HIDING) { + if ((gNewHud < 2) && (!gAlwaysShowHealth)) { + if (sPowerMeterHUD.animation != POWER_METER_HIDING) { + handle_power_meter_actions(shownHealthWedges); + } + + if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) { + return; + } + + switch (sPowerMeterHUD.animation) { + case POWER_METER_EMPHASIZED: + animate_power_meter_emphasized(); + break; + case POWER_METER_DEEMPHASIZING: + animate_power_meter_deemphasizing(); + break; + case POWER_METER_HIDING: + animate_power_meter_hiding(); + break; + default: + break; + } + } + else { + sPowerMeterHUD.y = 200; handle_power_meter_actions(shownHealthWedges); } - if (sPowerMeterHUD.animation == POWER_METER_HIDDEN) { - return; - } - - switch (sPowerMeterHUD.animation) { - case POWER_METER_EMPHASIZED: - animate_power_meter_emphasized(); - break; - case POWER_METER_DEEMPHASIZING: - animate_power_meter_deemphasizing(); - break; - case POWER_METER_HIDING: - animate_power_meter_hiding(); - break; - default: - break; - } - render_dl_power_meter(shownHealthWedges); - sPowerMeterVisibleTimer += 1; + if ((gNewHud < 2) && (!gAlwaysShowHealth)) { + sPowerMeterVisibleTimer += 1; + } } #ifdef VERSION_JP @@ -262,22 +309,59 @@ void render_hud_power_meter(void) { #define HUD_TOP_Y 209 #endif +#define HUD_TOP_Y_NEW 212 +#define HUD_LEFT_X 22 + /** * Renders the amount of lives Mario has. */ void render_hud_mario_lives(void) { - print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(22), HUD_TOP_Y, ","); // 'Mario Head' glyph - print_text(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(38), HUD_TOP_Y, "*"); // 'X' glyph - print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(54), HUD_TOP_Y, "%d", gHudDisplay.lives); + s32 x; + s32 y; + if (gNewHud == 2) { + x = get_left(HUD_LEFT_X)+64; + y = HUD_TOP_Y_NEW; + } + else if (gNewHud == 1) { + x = get_left(HUD_LEFT_X); + y = HUD_TOP_Y_NEW; + } + else { + x = get_left(22); + y = HUD_TOP_Y; + } + /*if (gLifeMode) { + print_text(x, y, "Q"); // The new'Mario Head' glyph + } + else {*/ + print_text(x, y, ","); // 'Mario Head' glyph + //} + if (gNewHud > 0) { + print_text(x+17, y, "*"); // 'X' glyph + print_text_fmt_int(x+33, y, "%d", gHudDisplay.lives); + } + else { + print_text(x+16, y, "*"); // 'X' glyph + print_text_fmt_int(x+32, y, "%d", gHudDisplay.lives); + } } +#define HUD_COIN_X 168 + /** * Renders the amount of coins collected. */ void render_hud_coins(void) { - print_text(168, HUD_TOP_Y, "+"); // 'Coin' glyph - print_text(184, HUD_TOP_Y, "*"); // 'X' glyph - print_text_fmt_int(198, HUD_TOP_Y, "%d", gHudDisplay.coins); + if (gNewHud > 0) { + print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW-18, "+"); // 'Coin' glyph + print_text(get_left(HUD_LEFT_X)+17, HUD_TOP_Y_NEW-18, "*"); // 'X' glyph + print_text_fmt_int(get_left(HUD_LEFT_X)+33, HUD_TOP_Y_NEW-18, "%d", gHudDisplay.coins); + } + else { + print_text(HUD_COIN_X, HUD_TOP_Y, "+"); // 'Coin' glyph + print_text(HUD_COIN_X+16, HUD_TOP_Y, "*"); // 'X' glyph + print_text_fmt_int(HUD_COIN_X+30, HUD_TOP_Y, "%d", gHudDisplay.coins); + } } #ifdef VERSION_JP @@ -286,6 +370,8 @@ void render_hud_coins(void) { #define HUD_STARS_X 78 #endif +#define HUD_STARS_X_NEW 76 + /** * Renders the amount of stars collected. * Disables "X" glyph when Mario has 100 stars or more. @@ -296,17 +382,51 @@ void render_hud_stars(void) { if (gHudFlash == 1 && gGlobalTimer & 0x08) { return; } - - if (gHudDisplay.stars < 100) { - showX = 1; + + if (gNewHud == 2) { + if (gHudDisplay.stars < 10) { + print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW, "-"); // 'Star' glyph + print_text(get_left(HUD_LEFT_X) + 17, HUD_TOP_Y_NEW, "*"); // 'X' glyph + print_text_fmt_int(get_left(HUD_LEFT_X) + 33, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars); + } + else if (gHudDisplay.stars < 100) { + print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW, "-"); // 'Star' glyph + print_text(get_left(HUD_LEFT_X) + 15, HUD_TOP_Y_NEW, "*"); // 'X' glyph + print_text_fmt_int(get_left(HUD_LEFT_X) + 31, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars); + } + else { + print_text(get_left(HUD_LEFT_X), HUD_TOP_Y_NEW, "-"); // 'Star' glyph + print_text_fmt_int(get_left(HUD_LEFT_X) + 15, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars); + } } - - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X), HUD_TOP_Y, "-"); // 'Star' glyph - if (showX == 1) { - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph + else if (gNewHud == 1) { + if (gHudDisplay.stars < 10) { + print_text(get_right(HUD_STARS_X_NEW) + 6, HUD_TOP_Y_NEW, "-"); // 'Star' glyph + print_text(get_right(HUD_STARS_X_NEW) + 23, HUD_TOP_Y_NEW, "*"); // 'X' glyph + print_text_fmt_int(get_right(HUD_STARS_X_NEW) + 39, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars); + } + else if (gHudDisplay.stars < 100) { + print_text(get_right(HUD_STARS_X_NEW) - 4, HUD_TOP_Y_NEW, "-"); // 'Star' glyph + print_text(get_right(HUD_STARS_X_NEW) + 11, HUD_TOP_Y_NEW, "*"); // 'X' glyph + print_text_fmt_int(get_right(HUD_STARS_X_NEW) + 27, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars); + } + else { + print_text(get_right(HUD_STARS_X_NEW), HUD_TOP_Y_NEW, "-"); // 'Star' glyph + print_text_fmt_int(get_right(HUD_STARS_X_NEW) + 15, HUD_TOP_Y_NEW, "%d", gHudDisplay.stars); + } + } + else { + if (gHudDisplay.stars < 100) { + showX = 1; + } + + print_text(get_right(HUD_STARS_X), HUD_TOP_Y, "-"); // 'Star' glyph + if (showX == 1) { + print_text(get_right(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph + } + print_text_fmt_int((showX * 14) + get_right(HUD_STARS_X - 16), + HUD_TOP_Y, "%d", gHudDisplay.stars); } - print_text_fmt_int((showX * 14) + GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X - 16), - HUD_TOP_Y, "%d", gHudDisplay.stars); } /** @@ -336,13 +456,13 @@ void render_hud_timer(void) { #ifdef VERSION_EU switch (eu_get_language()) { case LANGUAGE_ENGLISH: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); + print_text(get_right(150), 185, "TIME"); break; case LANGUAGE_FRENCH: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(155), 185, "TEMPS"); + print_text(get_right(155), 185, "TEMPS"); break; case LANGUAGE_GERMAN: - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "ZEIT"); + print_text(get_right(150), 185, "ZEIT"); break; } #endif @@ -350,15 +470,32 @@ void render_hud_timer(void) { timerSecs = (timerValFrames - (timerMins * 1800)) / 30; timerFracSecs = ((timerValFrames - (timerMins * 1800) - (timerSecs * 30)) & 0xFFFF) / 3; + + if (gNewHud != 0) { + s32 add; + if (gNewHud == 2) + add = 157; + else + add = 0; + print_text(get_right(151), HUD_TOP_Y_NEW-18-add, "TIME"); + print_text_fmt_int(get_right(92), HUD_TOP_Y_NEW-18-add, "%0d", timerMins); + print_text_fmt_int(get_right(72), HUD_TOP_Y_NEW-18-add, "%02d", timerSecs); + print_text_fmt_int(get_right(38), HUD_TOP_Y_NEW-18-add, "%d", timerFracSecs); + gSPDisplayList(gDisplayListHead++, dl_hud_img_begin); + render_hud_tex_lut(get_right(82), 23+add, (*hudLUT)[GLYPH_APOSTROPHE]); + render_hud_tex_lut(get_right(47), 23+add, (*hudLUT)[GLYPH_DOUBLE_QUOTE]); + } + else { #ifndef VERSION_EU - print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(150), 185, "TIME"); + print_text(get_right(150), 185, "TIME"); #endif - print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(91), 185, "%0d", timerMins); - print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(71), 185, "%02d", timerSecs); - print_text_fmt_int(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(37), 185, "%d", timerFracSecs); - gSPDisplayList(gDisplayListHead++, dl_hud_img_begin); - render_hud_tex_lut(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(81), 32, (*hudLUT)[GLYPH_APOSTROPHE]); - render_hud_tex_lut(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(46), 32, (*hudLUT)[GLYPH_DOUBLE_QUOTE]); + print_text_fmt_int(get_right(91), 185, "%0d", timerMins); + print_text_fmt_int(get_right(71), 185, "%02d", timerSecs); + print_text_fmt_int(get_right(37), 185, "%d", timerFracSecs); + gSPDisplayList(gDisplayListHead++, dl_hud_img_begin); + render_hud_tex_lut(get_right(81), 32, (*hudLUT)[GLYPH_APOSTROPHE]); + render_hud_tex_lut(get_right(46), 32, (*hudLUT)[GLYPH_DOUBLE_QUOTE]); + } gSPDisplayList(gDisplayListHead++, dl_hud_img_end); } @@ -380,8 +517,14 @@ void render_hud_camera_status(void) { s32 y; cameraLUT = segmented_to_virtual(&main_hud_camera_lut); - x = GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(54); - y = 205; + if (gNewHud == 1) { + x = get_right(53); + y = 208; + } + else { + x = get_right(54); + y = 205; + } if (sCameraHUD.status == CAM_STATUS_NONE) { return; @@ -414,6 +557,13 @@ void render_hud_camera_status(void) { gSPDisplayList(gDisplayListHead++, dl_hud_img_end); } +// The sides for the 4:3 mode +void render_sides(void) { + gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(0, 0, 0, 1)); + gDPFillRectangle(gDisplayListHead++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), 0, 0, SCREEN_HEIGHT); + gDPFillRectangle(gDisplayListHead++, SCREEN_WIDTH, 0, GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0), SCREEN_HEIGHT); +} + /** * Render HUD strings using hudDisplayFlags with it's render functions, * excluding the cannon reticle which detects a camera preset for it. @@ -451,7 +601,7 @@ void render_hud(void) { render_hud_cannon_reticle(); } - if (hudDisplayFlags & HUD_DISPLAY_FLAG_LIVES) { + if (hudDisplayFlags & HUD_DISPLAY_FLAG_LIVES && !(save_file_get_flags() & SAVE_FLAG_HARDCORE_MODE)) { render_hud_mario_lives(); } @@ -475,5 +625,9 @@ void render_hud(void) { if (hudDisplayFlags & HUD_DISPLAY_FLAG_TIMER) { render_hud_timer(); } + + if (gDrawPillarbox) { + render_sides(); + } } } diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index b9a43df..3b45733 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -23,6 +23,8 @@ #include "text_strings.h" #include "types.h" +#include "settings.h" + u16 gDialogColorFadeTimer; s8 gLastDialogLineNum; s32 gDialogVariable; @@ -2251,7 +2253,12 @@ void render_pause_my_score_coins(void) { gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); if (courseIndex < COURSE_STAGES_COUNT && save_file_get_course_star_count(gCurrSaveFileNum - 1, courseIndex) != 0) { - print_generic_string(MYSCORE_X, 121, textMyScore); + if (get_mirror()) { + print_generic_string(280-MYSCORE_X, 121, textMyScore); + } + else { + print_generic_string(MYSCORE_X, 121, textMyScore); + } } courseName = segmented_to_virtual(courseNameTbl[courseIndex]); @@ -2628,7 +2635,7 @@ s16 render_pause_courses_and_castle(void) { render_pause_my_score_coins(); render_pause_red_coins(); - if (gMarioStates[0].action & ACT_FLAG_PAUSE_EXIT) { + if (gLeaveAnyTime || gMarioStates[0].action & ACT_FLAG_PAUSE_EXIT) { render_pause_course_options(99, 93, &gDialogLineNum, 15); } @@ -2767,7 +2774,9 @@ void print_hud_course_complete_coins(s16 x, s16 y) { if (gCourseCompleteCoins == 50 || gCourseCompleteCoins == 100 || gCourseCompleteCoins == 150) { play_sound(SOUND_GENERAL_COLLECT_1UP, gDefaultSoundArgs); - gMarioState[0].numLives++; + if (!gLifeMode) { + gMarioState[0].numLives++; + } } } diff --git a/src/game/interaction.c b/src/game/interaction.c index d27b488..5575d51 100644 --- a/src/game/interaction.c +++ b/src/game/interaction.c @@ -24,6 +24,8 @@ #include "sound_init.h" #include "thread6.h" +#include "settings.h" + #define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01 #define INT_PUNCH (1 << 1) // 0x02 #define INT_KICK (1 << 2) // 0x04 @@ -769,6 +771,12 @@ u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct O u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0; u32 grandStar = (o->oInteractionSubtype & INT_SUBTYPE_GRAND_STAR) != 0; + // Don't kick Mario if staying in levels is active + if (gDontKick && gCurrLevelNum != LEVEL_BOWSER_1 && gCurrLevelNum != LEVEL_BOWSER_2 + && gCurrLevelNum != LEVEL_CASTLE && gCurrLevelNum != LEVEL_CASTLE_COURTYARD && gCurrLevelNum != LEVEL_CASTLE_GROUNDS) { + noExit = 1; + } + if (m->health >= 0x100) { mario_stop_riding_and_holding(m); #ifdef VERSION_SH @@ -1018,7 +1026,7 @@ u32 interact_door(struct MarioState *m, UNUSED u32 interactType, struct Object * enterDoorAction = ACT_ENTERING_STAR_DOOR; } - if (doorSaveFileFlag != 0 && !(save_file_get_flags() & doorSaveFileFlag)) { + if (!gSkipCutscenes && doorSaveFileFlag != 0 && !(save_file_get_flags() & doorSaveFileFlag)) { enterDoorAction = ACT_UNLOCKING_STAR_DOOR; } diff --git a/src/game/level_update.c b/src/game/level_update.c index 5ecc48d..3183a7a 100644 --- a/src/game/level_update.c +++ b/src/game/level_update.c @@ -29,6 +29,8 @@ #include "course_table.h" #include "thread6.h" +#include "settings.h" + #define PLAY_MODE_NORMAL 0 #define PLAY_MODE_PAUSED 2 #define PLAY_MODE_CHANGE_AREA 3 @@ -726,7 +728,11 @@ s16 level_trigger_warp(struct MarioState *m, s32 warpOp) { break; case WARP_OP_DEATH: - if (m->numLives == 0) { + if (save_file_get_flags() & SAVE_FLAG_HARDCORE_MODE) { + sDelayedWarpOp = WARP_OP_GAME_OVER; + save_file_erase(gCurrSaveFileNum - 1); + } + if (m->numLives == 0 && gLifeMode == 0) { sDelayedWarpOp = WARP_OP_GAME_OVER; } sDelayedWarpTimer = 48; @@ -738,9 +744,14 @@ s16 level_trigger_warp(struct MarioState *m, s32 warpOp) { case WARP_OP_WARP_FLOOR: sSourceWarpNodeId = WARP_NODE_WARP_FLOOR; if (area_get_warp_node(sSourceWarpNodeId) == NULL) { - if (m->numLives == 0) { + if (save_file_get_flags() & SAVE_FLAG_HARDCORE_MODE) { sDelayedWarpOp = WARP_OP_GAME_OVER; - } else { + save_file_erase(gCurrSaveFileNum - 1); + } + else if (m->numLives == 0 && gLifeMode == 0) { + sDelayedWarpOp = WARP_OP_GAME_OVER; + } + else { sSourceWarpNodeId = WARP_NODE_DEATH; } } @@ -865,9 +876,26 @@ void initiate_delayed_warp(void) { destWarpNode, 0); break; - default: + case WARP_OP_STAR_EXIT: warpNode = area_get_warp_node(sSourceWarpNodeId); + initiate_warp(warpNode->node.destLevel & 0x7F, warpNode->node.destArea, + warpNode->node.destNode, sDelayedWarpArg); + + check_if_should_set_warp_checkpoint(&warpNode->node); + if (sWarpDest.type != WARP_TYPE_CHANGE_LEVEL) { + level_set_transition(2, NULL); + } + break; + + default: + if (gRemoveAnnoyingWarps && (sDelayedWarpOp & WARP_OP_TRIGGERS_LEVEL_SELECT)) { + warpNode = area_get_warp_node(WARP_NODE_DEATH); + } + else { + warpNode = area_get_warp_node(sSourceWarpNodeId); + } + initiate_warp(warpNode->node.destLevel & 0x7F, warpNode->node.destArea, warpNode->node.destNode, sDelayedWarpArg); @@ -1193,6 +1221,23 @@ s32 init_level(void) { } else { set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); val4 = 1; + if (gPlayer1Controller->buttonDown & U_CBUTTONS && gPlayer1Controller->buttonDown & R_TRIG) { + level_trigger_warp(gMarioState, WARP_OP_CREDITS_START); + } + else if (gSkipCutscenes) { + set_mario_action(gMarioState, ACT_IDLE, 0); + } else { + set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); + val4 = 1; + } + } + if (gHardSave) { + gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); + save_file_set_flags(SAVE_FLAG_HARD_MODE); + } + if (gHardcoreSave) { + gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); + save_file_set_flags(SAVE_FLAG_HARDCORE_MODE); } } } @@ -1318,3 +1363,10 @@ s32 lvl_play_the_end_screen_sound(UNUSED s16 arg0, UNUSED s32 arg1) { play_sound(SOUND_MENU_THANK_YOU_PLAYING_MY_GAME, gDefaultSoundArgs); return 1; } + +s32 credits_wait_for_reset() { + if (gPlayer1Controller->buttonPressed & START_BUTTON || gPlayer1Controller->buttonPressed & A_BUTTON) { + return 1; + } + return 0; +} \ No newline at end of file diff --git a/src/game/level_update.h b/src/game/level_update.h index ecf9c3a..86b746c 100644 --- a/src/game/level_update.h +++ b/src/game/level_update.h @@ -127,6 +127,7 @@ s32 lvl_init_or_update(s16 initOrUpdate, UNUSED s32 unused); s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum); s32 lvl_set_current_level(UNUSED s16 arg0, s32 levelNum); s32 lvl_play_the_end_screen_sound(UNUSED s16 arg0, UNUSED s32 arg1); +s32 credits_wait_for_reset(); void basic_update(UNUSED s16 *arg); #endif // LEVEL_UPDATE_H diff --git a/src/game/mario.c b/src/game/mario.c index ecb4613..888b542 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -34,6 +34,8 @@ #include "sound_init.h" #include "thread6.h" +#include "settings.h" + u32 unused80339F10; s8 filler80339F1C[20]; @@ -810,7 +812,12 @@ static u32 set_mario_action_airborne(struct MarioState *m, u32 action, u32 actio case ACT_WATER_JUMP: case ACT_HOLD_WATER_JUMP: if (actionArg == 0) { - set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.0f); + if (gBetterControls) { + set_mario_y_vel_based_on_fspeed(m, 48.0f, 0.0f); + } + else { + set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.0f); + } } break; @@ -831,6 +838,19 @@ static u32 set_mario_action_airborne(struct MarioState *m, u32 action, u32 actio break; case ACT_WALL_KICK_AIR: + set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); + if (gBetterControls) { + if (m->forwardVel < 28.0f) { + m->forwardVel = 28.0f; + } + } + else { + if (m->forwardVel < 24.0f) { + m->forwardVel = 24.0f; + } + } + m->wallKickTimer = 0; + break; case ACT_TOP_OF_POLE_JUMP: set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); if (m->forwardVel < 24.0f) { @@ -878,15 +898,28 @@ static u32 set_mario_action_airborne(struct MarioState *m, u32 action, u32 actio break; case ACT_SLIDE_KICK: - m->vel[1] = 12.0f; - if (m->forwardVel < 32.0f) { - m->forwardVel = 32.0f; + if (gBetterControls) { + m->vel[1] = 14.0f; + if (m->forwardVel < 36.0f) { + m->forwardVel = 36.0f; + } + } + else { + m->vel[1] = 12.0f; + if (m->forwardVel < 32.0f) { + m->forwardVel = 32.0f; + } } break; case ACT_JUMP_KICK: m->vel[1] = 20.0f; break; + + case ACT_WALL_SLIDE: + m->vel[1] = 4.0f; + mario_set_forward_vel(m, 8.0f); + break; } m->peakHeight = m->pos[1]; @@ -1473,7 +1506,9 @@ void update_mario_health(struct MarioState *m) { // when in snow terrains lose 3 health. // If using the debug level select, do not lose any HP to water. if ((m->pos[1] >= (m->waterLevel - 140)) && !terrainIsSnow) { - m->health += 0x1A; + if (!save_file_get_flags() & SAVE_FLAG_HARD_MODE) { + m->health += 0x1A; + } } else if (gDebugLevelSelect == 0) { m->health -= (terrainIsSnow ? 3 : 1); } @@ -1808,7 +1843,7 @@ void init_mario(void) { if (save_file_get_flags() & (SAVE_FLAG_CAP_ON_GROUND | SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI - | SAVE_FLAG_CAP_ON_MR_BLIZZARD)) { + | SAVE_FLAG_CAP_ON_MR_BLIZZARD | SAVE_FLAG_HARD_MODE)) { gMarioState->flags = 0; } else { gMarioState->flags = (MARIO_CAP_ON_HEAD | MARIO_NORMAL_CAP); @@ -1892,7 +1927,12 @@ 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; + if (gLifeMode) { + gMarioState->numLives = 0; + } + else { + gMarioState->numLives = 4; + } gMarioState->health = 0x880; gMarioState->prevNumStarsForDialog = gMarioState->numStars; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index a9f0089..c7f52c3 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -15,6 +15,8 @@ #include "save_file.h" #include "thread6.h" +#include "settings.h" + void play_flip_sounds(struct MarioState *m, s16 frame1, s16 frame2, s16 frame3) { s32 animFrame = m->marioObj->header.gfx.unk38.animFrame; if (animFrame == frame1 || animFrame == frame2 || animFrame == frame3) { @@ -80,7 +82,9 @@ s32 check_fall_damage(struct MarioState *m, u32 hardFallAction) { if (m->action != ACT_TWIRLING && m->floor->type != SURFACE_BURNING) { if (m->vel[1] < -55.0f) { if (fallHeight > 3000.0f) { - m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 16 : 24; + if (!gDisableFallDamage) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 16 : 24; + } #ifdef VERSION_SH queue_rumble_data(5, 80); #endif @@ -88,7 +92,9 @@ s32 check_fall_damage(struct MarioState *m, u32 hardFallAction) { play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); return drop_and_set_mario_action(m, hardFallAction, 4); } else if (fallHeight > damageHeight && !mario_floor_is_slippery(m)) { - m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 8 : 12; + if (!gDisableFallDamage) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 8 : 12; + } m->squishTimer = 30; #ifdef VERSION_SH queue_rumble_data(5, 80); @@ -104,7 +110,15 @@ s32 check_fall_damage(struct MarioState *m, u32 hardFallAction) { s32 check_kick_or_dive_in_air(struct MarioState *m) { if (m->input & INPUT_B_PRESSED) { - return set_mario_action(m, m->forwardVel > 28.0f ? ACT_DIVE : ACT_JUMP_KICK, 0); + if (gBetterControls) { + if (m->forwardVel >= 28.0f && m->controller->stickMag > 48.0f) { + return set_mario_action(m, ACT_DIVE, 1); + } + return set_mario_action(m, ACT_JUMP_KICK, 0); + } + else { + return set_mario_action(m, m->forwardVel > 28.0f ? ACT_DIVE : ACT_JUMP_KICK, 0); + } } return FALSE; } @@ -193,8 +207,20 @@ void update_air_with_turn(struct MarioState *m) { intendedDYaw = m->intendedYaw - m->faceAngle[1]; intendedMag = m->intendedMag / 32.0f; - m->forwardVel += 1.5f * coss(intendedDYaw) * intendedMag; - m->faceAngle[1] += 512.0f * sins(intendedDYaw) * intendedMag; + if (gBetterControls) { + if ((m->forwardVel > 0 && intendedMag * coss(intendedDYaw) > 0) || (m->forwardVel < 0 && intendedMag * coss(intendedDYaw) < 0)) { + if (m->action != ACT_WALL_KICK_AIR) + m->forwardVel += intendedMag * coss(intendedDYaw) * 1.5f; + } + else { + m->forwardVel += intendedMag * coss(intendedDYaw) * 3.5f; + } + m->faceAngle[1] += 1024.0f * sins(intendedDYaw) * intendedMag; + } + else { + m->forwardVel += 1.5f * coss(intendedDYaw) * intendedMag; + m->faceAngle[1] += 512.0f * sins(intendedDYaw) * intendedMag; + } } //! Uncapped air speed. Net positive when moving forward. @@ -210,7 +236,7 @@ void update_air_with_turn(struct MarioState *m) { } } -void update_air_without_turn(struct MarioState *m) { +void update_air_without_turn(struct MarioState *m, u32 canTurn) { f32 sidewaysSpeed = 0.0f; f32 dragThreshold; s16 intendedDYaw; @@ -224,8 +250,38 @@ void update_air_without_turn(struct MarioState *m) { intendedDYaw = m->intendedYaw - m->faceAngle[1]; intendedMag = m->intendedMag / 32.0f; - m->forwardVel += intendedMag * coss(intendedDYaw) * 1.5f; - sidewaysSpeed = intendedMag * sins(intendedDYaw) * 10.0f; + if (gBetterControls) { + if ((m->forwardVel > 0 && intendedMag * coss(intendedDYaw) > 0) || (m->forwardVel < 0 && intendedMag * coss(intendedDYaw) < 0)) { + if (m->action != ACT_WALL_KICK_AIR) + m->forwardVel += intendedMag * coss(intendedDYaw) * 1.5f; + } + else { + m->forwardVel += intendedMag * coss(intendedDYaw) * 3.5f; + } + if (gAirTurn && canTurn) { + sidewaysSpeed = intendedMag * sins(intendedDYaw) * 7.5f; + m->faceAngle[1] += 768.0f * sins(intendedDYaw) * intendedMag; + } + else if (canTurn) { + sidewaysSpeed = intendedMag * sins(intendedDYaw) * 15.0f; + m->faceAngle[1] += 320.0f * sins(intendedDYaw) * intendedMag; + } + else { + sidewaysSpeed = intendedMag * sins(intendedDYaw) * 15.0f; + m->faceAngle[1] += 64.0f * sins(intendedDYaw) * intendedMag; + } + + } + else { + m->forwardVel += intendedMag * coss(intendedDYaw) * 1.5f; + if (gAirTurn && canTurn) { + sidewaysSpeed = intendedMag * sins(intendedDYaw) * 5.0f; + m->faceAngle[1] += 768.0f * sins(intendedDYaw) * intendedMag; + } + else { + sidewaysSpeed = intendedMag * sins(intendedDYaw) * 10.0f; + } + } } //! Uncapped air speed. Net positive when moving forward. @@ -368,7 +424,7 @@ void update_flying(struct MarioState *m) { u32 common_air_action_step(struct MarioState *m, u32 landAction, s32 animation, u32 stepArg) { u32 stepResult; - update_air_without_turn(m); + update_air_without_turn(m, 1); stepResult = perform_air_step(m, stepArg); switch (stepResult) { @@ -441,6 +497,9 @@ u32 common_air_action_step(struct MarioState *m, u32 landAction, s32 animation, } s32 act_jump(struct MarioState *m) { + if (gDebugMovementMode) { + set_mario_action(m, ACT_DEBUG_FREE_MOVE, 0); + } if (check_kick_or_dive_in_air(m)) { return TRUE; } @@ -654,7 +713,7 @@ s32 act_riding_shell_air(struct MarioState *m) { play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); set_mario_animation(m, MARIO_ANIM_JUMP_RIDING_SHELL); - update_air_without_turn(m); + update_air_without_turn(m, 1); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -732,7 +791,7 @@ s32 act_dive(struct MarioState *m) { } } - update_air_without_turn(m); + update_air_without_turn(m, 0); switch (perform_air_step(m, 0)) { case AIR_STEP_NONE: @@ -794,7 +853,7 @@ s32 act_air_throw(struct MarioState *m) { play_sound_if_no_flag(m, SOUND_MARIO_WAH2, MARIO_MARIO_SOUND_PLAYED); set_mario_animation(m, MARIO_ANIM_THROW_LIGHT_OBJECT); - update_air_without_turn(m); + update_air_without_turn(m, 1); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -816,8 +875,15 @@ s32 act_air_throw(struct MarioState *m) { } s32 act_water_jump(struct MarioState *m) { - if (m->forwardVel < 15.0f) { - mario_set_forward_vel(m, 15.0f); + if (gBetterControls) { + if (m->forwardVel < 20.0f) { + mario_set_forward_vel(m, 20.0f); + } + } + else { + if (m->forwardVel < 15.0f) { + mario_set_forward_vel(m, 15.0f); + } } play_mario_sound(m, SOUND_ACTION_UNKNOWN432, 0); @@ -830,7 +896,12 @@ s32 act_water_jump(struct MarioState *m) { break; case AIR_STEP_HIT_WALL: - mario_set_forward_vel(m, 15.0f); + if (gBetterControls) { + mario_set_forward_vel(m, 20.0f); + } + else { + mario_set_forward_vel(m, 15.0f); + } break; case AIR_STEP_GRABBED_LEDGE: @@ -854,8 +925,15 @@ s32 act_hold_water_jump(struct MarioState *m) { return drop_and_set_mario_action(m, ACT_FREEFALL, 0); } - if (m->forwardVel < 15.0f) { - mario_set_forward_vel(m, 15.0f); + if (gBetterControls) { + if (m->forwardVel < 20.0f) { + mario_set_forward_vel(m, 20.0f); + } + } + else { + if (m->forwardVel < 15.0f) { + mario_set_forward_vel(m, 15.0f); + } } play_mario_sound(m, SOUND_ACTION_UNKNOWN432, 0); @@ -868,7 +946,12 @@ s32 act_hold_water_jump(struct MarioState *m) { break; case AIR_STEP_HIT_WALL: - mario_set_forward_vel(m, 15.0f); + if (gBetterControls) { + mario_set_forward_vel(m, 20.0f); + } + else { + mario_set_forward_vel(m, 15.0f); + } break; case AIR_STEP_HIT_LAVA_WALL: @@ -912,6 +995,7 @@ s32 act_steep_jump(struct MarioState *m) { s32 act_ground_pound(struct MarioState *m) { u32 stepResult; f32 yOffset; + u8 lagTime; play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED); @@ -925,7 +1009,13 @@ s32 act_ground_pound(struct MarioState *m) { } } - m->vel[1] = -50.0f; + else if (gBetterControls) { + m->vel[1] = -54.0f; + } + else { + m->vel[1] = -50.0f; + } + mario_set_forward_vel(m, 0.0f); set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_START_GROUND_POUND @@ -935,10 +1025,22 @@ s32 act_ground_pound(struct MarioState *m) { } m->actionTimer++; - if (m->actionTimer >= m->marioObj->header.gfx.unk38.curAnim->unk08 + 4) { + if (gBetterControls) { + lagTime = 2; + } + else { + lagTime = 4; + } + if (m->actionTimer >= m->marioObj->header.gfx.unk38.curAnim->unk08 + lagTime) { play_sound(SOUND_MARIO_GROUND_POUND_WAH, m->marioObj->header.gfx.cameraToObject); m->actionState = 1; } + + if (gOdysseyDive && m->input & INPUT_B_PRESSED) { + set_mario_action(m, ACT_DIVE, 0); + mario_set_forward_vel(m, 24.0f); + m->vel[1] = 28; + } } else { set_mario_animation(m, MARIO_ANIM_GROUND_POUND); @@ -1058,7 +1160,7 @@ s32 act_crazy_box_bounce(struct MarioState *m) { play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); set_mario_animation(m, MARIO_ANIM_DIVE); - update_air_without_turn(m); + update_air_without_turn(m, 0); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -1141,12 +1243,45 @@ u32 common_air_knockback_step(struct MarioState *m, u32 landAction, u32 hardFall s32 check_wall_kick(struct MarioState *m) { if ((m->input & INPUT_A_PRESSED) && m->wallKickTimer != 0 && m->prevAction == ACT_AIR_HIT_WALL) { m->faceAngle[1] += 0x8000; + if (gBetterControls) { + m->particleFlags |= PARTICLE_VERTICAL_STAR; + } return set_mario_action(m, ACT_WALL_KICK_AIR, 0); } return FALSE; } +s32 act_wall_slide(struct MarioState *m) { + m->marioObj->header.gfx.angle[1] = m->faceAngle[1]; + if (m->input & INPUT_A_PRESSED) { + m->faceAngle[1] += 0x8000; + m->particleFlags |= PARTICLE_VERTICAL_STAR; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + set_mario_action(m, ACT_FREEFALL, 0); + break; + + case AIR_STEP_LANDED: + set_mario_action(m, ACT_JUMP_LAND_STOP, 0); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + if (m->vel[1] < -20.0f) { + m->vel[1] = -20.0f; + } + mario_set_forward_vel(m, 1.0f); + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_DUST; + m->marioObj->header.gfx.angle[1] = m->faceAngle[1]+0x8000; + return FALSE; +} + s32 act_backward_air_kb(struct MarioState *m) { if (check_wall_kick(m)) { return 1; @@ -1304,31 +1439,49 @@ s32 act_air_hit_wall(struct MarioState *m) { if (m->heldObj != NULL) { mario_drop_held_object(m); } + if (gModernWallJump) { + if (++(m->actionTimer) <= 2) { + if (m->input & INPUT_A_PRESSED) { + m->vel[1] = 52.0f; + m->faceAngle[1] += 0x8000; + m->particleFlags |= PARTICLE_VERTICAL_STAR; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + } + else { + m->wallKickTimer = 5; + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + return set_mario_action(m, ACT_WALL_SLIDE, 0); + } + } + else { + if (++(m->actionTimer) <= 2) { + if (m->input & INPUT_A_PRESSED) { + m->vel[1] = 52.0f; + m->faceAngle[1] += 0x8000; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + } else if (m->forwardVel >= 38.0f) { + m->wallKickTimer = 5; + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } - if (++(m->actionTimer) <= 2) { - if (m->input & INPUT_A_PRESSED) { - m->vel[1] = 52.0f; - m->faceAngle[1] += 0x8000; - return set_mario_action(m, ACT_WALL_KICK_AIR, 0); - } - } else if (m->forwardVel >= 38.0f) { - m->wallKickTimer = 5; - if (m->vel[1] > 0.0f) { - m->vel[1] = 0.0f; - } + m->particleFlags |= PARTICLE_VERTICAL_STAR; + return set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + } else { + m->wallKickTimer = 5; + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } - m->particleFlags |= PARTICLE_VERTICAL_STAR; - return set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); - } else { - m->wallKickTimer = 5; - if (m->vel[1] > 0.0f) { - m->vel[1] = 0.0f; + if (m->forwardVel > 8.0f) { + mario_set_forward_vel(m, -8.0f); + } + return set_mario_action(m, ACT_SOFT_BONK, 0); } - - if (m->forwardVel > 8.0f) { - mario_set_forward_vel(m, -8.0f); - } - return set_mario_action(m, ACT_SOFT_BONK, 0); } #ifdef AVOID_UB @@ -1353,7 +1506,7 @@ s32 act_forward_rollout(struct MarioState *m) { play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); - update_air_without_turn(m); + update_air_without_turn(m, 0); switch (perform_air_step(m, 0)) { case AIR_STEP_NONE: @@ -1394,7 +1547,7 @@ s32 act_backward_rollout(struct MarioState *m) { play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); - update_air_without_turn(m); + update_air_without_turn(m, 0); switch (perform_air_step(m, 0)) { case AIR_STEP_NONE: @@ -1582,7 +1735,7 @@ s32 act_slide_kick(struct MarioState *m) { return set_mario_action(m, ACT_FREEFALL, 2); } - update_air_without_turn(m); + update_air_without_turn(m, 0); switch (perform_air_step(m, 0)) { case AIR_STEP_NONE: @@ -1641,7 +1794,7 @@ s32 act_jump_kick(struct MarioState *m) { m->flags |= MARIO_KICKING; } - update_air_without_turn(m); + update_air_without_turn(m, 1); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -1933,7 +2086,7 @@ s32 act_flying_triple_jump(struct MarioState *m) { set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); } - update_air_without_turn(m); + update_air_without_turn(m, 1); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -1981,7 +2134,7 @@ s32 act_vertical_wind(struct MarioState *m) { set_mario_animation(m, MARIO_ANIM_AIRBORNE_ON_STOMACH); } - update_air_without_turn(m); + update_air_without_turn(m, 1); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -2009,7 +2162,7 @@ s32 act_special_triple_jump(struct MarioState *m) { play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAHOO); - update_air_without_turn(m); + update_air_without_turn(m, 1); switch (perform_air_step(m, 0)) { case AIR_STEP_LANDED: @@ -2111,6 +2264,7 @@ s32 mario_execute_airborne_action(struct MarioState *m) { case ACT_RIDING_HOOT: cancel = act_riding_hoot(m); break; case ACT_TOP_OF_POLE_JUMP: cancel = act_top_of_pole_jump(m); break; case ACT_VERTICAL_WIND: cancel = act_vertical_wind(m); break; + case ACT_WALL_SLIDE: cancel = act_wall_slide(m); break; } /* clang-format on */ diff --git a/src/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c index ee6720b..f32ce92 100644 --- a/src/game/mario_actions_automatic.c +++ b/src/game/mario_actions_automatic.c @@ -17,6 +17,8 @@ #include "level_table.h" #include "thread6.h" +#include "settings.h" + #define POLE_NONE 0 #define POLE_TOUCHED_FLOOR 1 #define POLE_FELL_OFF 2 @@ -711,8 +713,14 @@ s32 act_in_cannon(struct MarioState *m) { break; case 2: - m->faceAngle[0] -= (s16)(m->controller->stickY * 10.0f); - marioObj->oMarioCannonInputYaw -= (s16)(m->controller->stickX * 10.0f); + if (gBetterControls) { + m->faceAngle[0] -= (s16)(m->controller->stickY * 6.0f); + marioObj->oMarioCannonInputYaw -= (s16)(m->controller->stickX * 6.0f); + } + else { + m->faceAngle[0] -= (s16)(m->controller->stickY * 10.0f); + marioObj->oMarioCannonInputYaw -= (s16)(m->controller->stickX * 10.0f); + } if (m->faceAngle[0] > 0x38E3) { m->faceAngle[0] = 0x38E3; @@ -721,11 +729,21 @@ s32 act_in_cannon(struct MarioState *m) { m->faceAngle[0] = 0; } - if (marioObj->oMarioCannonInputYaw > 0x4000) { - marioObj->oMarioCannonInputYaw = 0x4000; + if (gFlexibleCannons) { + if (marioObj->oMarioCannonInputYaw > 0x8000) { + marioObj->oMarioCannonInputYaw = 0x8000; + } + if (marioObj->oMarioCannonInputYaw < -0x8000) { + marioObj->oMarioCannonInputYaw = -0x8000; + } } - if (marioObj->oMarioCannonInputYaw < -0x4000) { - marioObj->oMarioCannonInputYaw = -0x4000; + else { + if (marioObj->oMarioCannonInputYaw > 0x4000) { + marioObj->oMarioCannonInputYaw = 0x4000; + } + if (marioObj->oMarioCannonInputYaw < -0x4000) { + marioObj->oMarioCannonInputYaw = -0x4000; + } } m->faceAngle[1] = marioObj->oMarioCannonObjectYaw + marioObj->oMarioCannonInputYaw; diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index 3817fd1..8548a10 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -29,6 +29,8 @@ #include "sound_init.h" #include "thread6.h" +#include "settings.h" + // TODO: put this elsewhere enum SaveOption { SAVE_OPT_SAVE_AND_CONTINUE = 1, SAVE_OPT_SAVE_AND_QUIT, SAVE_OPT_CONTINUE_DONT_SAVE }; @@ -618,9 +620,20 @@ void general_star_dance_handler(struct MarioState *m, s32 isInWater) { if ((m->actionArg & 1) == 0) { level_trigger_warp(m, WARP_OP_STAR_EXIT); } else { - enable_time_stop(); - create_dialog_box_with_response(gLastCompletedStarNum == 7 ? DIALOG_013 : DIALOG_014); - m->actionState = 1; + if (gDontKick == 2) { + save_file_do_save(gCurrSaveFileNum - 1); + m->actionState = 2; + } + else { + enable_time_stop(); + if (gDontKick) { + create_dialog_box_with_response(DIALOG_170); + } + else { + create_dialog_box_with_response(gLastCompletedStarNum == 7 ? DIALOG_013 : DIALOG_014); + } + m->actionState = 1; + } } break; } @@ -628,6 +641,9 @@ void general_star_dance_handler(struct MarioState *m, s32 isInWater) { if (gDialogResponse == 1) { save_file_do_save(gCurrSaveFileNum - 1); } + else if (gDontKick && gLastCompletedStarNum < 7) { + level_trigger_warp(m, WARP_OP_STAR_EXIT); + } m->actionState = 2; } else if (m->actionState == 2 && is_anim_at_end(m)) { disable_time_stop(); @@ -966,7 +982,7 @@ s32 act_warp_door_spawn(struct MarioState *m) { m->usedObj->oInteractStatus = 0x00080000; } } else if (m->usedObj->oAction == 0) { - if (gShouldNotPlayCastleMusic == TRUE && gCurrLevelNum == LEVEL_CASTLE) { + if (!gSkipCutscenes && gShouldNotPlayCastleMusic == TRUE && gCurrLevelNum == LEVEL_CASTLE) { set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_021); } else { set_mario_action(m, ACT_IDLE, 0); @@ -1173,7 +1189,12 @@ s32 act_death_exit(struct MarioState *m) { #ifdef VERSION_SH queue_rumble_data(5, 80); #endif - m->numLives--; + if (gLifeMode) { + m->numLives++; + } + else if (m->numLives > 0) { + m->numLives--; + } // restore 7.75 units of health m->healCounter = 31; } @@ -1189,7 +1210,12 @@ s32 act_unused_death_exit(struct MarioState *m) { #else play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); #endif - m->numLives--; + if (gLifeMode) { + m->numLives++; + } + else if (m->numLives > 0) { + m->numLives--; + } // restore 7.75 units of health m->healCounter = 31; } @@ -1208,7 +1234,12 @@ s32 act_falling_death_exit(struct MarioState *m) { #ifdef VERSION_SH queue_rumble_data(5, 80); #endif - m->numLives--; + if (gLifeMode) { + m->numLives++; + } + else if (m->numLives > 0) { + m->numLives--; + } // restore 7.75 units of health m->healCounter = 31; } @@ -1255,7 +1286,12 @@ s32 act_special_death_exit(struct MarioState *m) { #ifdef VERSION_SH queue_rumble_data(5, 80); #endif - m->numLives--; + if (gLifeMode) { + m->numLives++; + } + else if (m->numLives > 0) { + m->numLives--; + } m->healCounter = 31; } // show Mario @@ -2524,7 +2560,7 @@ static s32 act_end_peach_cutscene(struct MarioState *m) { return FALSE; } -#ifdef VERSION_EU +/*#ifdef VERSION_EU #define TIMER_CREDITS_SHOW 51 #define TIMER_CREDITS_PROGRESS 80 #define TIMER_CREDITS_WARP 160 @@ -2532,6 +2568,16 @@ static s32 act_end_peach_cutscene(struct MarioState *m) { #define TIMER_CREDITS_SHOW 61 #define TIMER_CREDITS_PROGRESS 90 #define TIMER_CREDITS_WARP 200 +#endif*/ + +#ifdef VERSION_EU + #define TIMER_CREDITS_SHOW 64 + #define TIMER_CREDITS_PROGRESS 93 + #define TIMER_CREDITS_WARP 173 +#else + #define TIMER_CREDITS_SHOW 74 + #define TIMER_CREDITS_PROGRESS 103 + #define TIMER_CREDITS_WARP 213 #endif static s32 act_credits_cutscene(struct MarioState *m) { diff --git a/src/game/mario_actions_moving.c b/src/game/mario_actions_moving.c index 8f672fa..046aab7 100644 --- a/src/game/mario_actions_moving.c +++ b/src/game/mario_actions_moving.c @@ -13,6 +13,8 @@ #include "behavior_data.h" #include "thread6.h" +#include "settings.h" + struct LandingAction { s16 numFrames; s16 unk02; @@ -461,8 +463,12 @@ void update_walking_speed(struct MarioState *m) { m->forwardVel = 48.0f; } - m->faceAngle[1] = - m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + if (gBetterControls) { + m->faceAngle[1] = m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x1000, 0x1000); + } + else { + m->faceAngle[1] = m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + } apply_slope_accel(m); } @@ -806,8 +812,20 @@ s32 act_walking(struct MarioState *m) { return begin_braking_action(m); } - if (analog_stick_held_back(m) && m->forwardVel >= 16.0f) { - return set_mario_action(m, ACT_TURNING_AROUND, 0); + if (gBetterControls) { + if (analog_stick_held_back(m)) { + if (m->forwardVel >= 12.0f){ + return set_mario_action(m, ACT_TURNING_AROUND, 0); + } else if ((m->forwardVel) < 10.0f && (m->forwardVel > 0.0f)){ + m->faceAngle[1] = m->intendedYaw; + return set_mario_action(m, ACT_TURNING_AROUND, 0); + } + } + } + else { + if (analog_stick_held_back(m) && m->forwardVel >= 16.0f) { + return set_mario_action(m, ACT_TURNING_AROUND, 0); + } } if (m->input & INPUT_Z_PRESSED) { @@ -1047,8 +1065,15 @@ s32 act_braking(struct MarioState *m) { return check_common_action_exits(m); } - if (apply_slope_decel(m, 2.0f)) { - return set_mario_action(m, ACT_BRAKING_STOP, 0); + if (gBetterControls) { + if (apply_slope_decel(m, 2.5f)) { + return set_mario_action(m, ACT_BRAKING_STOP, 0); + } + } + else { + if (apply_slope_decel(m, 2.0f)) { + return set_mario_action(m, ACT_BRAKING_STOP, 0); + } } if (m->input & INPUT_B_PRESSED) { @@ -1328,8 +1353,12 @@ s32 act_burning_ground(struct MarioState *m) { m->forwardVel = approach_f32(m->forwardVel, 32.0f, 4.0f, 1.0f); if (m->input & INPUT_NONZERO_ANALOG) { - m->faceAngle[1] = - m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x600, 0x600); + if (gBetterControls) { + m->faceAngle[1] = m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + } + else { + m->faceAngle[1] = m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x600, 0x600); + } } apply_slope_accel(m); @@ -1565,8 +1594,15 @@ s32 act_dive_slide(struct MarioState *m) { #ifdef VERSION_SH queue_rumble_data(5, 80); #endif - return set_mario_action(m, m->forwardVel > 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, - 0); + if (gSunshineDive && m->input & INPUT_B_PRESSED) { + mario_set_forward_vel(m, 20.0f); + m->vel[1] = 21.0f; + return set_mario_action(m, ACT_DIVE, 0); + } + else { + return set_mario_action(m, m->forwardVel > 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, + 0); + } } play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); @@ -1856,6 +1892,11 @@ s32 act_long_jump_land(struct MarioState *m) { } #endif + // Sorry daddy... + if (gDisableBLJ && m->forwardVel < 0.0f) { + m->forwardVel = 0.0f; + } + if (!(m->input & INPUT_Z_DOWN)) { m->input &= ~INPUT_A_PRESSED; } diff --git a/src/game/mario_actions_stationary.c b/src/game/mario_actions_stationary.c index 3791d11..75d9f2c 100644 --- a/src/game/mario_actions_stationary.c +++ b/src/game/mario_actions_stationary.c @@ -17,6 +17,9 @@ #include "sound_init.h" #include "surface_terrains.h" #include "thread6.h" +#include "game_init.h" + +#include "settings.h" s32 check_common_idle_cancels(struct MarioState *m) { mario_drop_held_object(m); @@ -1046,16 +1049,45 @@ s32 act_twirl_land(struct MarioState *m) { s32 act_ground_pound_land(struct MarioState *m) { m->actionState = 1; - if (m->input & INPUT_UNKNOWN_10) { - return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + + if (gFlashbackPound) { + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + else if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BUTT_SLIDE, 0); + } + if (m->input & INPUT_Z_DOWN) { + if (gPlayer1Controller->stickX != 0 || gPlayer1Controller->stickY != 0) { + m->vel[1] = 2.0f; + mario_set_forward_vel(m, 56.0f); + m->faceAngle[1] = m->intendedYaw; + return set_mario_action(m, ACT_SLIDE_KICK, 0); + } + else { + return set_mario_action(m, ACT_CROUCHING, 0); + } + } + else { + set_mario_action(m, ACT_BACKWARD_ROLLOUT, 0); + m->actionState = 1; + m->vel[1] = 40.0f; + mario_set_forward_vel(m, 0.0f); + return TRUE; + } } + else { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } - if (m->input & INPUT_OFF_FLOOR) { - return set_mario_action(m, ACT_FREEFALL, 0); - } + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } - if (m->input & INPUT_ABOVE_SLIDE) { - return set_mario_action(m, ACT_BUTT_SLIDE, 0); + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BUTT_SLIDE, 0); + } } landing_step(m, MARIO_ANIM_GROUND_POUND_LANDING, ACT_BUTT_SLIDE_STOP); diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c index 72e845f..8d2b1f9 100644 --- a/src/game/mario_actions_submerged.c +++ b/src/game/mario_actions_submerged.c @@ -17,6 +17,8 @@ #include "level_table.h" #include "thread6.h" +#include "settings.h" + #define MIN_SWIM_STRENGTH 160 #define MIN_SWIM_SPEED 16.0f @@ -59,9 +61,19 @@ static f32 get_buoyancy(struct MarioState *m) { buoyancy = -18.0f; } } else if (swimming_near_surface(m)) { - buoyancy = 1.25f; + if (gBetterControls) { + buoyancy = 2.0f; + } + else { + buoyancy = 1.25f; + } } else if (!(m->action & ACT_FLAG_MOVING)) { - buoyancy = -2.0f; + if (gBetterControls) { + buoyancy = -0.5f; + } + else { + buoyancy = -2.0f; + } } return buoyancy; @@ -493,7 +505,12 @@ static s32 check_water_jump(struct MarioState *m) { if (probe >= m->waterLevel - 80 && m->faceAngle[0] >= 0 && m->controller->stickY < -60.0f) { vec3s_set(m->angleVel, 0, 0, 0); - m->vel[1] = 62.0f; + if (gBetterControls) { + m->vel[1] = 64.0f; + } + else { + m->vel[1] = 62.0f; + } if (m->heldObj == NULL) { return set_mario_action(m, ACT_WATER_JUMP, 0); @@ -528,11 +545,21 @@ static s32 act_breaststroke(struct MarioState *m) { } if (m->actionTimer < 6) { - m->forwardVel += 0.5f; + if (gBetterControls) { + m->forwardVel += 1.0f; + } + else { + m->forwardVel += 0.5f; + } } if (m->actionTimer >= 9) { - m->forwardVel += 1.5f; + if (gBetterControls) { + m->forwardVel += 3.0f; + } + else { + m->forwardVel += 1.5f; + } } if (m->actionTimer >= 2) { diff --git a/src/game/mario_step.c b/src/game/mario_step.c index ba8315c..2d94423 100644 --- a/src/game/mario_step.c +++ b/src/game/mario_step.c @@ -9,6 +9,10 @@ #include "interaction.h" #include "mario_step.h" +#include "object_list_processor.h" + +#include "settings.h" + static s16 sMovingSandSpeeds[] = { 12, 8, 4, 0 }; struct Surface gWaterSurfacePseudoFloor = { @@ -402,7 +406,12 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f); floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); - ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + if (gCollisionFixes) { + ceilHeight = vec3f_find_ceil(nextPos, nextPos[1], &ceil); + } + else { + ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + } waterLevel = find_water_level(nextPos[0], nextPos[2]); @@ -429,18 +438,20 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr //! This check uses f32, but findFloor uses short (overflow jumps) if (nextPos[1] <= floorHeight) { - if (ceilHeight - floorHeight > 160.0f) { - m->pos[0] = nextPos[0]; - m->pos[2] = nextPos[2]; - m->floor = floor; - m->floorHeight = floorHeight; - } + if (!gCollisionFixes || gMarioObject->platform == NULL | (m->vel[1]<=0)) { + if (ceilHeight - floorHeight > 160.0f) { + m->pos[0] = nextPos[0]; + m->pos[2] = nextPos[2]; + m->floor = floor; + m->floorHeight = floorHeight; + } - //! When ceilHeight - floorHeight <= 160, the step result says that - // Mario landed, but his movement is cancelled and his referenced floor - // isn't updated (pedro spots) - m->pos[1] = floorHeight; - return AIR_STEP_LANDED; + //! When ceilHeight - floorHeight <= 160, the step result says that + // Mario landed, but his movement is cancelled and his referenced floor + // isn't updated (pedro spots) + m->pos[1] = floorHeight; + return AIR_STEP_LANDED; + } } if (nextPos[1] + 160.0f > ceilHeight) { diff --git a/src/game/obj_behaviors.c b/src/game/obj_behaviors.c index 77bb264..5f89e50 100644 --- a/src/game/obj_behaviors.c +++ b/src/game/obj_behaviors.c @@ -31,6 +31,8 @@ #include "spawn_object.h" #include "spawn_sound.h" +#include "settings.h" + /** * @file obj_behaviors.c * This file contains a portion of the obj behaviors and many helper functions for those @@ -529,7 +531,7 @@ void set_object_visibility(struct Object *obj, s32 dist) { f32 objY = obj->oPosY; f32 objZ = obj->oPosZ; - if (is_point_within_radius_of_mario(objX, objY, objZ, dist) == TRUE) { + if (gDisableDrawDistance || is_point_within_radius_of_mario(objX, objY, objZ, dist) == TRUE) { obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; } else { obj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index a421271..d7c0752 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -11,6 +11,8 @@ #include "shadow.h" #include "sm64.h" +#include "settings.h" + /** * This file contains the code that processes the scene graph for rendering. * The scene graph is responsible for drawing everything except the HUD / text boxes. @@ -272,6 +274,14 @@ static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) { s16 *mtx = (s16 *) gMatStackFixed[gMatStackIndex]; s16 distanceFromCam = -mtx[14]; // z-component of the translation column + // You know what they say, if it works, it works. + if (gNoLowPoly) { + distanceFromCam = 0; + } + if (gForceLowPoly) { + distanceFromCam = 10000; + } + if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) { if (node->node.children != 0) { geo_process_node_and_siblings(node->node.children); diff --git a/src/game/save_file.c b/src/game/save_file.c index 4748b99..28f4fe0 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -481,6 +481,13 @@ u32 save_file_get_flags(void) { return gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags; } +s8 is_save_hard(s16 slot) { + return ((gSaveBuffer.files[slot][0].flags & SAVE_FLAG_HARD_MODE) != 0); +} +s8 is_save_hardcore(s16 slot) { + return ((gSaveBuffer.files[slot][0].flags & SAVE_FLAG_HARDCORE_MODE) != 0); +} + /** * Return the bitset of obtained stars in the specified course. * If course is -1, return the bitset of obtained castle secret stars. diff --git a/src/game/save_file.h b/src/game/save_file.h index 3ee5a19..1ef0c7a 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -104,6 +104,8 @@ extern s8 gLevelToCourseNumTable[]; #define SAVE_FLAG_CAP_ON_UKIKI /* 0x040000 */ (1 << 18) #define SAVE_FLAG_CAP_ON_MR_BLIZZARD /* 0x080000 */ (1 << 19) #define SAVE_FLAG_UNLOCKED_50_STAR_DOOR /* 0x100000 */ (1 << 20) +#define SAVE_FLAG_HARD_MODE /* 0x200000 */ (1 << 21) +#define SAVE_FLAG_HARDCORE_MODE /* 0x800000 */ (1 << 23) // Variable for setting a warp checkpoint. @@ -134,6 +136,8 @@ s32 save_file_get_total_star_count(s32 fileIndex, s32 minCourse, s32 maxCourse); void save_file_set_flags(u32 flags); void save_file_clear_flags(u32 flags); u32 save_file_get_flags(void); +s8 is_save_hard(s16 slot); +s8 is_save_hardcore(s16 slot); u32 save_file_get_star_flags(s32 fileIndex, s32 courseIndex); void save_file_set_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlags); s32 save_file_get_course_coin_score(s32 fileIndex, s32 courseIndex); diff --git a/src/game/settings.c b/src/game/settings.c new file mode 100644 index 0000000..a925dd5 --- /dev/null +++ b/src/game/settings.c @@ -0,0 +1,83 @@ +#include + +s8 gBetterControls = 1; +s8 gDpadInput = 1; +s8 gAirTurn = 0; +s8 gDisableBLJ = 0; +s8 gDisableFallDamage = 0; +int gControllerDeadzone = 500; + +s8 gCollisionFixes = 1; +s8 gRemoveAnnoyingWarps = 1; +s8 gDisableBooDialogue = 1; +s8 gSkipCutscenes = 0; +s8 gLeaveAnyTime = 0; +s8 gVisibleSecrets = 0; +s8 gFlexibleCannons = 0; +int gDontKick = 0; + +s8 gModernWallJump = 1; +s8 gSunshineDive = 0; +s8 gOdysseyDive = 0; +s8 gFlashbackPound = 0; + +s8 configFullscreen = 1; +int gCustomFullscreenResolution = 0; +int gFullscreenWidth = 1920; +int gFullscreenHeight = 1080; +int gFullscreenRefreshRate = 60; +int gWindowWidth = 1280; +int gWindowHeight = 720; +s8 gCustomInternalResolution = 1; +int gInternalResolutionWidth = 3840; +int gInternalResolutionHeight = 2160; +s8 gDrawPillarbox = 0; + +s8 gDisableDrawDistance = 1; +s8 gNoLowPoly = 1; +int gNoiseType = 0; +s8 gDisableFog = 0; +s8 gForceLowPoly = 0; +s8 gNearestNeighbor = 0; +s8 gFXMode = 0; + +s8 gImprovedCamera = 1; +s8 gCenterCam = 1; +s8 gInvertedCamera = 0; +int gCameraSpeed = 32; +int gAdditionalCameraDistance = 0; +int gAdditionalFOV = 0; + +int gNewHud = 2; +s8 gCenterHud = 0; +s8 gHUDFiltering = 0; +int gHUDUpscaling = 0; +s8 gAlwaysShowHealth = 0; +s8 gHideHud = 0; + +s8 gTrapdoorSound = 1; + +s8 gLifeMode = 0; +s8 gHardSave = 0; +s8 gHardcoreSave = 0; +s8 gGreenDemon = 0; +s8 gEncoreMode = 0; + +s8 gDebugMovementMode = 0; + +s8 gShow100CoinStar = 0; +int gTextureUpscaling = 0; + +unsigned int configKeyA = 0x26; +unsigned int configKeyB = 0x33; +unsigned int configKeyStart = 0x39; +unsigned int configKeyR = 0x36; +unsigned int configKeyZ = 0x25; +unsigned int configKeyCUp = 0x148; +unsigned int configKeyCDown = 0x150; +unsigned int configKeyCLeft = 0x14B; +unsigned int configKeyCRight = 0x14D; +unsigned int configKeyStickUp = 0x11; +unsigned int configKeyStickDown = 0x1F; +unsigned int configKeyStickLeft = 0x1E; +unsigned int configKeyStickRight = 0x20; \ No newline at end of file diff --git a/src/game/settings.h b/src/game/settings.h new file mode 100644 index 0000000..ba3062c --- /dev/null +++ b/src/game/settings.h @@ -0,0 +1,83 @@ +#include + +extern s8 gBetterControls; +extern s8 gDpadInput; +extern s8 gAirTurn; +extern s8 gDisableBLJ; +extern s8 gDisableFallDamage; +extern int gControllerDeadzone; + +extern s8 gCollisionFixes; +extern s8 gRemoveAnnoyingWarps; +extern s8 gDisableBooDialogue; +extern s8 gSkipCutscenes; +extern s8 gLeaveAnyTime; +extern s8 gVisibleSecrets; +extern s8 gFlexibleCannons; +extern int gDontKick; + +extern s8 gModernWallJump; +extern s8 gSunshineDive; +extern s8 gOdysseyDive; +extern s8 gFlashbackPound; + +extern s8 configFullscreen; +extern s8 gCustomFullscreenResolution; +extern int gFullscreenWidth; +extern int gFullscreenHeight; +extern int gFullscreenRefreshRate; +extern int gWindowWidth; +extern int gWindowHeight; +extern s8 gCustomInternalResolution; +extern int gInternalResolutionWidth; +extern int gInternalResolutionHeight; +extern s8 gDrawPillarbox; + +extern s8 gDisableDrawDistance; +extern s8 gNoLowPoly; +extern int gNoiseType; +extern s8 gDisableFog; +extern s8 gForceLowPoly; +extern s8 gNearestNeighbor; +extern s8 gFXMode; + +extern s8 gImprovedCamera; +extern s8 gCenterCam; +extern s8 gInvertedCamera; +extern int gCameraSpeed; +extern int gAdditionalCameraDistance; +extern int gAdditionalFOV; + +extern int gNewHud; +extern s8 gCenterHud; +extern s8 gHUDFiltering; +extern int gHUDUpscaling; +extern s8 gAlwaysShowHealth; +extern s8 gHideHud; + +extern s8 gTrapdoorSound; + +extern s8 gLifeMode; +extern s8 gHardSave; +extern s8 gHardcoreSave; +extern s8 gGreenDemon; +extern s8 gEncoreMode; + +extern s8 gDebugMovementMode; + +extern s8 gShow100CoinStar; +extern int gTextureUpscaling; + +extern unsigned int configKeyA; +extern unsigned int configKeyB; +extern unsigned int configKeyStart; +extern unsigned int configKeyR; +extern unsigned int configKeyZ; +extern unsigned int configKeyCUp; +extern unsigned int configKeyCDown; +extern unsigned int configKeyCLeft; +extern unsigned int configKeyCRight; +extern unsigned int configKeyStickUp; +extern unsigned int configKeyStickDown; +extern unsigned int configKeyStickLeft; +extern unsigned int configKeyStickRight; \ No newline at end of file diff --git a/src/game/shadow.c b/src/game/shadow.c index 5ff6bed..8e7123f 100644 --- a/src/game/shadow.c +++ b/src/game/shadow.c @@ -13,6 +13,11 @@ #include "shadow.h" #include "sm64.h" +#ifndef TARGET_N64 +// Avoid Z-fighting +#define find_floor_height_and_data 0.4 + find_floor_height_and_data +#endif + /** * @file shadow.c * This file implements a self-contained subsystem used to draw shadows. diff --git a/src/menu/file_select.c b/src/menu/file_select.c index df28306..65e8b66 100644 --- a/src/menu/file_select.c +++ b/src/menu/file_select.c @@ -21,6 +21,8 @@ #include "sm64.h" #include "text_strings.h" +#include "game/settings.h" + #include "eu_translation.h" #ifdef VERSION_EU #undef LANGUAGE_FUNCTION @@ -177,6 +179,16 @@ static unsigned char textMarioB[] = { TEXT_FILE_MARIO_B }; static unsigned char textMarioC[] = { TEXT_FILE_MARIO_C }; static unsigned char textMarioD[] = { TEXT_FILE_MARIO_D }; +static unsigned char textHardA[] = { TEXT_FILE_HARD_A }; +static unsigned char textHardB[] = { TEXT_FILE_HARD_B }; +static unsigned char textHardC[] = { TEXT_FILE_HARD_C }; +static unsigned char textHardD[] = { TEXT_FILE_HARD_D }; + +static unsigned char textHardcoreA[] = { TEXT_FILE_HARDCORE_A }; +static unsigned char textHardcoreB[] = { TEXT_FILE_HARDCORE_B }; +static unsigned char textHardcoreC[] = { TEXT_FILE_HARDCORE_C }; +static unsigned char textHardcoreD[] = { TEXT_FILE_HARDCORE_D }; + #ifndef VERSION_EU static unsigned char textNew[] = { TEXT_NEW }; static unsigned char starIcon[] = { GLYPH_STAR, GLYPH_SPACE }; @@ -1794,10 +1806,45 @@ void print_main_menu_strings(void) { // Print file names gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_begin); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, sTextBaseAlpha); - print_menu_generic_string(MARIOTEXT_X1, 65, textMarioA); - print_menu_generic_string(MARIOTEXT_X2, 65, textMarioB); - print_menu_generic_string(MARIOTEXT_X1, 105, textMarioC); - print_menu_generic_string(MARIOTEXT_X2, 105, textMarioD); + if (is_save_hardcore(0)) { + print_menu_generic_string(MARIOTEXT_X1, 65, textHardcoreA); + } + else if (is_save_hard(0)) { + print_menu_generic_string(MARIOTEXT_X1, 65, textHardA); + } + else { + print_menu_generic_string(MARIOTEXT_X1, 65, textMarioA); + } + + if (is_save_hardcore(1)) { + print_menu_generic_string(MARIOTEXT_X2, 65, textHardcoreB); + } + else if (is_save_hard(1)) { + print_menu_generic_string(MARIOTEXT_X2, 65, textHardB); + } + else { + print_menu_generic_string(MARIOTEXT_X2, 65, textMarioB); + } + + if (is_save_hardcore(2)) { + print_menu_generic_string(MARIOTEXT_X1, 105, textHardcoreC); + } + else if (is_save_hard(2)) { + print_menu_generic_string(MARIOTEXT_X1, 105, textHardC); + } + else { + print_menu_generic_string(MARIOTEXT_X1, 105, textMarioC); + } + + if (is_save_hardcore(3)) { + print_menu_generic_string(MARIOTEXT_X2, 105, textHardcoreD); + } + else if (is_save_hard(3)) { + print_menu_generic_string(MARIOTEXT_X2, 105, textHardD); + } + else { + print_menu_generic_string(MARIOTEXT_X2, 105, textMarioD); + } gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_end); } diff --git a/src/menu/star_select.c b/src/menu/star_select.c index f3ee91f..678b33e 100644 --- a/src/menu/star_select.c +++ b/src/menu/star_select.c @@ -20,6 +20,8 @@ #include "text_strings.h" #include "prevent_bss_reordering.h" +#include "game/settings.h" + /** * @file star_select.c * This file implements how the star select screen (act selector) function. @@ -96,6 +98,13 @@ void render_100_coin_star(u8 stars) { sStarSelectorModels[6]->oStarSelectorSize = 0.8; sStarSelectorModels[6]->oStarSelectorType = STAR_SELECTOR_100_COINS; } + else if (gShow100CoinStar) { + sStarSelectorModels[6] = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TRANSPARENT_STAR, + bhvActSelectorStarType, 300, 30, -300, 0, 0, 0); + sStarSelectorModels[6]->oStarSelectorSize = 0.8; + sStarSelectorModels[6]->oStarSelectorType = STAR_SELECTOR_100_COINS; + + } } /** diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 10ff1e6..c3f52d9 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -7,6 +7,8 @@ #include #include "configfile.h" +#include "../game/settings.h" +#include "../game/main.h" #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) @@ -26,28 +28,78 @@ struct ConfigOption { }; }; -/* - *Config options and default values - */ -bool configFullscreen = false; -// Keyboard mappings (scancode values) -unsigned int configKeyA = 0x26; -unsigned int configKeyB = 0x33; -unsigned int configKeyStart = 0x39; -unsigned int configKeyR = 0x36; -unsigned int configKeyZ = 0x25; -unsigned int configKeyCUp = 0x148; -unsigned int configKeyCDown = 0x150; -unsigned int configKeyCLeft = 0x14B; -unsigned int configKeyCRight = 0x14D; -unsigned int configKeyStickUp = 0x11; -unsigned int configKeyStickDown = 0x1F; -unsigned int configKeyStickLeft = 0x1E; -unsigned int configKeyStickRight = 0x20; - - static const struct ConfigOption options[] = { + {.name = "improved_controls",.type = CONFIG_TYPE_BOOL, .boolValue = &gBetterControls}, + {.name = "dpad_controls", .type = CONFIG_TYPE_BOOL, .boolValue = &gDpadInput}, + {.name = "full_air_control", .type = CONFIG_TYPE_BOOL, .boolValue = &gAirTurn}, + {.name = "disable_blj", .type = CONFIG_TYPE_BOOL, .boolValue = &gDisableBLJ}, + {.name = "disable_fall_damage",.type = CONFIG_TYPE_BOOL, .boolValue = &gDisableFallDamage}, + {.name = "analog_stick_deadzone",.type = CONFIG_TYPE_UINT, .uintValue = &gControllerDeadzone}, + + {.name = "fix_collision_errors",.type = CONFIG_TYPE_BOOL, .boolValue = &gCollisionFixes}, + {.name = "remove_annoying_warps",.type = CONFIG_TYPE_BOOL, .boolValue = &gRemoveAnnoyingWarps}, + {.name = "disable_all_boo_messages_except_the_first",.type = CONFIG_TYPE_BOOL, .boolValue = &gDisableBooDialogue}, + {.name = "skip_cutscenes", .type = CONFIG_TYPE_BOOL, .boolValue = &gSkipCutscenes}, + {.name = "allow_leaving_the_level_at_any_time", .type = CONFIG_TYPE_BOOL, .boolValue = &gLeaveAnyTime}, + {.name = "make_secrets_visible",.type = CONFIG_TYPE_BOOL, .boolValue = &gVisibleSecrets}, + {.name = "allow_the_cannons_to_rotate_more",.type = CONFIG_TYPE_BOOL, .boolValue = &gFlexibleCannons}, + {.name = "stay_in_level_after_getting_a_star", .type = CONFIG_TYPE_UINT, .uintValue = &gDontKick}, + + {.name = "wall_sliding",.type = CONFIG_TYPE_BOOL, .boolValue = &gModernWallJump}, + {.name = "sunshine_dive_hop", .type = CONFIG_TYPE_BOOL, .boolValue = &gSunshineDive}, + {.name = "odyssey_ground_pound_dive", .type = CONFIG_TYPE_BOOL, .boolValue = &gOdysseyDive}, + {.name = "flashback_ground_pound", .type = CONFIG_TYPE_BOOL, .boolValue = &gFlashbackPound}, + {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen}, + {.name = "custom_fullscreen_resolution",.type = CONFIG_TYPE_BOOL, .boolValue = &gCustomFullscreenResolution}, + {.name = "fullscreen_width",.type = CONFIG_TYPE_UINT, .uintValue = &gFullscreenWidth}, + {.name = "fullscreen_height",.type = CONFIG_TYPE_UINT, .uintValue = &gFullscreenHeight}, + {.name = "fullscreen_refresh_rate",.type = CONFIG_TYPE_UINT, .uintValue = &gFullscreenRefreshRate}, + {.name = "window_width", .type = CONFIG_TYPE_UINT, .uintValue = &gWindowWidth}, + {.name = "window_height", .type = CONFIG_TYPE_UINT, .uintValue = &gWindowHeight}, + {.name = "custom_internal_resolution",.type = CONFIG_TYPE_BOOL, .boolValue = &gCustomInternalResolution}, + {.name = "internal_resolution_width",.type = CONFIG_TYPE_UINT, .uintValue = &gInternalResolutionWidth}, + {.name = "internal_resolution_height",.type = CONFIG_TYPE_UINT, .uintValue = &gInternalResolutionHeight}, + {.name = "force_4by3", .type = CONFIG_TYPE_BOOL, .boolValue = &gDrawPillarbox}, + + {.name = "disable_draw_distance",.type = CONFIG_TYPE_BOOL, .boolValue = &gDisableDrawDistance}, + {.name = "disable_low_poly_mario",.type = CONFIG_TYPE_BOOL, .boolValue = &gNoLowPoly}, + {.name = "noise_type", .type = CONFIG_TYPE_UINT, .uintValue = &gNoiseType}, + {.name = "disable_fog", .type = CONFIG_TYPE_BOOL, .boolValue = &gDisableFog}, + {.name = "force_low_poly_mario", .type = CONFIG_TYPE_BOOL, .boolValue = &gForceLowPoly}, + {.name = "nearest_neighbor_filtering",.type = CONFIG_TYPE_BOOL, .boolValue = &gNearestNeighbor}, + {.name = "fx_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gFXMode}, + + {.name = "improved_camera", .type = CONFIG_TYPE_BOOL, .boolValue = &gImprovedCamera}, + {.name = "center_camera_button",.type = CONFIG_TYPE_BOOL, .boolValue = &gCenterCam}, + {.name = "invert_camera_controls",.type = CONFIG_TYPE_BOOL, .boolValue = &gInvertedCamera}, + {.name = "analog_camera_speed", .type = CONFIG_TYPE_UINT, .uintValue = &gCameraSpeed}, + {.name = "additional_camera_distance",.type = CONFIG_TYPE_UINT, .uintValue = &gAdditionalCameraDistance}, + {.name = "additional_fov", .type = CONFIG_TYPE_UINT, .uintValue = &gAdditionalFOV}, + + {.name = "hud_style", .type = CONFIG_TYPE_UINT, .uintValue = &gNewHud}, + {.name = "4by3_hud", .type = CONFIG_TYPE_BOOL, .boolValue = &gCenterHud}, + {.name = "hud_filtering",.type = CONFIG_TYPE_BOOL, .boolValue = &gHUDFiltering}, + {.name = "hud_upscaling", .type = CONFIG_TYPE_UINT, .uintValue = &gHUDUpscaling}, + {.name = "always_show_the_health_meter", .type = CONFIG_TYPE_BOOL, .boolValue = &gAlwaysShowHealth}, + {.name = "hide_hud", .type = CONFIG_TYPE_BOOL, .boolValue = &gHideHud}, + + {.name = "enable_the_trapdoor_sound", .type = CONFIG_TYPE_BOOL, .boolValue = &gTrapdoorSound}, + + {.name = "infinite_lives_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gLifeMode}, + {.name = "hard_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gHardSave}, + {.name = "permadeath_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gHardcoreSave}, + {.name = "green_demon_mode",.type = CONFIG_TYPE_BOOL, .boolValue = &gGreenDemon}, + {.name = "encore_mode", .type = CONFIG_TYPE_BOOL, .boolValue = &gEncoreMode}, + + {.name = "level_select", .type = CONFIG_TYPE_BOOL, .boolValue = &gDebugLevelSelect}, + {.name = "show_debug_profiler",.type = CONFIG_TYPE_BOOL, .boolValue = &gShowProfiler}, + {.name = "show_debug_display",.type = CONFIG_TYPE_BOOL, .boolValue = &gShowDebugText}, + {.name = "debug_movement_mode",.type = CONFIG_TYPE_BOOL, .boolValue = &gDebugMovementMode}, + + {.name = "show_100_coin_star",.type = CONFIG_TYPE_BOOL, .boolValue = &gShow100CoinStar}, + {.name = "texture_upscaling",.type = CONFIG_TYPE_UINT, .uintValue = &gTextureUpscaling}, + {.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA}, {.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB}, {.name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart}, @@ -60,7 +112,7 @@ static const struct ConfigOption options[] = { {.name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp}, {.name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown}, {.name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft}, - {.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight}, + {.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight} }; // Reads an entire line from a file (excluding the newline character) and returns an allocated string diff --git a/src/pc/configfile.h b/src/pc/configfile.h index ae9070b..54c4794 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -1,21 +1,6 @@ #ifndef CONFIGFILE_H #define CONFIGFILE_H -extern bool configFullscreen; -extern unsigned int configKeyA; -extern unsigned int configKeyB; -extern unsigned int configKeyStart; -extern unsigned int configKeyR; -extern unsigned int configKeyZ; -extern unsigned int configKeyCUp; -extern unsigned int configKeyCDown; -extern unsigned int configKeyCLeft; -extern unsigned int configKeyCRight; -extern unsigned int configKeyStickUp; -extern unsigned int configKeyStickDown; -extern unsigned int configKeyStickLeft; -extern unsigned int configKeyStickRight; - void configfile_load(const char *filename); void configfile_save(const char *filename); diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index f86cf0d..f83ed3f 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -45,6 +45,8 @@ void osContGetReadData(OSContPad *pad) { pad->button = 0; pad->stick_x = 0; pad->stick_y = 0; + pad->stick2_x = 0; + pad->stick2_y = 0; pad->errnum = 0; for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) { diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c index a49b86a..3fede92 100644 --- a/src/pc/controller/controller_keyboard.c +++ b/src/pc/controller/controller_keyboard.c @@ -7,7 +7,7 @@ #include "controller_emscripten_keyboard.h" #endif -#include "../configfile.h" +#include "game/settings.h" static int keyboard_buttons_down; diff --git a/src/pc/controller/controller_sdl.c b/src/pc/controller/controller_sdl.c index c3ab242..7319ca7 100644 --- a/src/pc/controller/controller_sdl.c +++ b/src/pc/controller/controller_sdl.c @@ -55,6 +55,7 @@ static void controller_sdl_read(OSContPad *pad) { if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) pad->button |= R_TRIG; if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_A)) pad->button |= A_BUTTON; if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_X)) pad->button |= B_BUTTON; + if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_RIGHTSTICK)) pad->button |= L_TRIG; int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX); int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY); @@ -78,8 +79,19 @@ static void controller_sdl_read(OSContPad *pad) { } #endif - if (rightx < -0x4000) pad->button |= L_CBUTTONS; - if (rightx > 0x4000) pad->button |= R_CBUTTONS; + if (gImprovedCamera) { + uint32_t magnitude_sq2 = (uint32_t)(rightx * rightx) + (uint32_t)(righty * righty); + if (magnitude_sq > (uint32_t)(DEADZONE * DEADZONE)) { + pad->stick2_x = rightx / 0x100; + //int stick_y = -righty / 0x100; + //pad->stick2_y = stick_y == 128 ? 127 : stick_y; + } + } + else { + if (rightx < -0x4000) pad->button |= L_CBUTTONS; + if (rightx > 0x4000) pad->button |= R_CBUTTONS; + } + if (righty < -0x4000) pad->button |= U_CBUTTONS; if (righty > 0x4000) pad->button |= D_CBUTTONS; diff --git a/src/pc/controller/controller_wup.c b/src/pc/controller/controller_wup.c index 0a675ff..30e4388 100644 --- a/src/pc/controller/controller_wup.c +++ b/src/pc/controller/controller_wup.c @@ -30,8 +30,16 @@ static void controller_wup_read(OSContPad *pad) { if (buttons & 0x0100) pad->button |= A_BUTTON; if (buttons & 0x0200) pad->button |= B_BUTTON; if (buttons & 0x1000) pad->button |= L_TRIG; - if (axis[2] < 0x40) pad->button |= L_CBUTTONS; - if (axis[2] > 0xC0) pad->button |= R_CBUTTONS; + if (gImprovedCamera) { + if (stick2_x != 0 || stick2_y != 0) { + pad->stick2_x = saturate(axis[2] - 128 - 0); + //pad->stick2_y = saturate(axis[3] - 128 - 0); + } + } + else { + if (axis[2] < 0x40) pad->button |= L_CBUTTONS; + if (axis[2] > 0xC0) pad->button |= R_CBUTTONS; + } if (axis[3] < 0x40) pad->button |= D_CBUTTONS; if (axis[3] > 0xC0) pad->button |= U_CBUTTONS; int8_t stick_x = saturate(axis[0] - 128 - 0); diff --git a/src/pc/controller/controller_xinput.c b/src/pc/controller/controller_xinput.c index bb44bc1..c502e42 100644 --- a/src/pc/controller/controller_xinput.c +++ b/src/pc/controller/controller_xinput.c @@ -6,8 +6,9 @@ #include #include "controller_api.h" +#include "game/settings.h" -#define DEADZONE 4960 +#define DEADZONE gControllerDeadzone*10 static void xinput_init(void) { } @@ -25,9 +26,17 @@ static void xinput_read(OSContPad *pad) { if (gp->bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) pad->button |= R_TRIG; if (gp->wButtons & XINPUT_GAMEPAD_A) pad->button |= A_BUTTON; if (gp->wButtons & XINPUT_GAMEPAD_X) pad->button |= B_BUTTON; - if (gp->wButtons & XINPUT_GAMEPAD_DPAD_LEFT) pad->button |= L_TRIG; - if (gp->sThumbRX < -0x4000) pad->button |= L_CBUTTONS; - if (gp->sThumbRX > 0x4000) pad->button |= R_CBUTTONS; + if (gp->wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) pad->button |= L_TRIG; + + if (gp->wButtons & XINPUT_GAMEPAD_DPAD_LEFT) pad->button |= L_JPAD; + if (gp->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) pad->button |= R_JPAD; + if (gp->wButtons & XINPUT_GAMEPAD_DPAD_UP) pad->button |= U_JPAD; + if (gp->wButtons & XINPUT_GAMEPAD_DPAD_DOWN) pad->button |= D_JPAD; + + if (!gImprovedCamera) { + if (gp->sThumbRX < -0x4000) pad->button |= L_CBUTTONS; + if (gp->sThumbRX > 0x4000) pad->button |= R_CBUTTONS; + } if (gp->sThumbRY < -0x4000) pad->button |= D_CBUTTONS; if (gp->sThumbRY > 0x4000) pad->button |= U_CBUTTONS; @@ -36,6 +45,13 @@ static void xinput_read(OSContPad *pad) { pad->stick_x = gp->sThumbLX / 0x100; pad->stick_y = gp->sThumbLY / 0x100; } + if (gImprovedCamera) { + uint32_t magnitude_sq2 = (uint32_t)(gp->sThumbRX * gp->sThumbRX) + (uint32_t)(gp->sThumbRY * gp->sThumbRY); + if (magnitude_sq2 > (uint32_t)(DEADZONE * DEADZONE)) { + pad->stick2_x = gp->sThumbRX / 0x100; + // pad->stick2_y = gp->sThumbRY / 0x100; + } + } break; } } diff --git a/src/pc/gfx/filters.h b/src/pc/gfx/filters.h new file mode 100644 index 0000000..22ac8f5 --- /dev/null +++ b/src/pc/gfx/filters.h @@ -0,0 +1,81 @@ +/* + * XBR filter: from the FFmpeg project + * + * Copyright (c) 2011, 2012 Hyllian/Jararaca + * Copyright (c) 2014 Arwa Arif + * Copyright (c) 2015 Treeki + * + * + * hqx filter: from the hqx project + * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) + * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) + * Copyright (c) 2015 Treeki + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LIBXBR_FILTERS_H_INCLUDED +#define __LIBXBR_FILTERS_H_INCLUDED + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER + #define XBR_INLINE __inline + + #ifdef XBR_INTERNAL + #define XBR_EXPORT __declspec(dllexport) + #else + #define XBR_EXPORT __declspec(dllimport) + #endif +#else + #define XBR_INLINE inline + #define XBR_EXPORT +#endif + +typedef struct { + uint32_t rgbtoyuv[1<<24]; +} xbr_data; + +typedef struct { + const uint8_t *input; + uint8_t *output; + int inWidth, inHeight; + int inPitch, outPitch; + const xbr_data *data; +} xbr_params; + +XBR_EXPORT void xbr_filter_xbr2x(const xbr_params *ctx); +XBR_EXPORT void xbr_filter_xbr3x(const xbr_params *ctx); +XBR_EXPORT void xbr_filter_xbr4x(const xbr_params *ctx); + +XBR_EXPORT void xbr_filter_hq2x(const xbr_params *ctx); +XBR_EXPORT void xbr_filter_hq3x(const xbr_params *ctx); +XBR_EXPORT void xbr_filter_hq4x(const xbr_params *ctx); + +XBR_EXPORT void xbr_init_data(xbr_data *data); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/pc/gfx/gfx_direct3d11.cpp b/src/pc/gfx/gfx_direct3d11.cpp index 3ccebe8..e1a39d6 100644 --- a/src/pc/gfx/gfx_direct3d11.cpp +++ b/src/pc/gfx/gfx_direct3d11.cpp @@ -27,6 +27,8 @@ #include "gfx_screen_config.h" +#include "./game/settings.h" + #define THREE_POINT_FILTERING 0 #define DEBUG_D3D 0 @@ -143,8 +145,14 @@ static void create_render_target_views(bool is_resize) { // Resize swap chain buffers ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1)); - ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags), - gfx_dxgi_get_h_wnd(), "Failed to resize IDXGISwapChain buffers."); + if (gCustomInternalResolution) { + ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, gInternalResolutionWidth, gInternalResolutionHeight, DXGI_FORMAT_UNKNOWN, desc1.Flags), + gfx_dxgi_get_h_wnd(), "Failed to resize IDXGISwapChain buffers."); + } + else { + ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags), + gfx_dxgi_get_h_wnd(), "Failed to resize IDXGISwapChain buffers."); + } } // Get new size @@ -247,6 +255,20 @@ static void gfx_d3d11_init(void) { // Create the swap chain d3d.swap_chain = gfx_dxgi_create_swap_chain(d3d.device.Get()); + // Enable MSAA + // I fucking TRIED but couldn't get it working I'm STUPID if anyone reading this that knows what they are doing let me know lol + /* + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc; + ThrowIfFailed(d3d.swap_chain->GetDesc1(&swap_chain_desc)); + + u_int num_levels = 0; + HRESULT hr = d3d.device->CheckMultisampleQualityLevels(swap_chain_desc.Format, D3D10_MAX_MULTISAMPLE_SAMPLE_COUNT, &num_levels); + if (hr == S_OK) { + swap_chain_desc.SampleDesc.Count = D3D10_MAX_MULTISAMPLE_SAMPLE_COUNT; + swap_chain_desc.SampleDesc.Quality = num_levels-1; + } + */ + // Create D3D Debug device if in debug mode #if DEBUG_D3D @@ -441,8 +463,14 @@ static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, int width, int h D3D11_TEXTURE2D_DESC texture_desc; ZeroMemory(&texture_desc, sizeof(D3D11_TEXTURE2D_DESC)); - texture_desc.Width = width; - texture_desc.Height = height; + if (gFXMode) { + texture_desc.Width = 1; + texture_desc.Height = 1; + } + else { + texture_desc.Width = width; + texture_desc.Height = height; + } texture_desc.Usage = D3D11_USAGE_IMMUTABLE; texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -682,8 +710,14 @@ static void gfx_d3d11_start_frame(void) { d3d.per_frame_cb_data.noise_frame = 0; } float aspect_ratio = (float) d3d.current_width / (float) d3d.current_height; - d3d.per_frame_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 - d3d.per_frame_cb_data.noise_scale_y = 120; + if (gNoiseType) { + d3d.per_frame_cb_data.noise_scale_x = d3d.current_width; + d3d.per_frame_cb_data.noise_scale_y = d3d.current_height; + } + else { + d3d.per_frame_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 + d3d.per_frame_cb_data.noise_scale_y = 120; + } D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); diff --git a/src/pc/gfx/gfx_direct3d12.cpp b/src/pc/gfx/gfx_direct3d12.cpp index 712fce6..08ee459 100644 --- a/src/pc/gfx/gfx_direct3d12.cpp +++ b/src/pc/gfx/gfx_direct3d12.cpp @@ -39,6 +39,8 @@ #include "gfx_screen_config.h" +#include "./game/settings.h" + #define DEBUG_D3D 0 using namespace Microsoft::WRL; // For ComPtr @@ -302,8 +304,14 @@ static void gfx_direct3d12_upload_texture(const uint8_t *rgba32_buf, int width, D3D12_RESOURCE_DESC texture_desc = {}; texture_desc.MipLevels = 1; texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - texture_desc.Width = width; - texture_desc.Height = height; + if (gFXMode) { + texture_desc.Width = 1; + texture_desc.Height = 1; + } + else { + texture_desc.Width = width; + texture_desc.Height = height; + } texture_desc.Flags = D3D12_RESOURCE_FLAG_NONE; texture_desc.DepthOrArraySize = 1; texture_desc.SampleDesc.Count = 1; @@ -609,8 +617,14 @@ static void gfx_direct3d12_start_frame(void) { d3d.noise_cb_data.noise_frame = 0; } float aspect_ratio = (float) d3d.current_width / (float) d3d.current_height; - d3d.noise_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 - d3d.noise_cb_data.noise_scale_y = 120; + if (gNoiseType) { + d3d.noise_cb_data.noise_scale_x = d3d.current_width; + d3d.noise_cb_data.noise_scale_y = d3d.current_height; + } + else { + d3d.noise_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 + d3d.noise_cb_data.noise_scale_y = 120; + } memcpy(d3d.mapped_noise_cb_address, &d3d.noise_cb_data, sizeof(struct NoiseCB)); d3d.vbuf_pos = 0; @@ -671,7 +685,13 @@ static void gfx_direct3d12_on_resize(void) { if (d3d.render_targets[0].Get() != nullptr) { d3d.render_targets[0].Reset(); d3d.render_targets[1].Reset(); - ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)); + if (gCustomInternalResolution) { + ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, gInternalResolutionWidth, gInternalResolutionHeight, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)); + } + else { + ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)); + } + d3d.frame_index = d3d.swap_chain->GetCurrentBackBufferIndex(); create_render_target_views(); create_depth_buffer(); diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp index 02cdd81..079093f 100644 --- a/src/pc/gfx/gfx_dxgi.cpp +++ b/src/pc/gfx/gfx_dxgi.cpp @@ -14,6 +14,7 @@ #include +#include "./game/settings.h" #ifndef _LANGUAGE_C #define _LANGUAGE_C @@ -143,9 +144,43 @@ static void toggle_borderless_window_full_screen(bool enable, bool call_callback return; } + // Get the primary monitor + POINT zero = { 0, 0 }; + HMONITOR h_monitor = MonitorFromPoint(zero, MONITOR_DEFAULTTOPRIMARY); + + // Get info from that monitor + MONITORINFOEX monitor_info; + monitor_info.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(h_monitor, &monitor_info); + RECT primary = monitor_info.rcMonitor; + if (!enable) { + + if (gCustomFullscreenResolution) { + ChangeDisplaySettings(NULL, CDS_RESET); + } RECT r = dxgi.last_window_rect; + // Prevent the window from going outside the primary display + if (r.left > primary.right) { + r.left -= primary.right; + r.right -= primary.right; + } + if (r.top > primary.bottom) { + r.top -= primary.bottom; + r.bottom -= primary.bottom; + } + + if (r.right < primary.left) { + r.left += primary.left; + r.right += primary.left; + } + + if (r.bottom < primary.top) { + r.top += primary.top; + r.bottom += primary.top; + } + // Set in window mode with the last saved position and size SetWindowLongPtr(dxgi.h_wnd, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); @@ -161,6 +196,25 @@ static void toggle_borderless_window_full_screen(bool enable, bool call_callback dxgi.is_full_screen = false; } else { + + if (gCustomFullscreenResolution) { + + DEVMODE fullscreenSettings; + + memset (&fullscreenSettings, 0, sizeof (fullscreenSettings)); + fullscreenSettings.dmSize = sizeof (fullscreenSettings); + fullscreenSettings.dmPelsWidth = gFullscreenWidth; + fullscreenSettings.dmPelsHeight = gFullscreenHeight; + fullscreenSettings.dmBitsPerPel = 32; + fullscreenSettings.dmDisplayFrequency = gFullscreenRefreshRate; + fullscreenSettings.dmFields = DM_PELSWIDTH | + DM_PELSHEIGHT | + DM_BITSPERPEL | + DM_DISPLAYFREQUENCY; + + ChangeDisplaySettings(&fullscreenSettings, CDS_FULLSCREEN); + } + // Save if window is maximized or not WINDOWPLACEMENT window_placement; window_placement.length = sizeof(WINDOWPLACEMENT); @@ -170,18 +224,9 @@ static void toggle_borderless_window_full_screen(bool enable, bool call_callback // Save window position and size if the window is not maximized GetWindowRect(dxgi.h_wnd, &dxgi.last_window_rect); - // Get in which monitor the window is - HMONITOR h_monitor = MonitorFromWindow(dxgi.h_wnd, MONITOR_DEFAULTTONEAREST); - - // Get info from that monitor - MONITORINFOEX monitor_info; - monitor_info.cbSize = sizeof(MONITORINFOEX); - GetMonitorInfo(h_monitor, &monitor_info); - RECT r = monitor_info.rcMonitor; - - // Set borderless full screen to that monitor + // Set borderless full screen to the primary monitor SetWindowLongPtr(dxgi.h_wnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); - SetWindowPos(dxgi.h_wnd, HWND_TOP, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_FRAMECHANGED); + SetWindowPos(dxgi.h_wnd, HWND_TOP, primary.left, primary.top, primary.right - primary.left, primary.bottom - primary.top, SWP_FRAMECHANGED); ShowCursor(FALSE); @@ -536,19 +581,26 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea } ComPtr gfx_dxgi_create_swap_chain(IUnknown *device) { - bool win8 = IsWindows8OrGreater(); // DXGI_SCALING_NONE is only supported on Win8 and beyond + bool win8 = IsWindows8OrGreater() && !gCustomInternalResolution; // DXGI_SCALING_NONE is only supported on Win8 and beyond bool dxgi_13 = dxgi.CreateDXGIFactory2 != nullptr; // DXGI 1.3 introduced waitable object DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; swap_chain_desc.BufferCount = 2; - swap_chain_desc.Width = 0; - swap_chain_desc.Height = 0; + if (gCustomInternalResolution) { + swap_chain_desc.Width = gInternalResolutionWidth; + swap_chain_desc.Height = gInternalResolutionHeight; + } + else { + swap_chain_desc.Width = 0; + swap_chain_desc.Height = 0; + } swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc.Scaling = win8 ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // Apparently this was backported to Win 7 Platform Update swap_chain_desc.Flags = dxgi_13 ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0; swap_chain_desc.SampleDesc.Count = 1; + run_as_dpi_aware([&] () { // When setting size for the buffers, the values that DXGI puts into the desc (that can later be retrieved by GetDesc1) diff --git a/src/pc/gfx/gfx_glx.c b/src/pc/gfx/gfx_glx.c index 97fe4a8..5962394 100644 --- a/src/pc/gfx/gfx_glx.c +++ b/src/pc/gfx/gfx_glx.c @@ -15,6 +15,8 @@ #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" +#include "./game/game_init.h" + #define GFX_API_NAME "GLX - OpenGL" #ifdef VERSION_EU diff --git a/src/pc/gfx/gfx_opengl.c b/src/pc/gfx/gfx_opengl.c index 7bc9745..8964855 100644 --- a/src/pc/gfx/gfx_opengl.c +++ b/src/pc/gfx/gfx_opengl.c @@ -28,6 +28,8 @@ #include "gfx_cc.h" #include "gfx_rendering_api.h" +#include "./game/settings.h" + struct ShaderProgram { uint32_t shader_id; GLuint opengl_program_id; @@ -408,7 +410,12 @@ static void gfx_opengl_select_texture(int tile, GLuint texture_id) { } static void gfx_opengl_upload_texture(const uint8_t *rgba32_buf, int width, int height) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf); + if (gFXMode) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf); + } } static uint32_t gfx_cm_to_opengl(uint32_t val) { diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index a60299e..4987648 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -16,6 +16,12 @@ #include "gfx_rendering_api.h" #include "gfx_screen_config.h" +#include "filters.h" + +#include "../../game/game_init.h" +#include "../../game/settings.h" +#include "../../engine/math_util.h" + #define SUPPORT_CHECK(x) assert(x) // SCALE_M_N: upscale/downscale M-bit integer to N-bit @@ -255,15 +261,18 @@ static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, co while (*node != NULL && *node - gfx_texture_cache.pool < (int)gfx_texture_cache.pool_pos) { if ((*node)->texture_addr == orig_addr && (*node)->fmt == fmt && (*node)->siz == siz) { gfx_rapi->select_texture(tile, (*node)->texture_id); + if (gEncoreMode) + (*node)->linear_filter = (get_palette() == 12); *n = *node; return true; } node = &(*node)->next; } - if (gfx_texture_cache.pool_pos == sizeof(gfx_texture_cache.pool) / sizeof(struct TextureHashmapNode)) { + if (gfx_texture_cache.pool_pos == sizeof(gfx_texture_cache.pool) / sizeof(struct TextureHashmapNode) || gReimportTextures) { // Pool is full. We just invalidate everything and start over. gfx_texture_cache.pool_pos = 0; node = &gfx_texture_cache.hashmap[hash]; + gReimportTextures = 0; //puts("Clearing texture cache"); } *node = &gfx_texture_cache.pool[gfx_texture_cache.pool_pos++]; @@ -274,7 +283,7 @@ static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, co gfx_rapi->set_sampler_parameters(tile, false, 0, 0); (*node)->cms = 0; (*node)->cmt = 0; - (*node)->linear_filter = false; + (*node)->linear_filter = (get_palette() == 12); // Use nearest neighbor on Wet Dry World (*node)->next = NULL; (*node)->texture_addr = orig_addr; (*node)->fmt = fmt; @@ -285,6 +294,9 @@ static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, co static void import_texture_rgba16(int tile) { uint8_t rgba32_buf[8192]; + uint8_t rgba32_buf_out[8192*4*4]; + xbr_data *xbrData; + xbr_params xbrParams; for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes / 2; i++) { uint16_t col16 = (rdp.loaded_texture[tile].addr[2 * i] << 8) | rdp.loaded_texture[tile].addr[2 * i + 1]; @@ -292,16 +304,183 @@ static void import_texture_rgba16(int tile) { uint8_t r = col16 >> 11; uint8_t g = (col16 >> 6) & 0x1f; uint8_t b = (col16 >> 1) & 0x1f; - rgba32_buf[4*i + 0] = SCALE_5_8(r); - rgba32_buf[4*i + 1] = SCALE_5_8(g); - rgba32_buf[4*i + 2] = SCALE_5_8(b); + if ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT) { + // Return different palettes + switch (get_palette()) { + default: + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(b); + break; + case 1: // Castle grounds + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b+r*0.125+g*0.25f,31)); + break; + case 2: //Bob Omb Battlefield + rgba32_buf[4*i + 0] = SCALE_5_8(max(min(r*1.125+g*0.625-b*0.625,31),0)); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(max(min(b*1.0625-(r+g)*0.03125,31), 0)); + break; + case 3: // Whomp's Fortress + case 21: // Metal Cave + rgba32_buf[4*i + 0] = SCALE_5_8(min(r+g*0.625f+b*0.0625f,31)); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g+r*0.25f+b*0.03125,31)); + rgba32_buf[4*i + 2] = SCALE_5_8(b); + break; + case 4: // Jolly Roger Bay + case 15: // Tick Tock Clock + case 22: // Wing Mario Over the Rainbows + rgba32_buf[4*i + 0] = SCALE_5_8(min(r*0.75, 31)*0.75); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g*0.5+r*0.0625+b*0.0625, 31)*0.75); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b+r*0.5+g*0.5,31)*0.75); + break; + case 5: // Cool Cool Mountain + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b*0.96875+r*0.5f+g*0.5f,31)); + break; + case 6: // Big Boo's Haunt + case 19: // Sky + rgba32_buf[4*i + 0] = SCALE_5_8((r+g+b)/3); + rgba32_buf[4*i + 1] = SCALE_5_8((r+g+b)/3); + rgba32_buf[4*i + 2] = SCALE_5_8((r+g+b)/3); + break; + case 7: // Hazy Maze Cave + rgba32_buf[4*i + 0] = SCALE_5_8(max(min(r-g*0.5+b*0.5,31),0)); + rgba32_buf[4*i + 1] = SCALE_5_8(max(min((g-r*0.5+b*0.5)*1.5-(r-g*0.5+b*0.5+b*0.75+g*0.25)*0.25, 31),0)); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b*0.75+g*0.25,31)); + break; + case 8: // Lethal Lava Land + case 23: // Vanish Cap area + case 10: // Dire Dire Docks + rgba32_buf[4*i + 0] = SCALE_5_8(min(r+g*0.5+b*0.5, 31)); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g+r*0.0625+b*0.0625, 31)); + rgba32_buf[4*i + 2] = SCALE_5_8(b*0.875); + break; + case 9: // Shitting Sand Land + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b*0.5+r*0.25+g*0.25, 31)); + break; + case 11: // Snowman + case 24: // Shit level + rgba32_buf[4*i + 0] = SCALE_5_8(min(r+b*0.25, 31)); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(b); + break; + case 12: // Wet Dry World + rgba32_buf[4*i + 0] = SCALE_5_8(min(round(sqrt(r/31.0f)*8)*4,31)); + rgba32_buf[4*i + 1] = SCALE_5_8(min(round(sqrt(g/31.0f)*8)*4,31)); + rgba32_buf[4*i + 2] = SCALE_5_8(min(round(sqrt(b/31.0f)*8)*4,31)); + break; + case 13: // Donkey Slide + rgba32_buf[4*i + 0] = SCALE_5_8(max(min(r+g*1.25-b*1.5,31), 0)); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(b); + break; + case 14: // Tiny Huge Island + rgba32_buf[4*i + 0] = SCALE_5_8(min(r*0.75, 31)); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g*0.5+r*0.0625+b*0.0625, 31)); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b+r*0.5+g*0.5,31)); + break; + case 16: // Rainbow Ride + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(max(min(b+g*1.25-r*0.5,31),0)); + break; + case 17: // Dank world + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(b); + rgba32_buf[4*i + 2] = SCALE_5_8(g); + break; + case 18: // Bowser in the Fire Sea + if (r > (g+b)*2) { + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g*0.875+r*0.0625+b*0.0625, 31)*0.3125+g*0.5); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b+r*0.75+g*0.75,31)*0.625); + } + else + { + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g*0.875+r*0.0625+b*0.0625, 31)*0.875); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b+r*0.75+g*0.75,31)*0.875); + } + break; + case 20: // Secret Slide + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8((r+g+b)/3); + rgba32_buf[4*i + 2] = SCALE_5_8((r+g+b)/3); + break; + case 25: // Secret Aquarium + rgba32_buf[4*i + 0] = SCALE_5_8(r*0.875); + rgba32_buf[4*i + 1] = SCALE_5_8(min(g*0.875+r*0.0625+b*0.0625, 31)); + rgba32_buf[4*i + 2] = SCALE_5_8(min(b+r*0.75+g*0.75,31)); + break; + case 26: // Ending + rgba32_buf[4*i + 0] = SCALE_5_8(min((r+g+b)/2,31)); + rgba32_buf[4*i + 1] = SCALE_5_8(min((r+g+b)/2.5,31)); + rgba32_buf[4*i + 2] = SCALE_5_8(min((r+g+b)/3,31)); + break; + } + + } + else { + rgba32_buf[4*i + 0] = SCALE_5_8(r); + rgba32_buf[4*i + 1] = SCALE_5_8(g); + rgba32_buf[4*i + 2] = SCALE_5_8(b); + } + rgba32_buf[4*i + 3] = a ? 255 : 0; } uint32_t width = rdp.texture_tile.line_size_bytes / 2; uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - gfx_rapi->upload_texture(rgba32_buf, width, height); + if (gTextureUpscaling && (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT) { + xbrData = malloc(sizeof(xbr_data)); + xbr_init_data(xbrData); + + xbrParams.data = xbrData; + xbrParams.input = rgba32_buf; + xbrParams.output = rgba32_buf_out; + xbrParams.inWidth = width; + xbrParams.inHeight = height; + xbrParams.inPitch = width * 4; + xbrParams.outPitch = width * 4 * 4; + + switch (gTextureUpscaling) { + case 1: xbr_filter_hq4x(&xbrParams); break; + case 2: xbr_filter_xbr4x(&xbrParams); break; + } + + free(xbrData); + gfx_rapi->upload_texture(rgba32_buf_out, width*4, height*4); + } + + else if (gHUDUpscaling && (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) == G_TF_POINT) { + xbrData = malloc(sizeof(xbr_data)); + xbr_init_data(xbrData); + + xbrParams.data = xbrData; + xbrParams.input = rgba32_buf; + xbrParams.output = rgba32_buf_out; + xbrParams.inWidth = width; + xbrParams.inHeight = height; + xbrParams.inPitch = width * 4; + xbrParams.outPitch = width * 4 * 4; + + switch (gHUDUpscaling) { + case 1: xbr_filter_hq4x(&xbrParams); break; + case 2: xbr_filter_xbr4x(&xbrParams); break; + } + + free(xbrData); + gfx_rapi->upload_texture(rgba32_buf_out, width*4, height*4); + } + else { + gfx_rapi->upload_texture(rgba32_buf, width, height); + } } static void import_texture_rgba32(int tile) { @@ -568,6 +747,9 @@ static void gfx_sp_matrix(uint8_t parameters, const int32_t *addr) { if (parameters & G_MTX_PROJECTION) { if (parameters & G_MTX_LOAD) { + if(get_mirror()) { + matrix[0][0] *= -1; + } memcpy(rsp.P_matrix, matrix, sizeof(matrix)); } else { gfx_matrix_mul(rsp.P_matrix, matrix, rsp.P_matrix); @@ -735,10 +917,20 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { switch (rsp.geometry_mode & G_CULL_BOTH) { case G_CULL_FRONT: - if (cross <= 0) return; + if (get_mirror()) { + if (cross >= 0) return; + } + else { + if (cross <= 0) return; + } break; case G_CULL_BACK: - if (cross >= 0) return; + if (get_mirror()) { + if (cross <= 0) return; + } + else { + if (cross >= 0) return; + } break; case G_CULL_BOTH: // Why is this even an option? @@ -784,9 +976,9 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { uint32_t cc_id = rdp.combine_mode; bool use_alpha = (rdp.other_mode_l & (G_BL_A_MEM << 18)) == 0; - bool use_fog = (rdp.other_mode_l >> 30) == G_BL_CLR_FOG; + bool use_fog = (rdp.other_mode_l >> 30) == G_BL_CLR_FOG && !gDisableFog; bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; - bool use_noise = (rdp.other_mode_l & G_AC_DITHER) == G_AC_DITHER; + bool use_noise = (rdp.other_mode_l & G_AC_DITHER) == G_AC_DITHER && gNoiseType != 2; if (texture_edge) { use_alpha = true; @@ -825,7 +1017,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { import_texture(i); rdp.textures_changed[i] = false; } - bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT; + bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT || gHUDFiltering; if (linear_filter != rendering_state.textures[i]->linear_filter || rdp.texture_tile.cms != rendering_state.textures[i]->cms || rdp.texture_tile.cmt != rendering_state.textures[i]->cmt) { gfx_flush(); gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt); @@ -880,7 +1072,16 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { color = &rdp.prim_color; break; case CC_SHADE: - color = &v_arr[i]->color; + if (get_palette() == 19) { + tmp.r = (v_arr[i]->color.r+v_arr[i]->color.g+v_arr[i]->color.b)/3; + tmp.g = (v_arr[i]->color.r+v_arr[i]->color.g+v_arr[i]->color.b)/3; + tmp.b = (v_arr[i]->color.r+v_arr[i]->color.g+v_arr[i]->color.b)/3; + tmp.a = v_arr[i]->color.a; + color = &tmp; + } + else { + color = &v_arr[i]->color; + } break; case CC_ENV: color = &rdp.env_color; @@ -1171,9 +1372,25 @@ static void gfx_dp_set_prim_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { } static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - rdp.fog_color.r = r; - rdp.fog_color.g = g; - rdp.fog_color.b = b; + + switch (get_palette()) { + default: + rdp.fog_color.r = r; + rdp.fog_color.g = g; + rdp.fog_color.b = b; + break; + case 2: // bobomb + rdp.fog_color.r = 255; + rdp.fog_color.g = 255; + rdp.fog_color.b = 247; + break; + case 4: // jollyy + case 15: // clock + rdp.fog_color.r = 11; + rdp.fog_color.g = 3; + rdp.fog_color.b = 63; + break; + } rdp.fog_color.a = a; } diff --git a/src/pc/gfx/gfx_screen_config.h b/src/pc/gfx/gfx_screen_config.h index 5b933f8..c22a09d 100644 --- a/src/pc/gfx/gfx_screen_config.h +++ b/src/pc/gfx/gfx_screen_config.h @@ -1,7 +1,7 @@ #ifndef GFX_SCREEN_CONFIG_H #define GFX_SCREEN_CONFIG_H -#define DESIRED_SCREEN_WIDTH 640 -#define DESIRED_SCREEN_HEIGHT 480 +#define DESIRED_SCREEN_WIDTH gWindowWidth +#define DESIRED_SCREEN_HEIGHT gWindowHeight #endif diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c index 05f1a5a..0c7804a 100644 --- a/src/pc/gfx/gfx_sdl2.c +++ b/src/pc/gfx/gfx_sdl2.c @@ -20,19 +20,22 @@ #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" +#include "game/settings.h" + #define GFX_API_NAME "SDL2 - OpenGL" static SDL_Window *wnd; static int inverted_scancode_table[512]; static int vsync_enabled = 0; -static unsigned int window_width = DESIRED_SCREEN_WIDTH; -static unsigned int window_height = DESIRED_SCREEN_HEIGHT; + static bool fullscreen_state; static void (*on_fullscreen_changed_callback)(bool is_now_fullscreen); static bool (*on_key_down_callback)(int scancode); static bool (*on_key_up_callback)(int scancode); static void (*on_all_keys_up_callback)(void); +static Uint32 last_time; + const SDL_Scancode windows_scancode_table[] = { /* 0 1 2 3 4 5 6 7 */ @@ -85,6 +88,9 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = { }; static void set_fullscreen(bool on, bool call_callback) { + unsigned int window_width; + unsigned int window_height; + if (fullscreen_state == on) { return; } @@ -92,15 +98,39 @@ static void set_fullscreen(bool on, bool call_callback) { if (on) { SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(0, &mode); + if (gCustomFullscreenResolution) { + mode.format = SDL_PIXELFORMAT_ARGB8888; + mode.w = gFullscreenWidth; + mode.h = gFullscreenHeight; + mode.refresh_rate = gFullscreenRefreshRate; + mode.driverdata = 0; + SDL_SetWindowDisplayMode(wnd, &mode); + } + else { + SDL_GetDesktopDisplayMode(0, &mode); + } window_width = mode.w; window_height = mode.h; + + SDL_ShowCursor(false); } else { + if (gCustomFullscreenResolution) { + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(0, &mode); + SDL_SetWindowDisplayMode(wnd, &mode); + } window_width = DESIRED_SCREEN_WIDTH; window_height = DESIRED_SCREEN_HEIGHT; + + SDL_ShowCursor(true); } SDL_SetWindowSize(wnd, window_width, window_height); - SDL_SetWindowFullscreen(wnd, on ? SDL_WINDOW_FULLSCREEN : 0); + if (gCustomFullscreenResolution) { + SDL_SetWindowFullscreen(wnd, on ? SDL_WINDOW_FULLSCREEN : 0); + } + else { + SDL_SetWindowFullscreen(wnd, on ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + } if (on_fullscreen_changed_callback != NULL && call_callback) { on_fullscreen_changed_callback(on); @@ -160,10 +190,10 @@ static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen) { //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); char title[512]; - int len = sprintf(title, "%s (%s)", game_name, GFX_API_NAME); + sprintf(title, "%s (%s)", game_name, GFX_API_NAME); wnd = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); if (start_in_fullscreen) { set_fullscreen(true, false); @@ -211,8 +241,10 @@ static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { } static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) { - *width = window_width; - *height = window_height; + int w, h; + SDL_GetWindowSize(wnd, &w, &h); + *width = w; + *height = h; } static int translate_scancode(int scancode) { @@ -239,27 +271,23 @@ static void gfx_sdl_onkeyup(int scancode) { static void gfx_sdl_handle_events(void) { SDL_Event event; + Uint8 *state = SDL_GetKeyboardState(NULL); while (SDL_PollEvent(&event)) { switch (event.type) { #ifndef TARGET_WEB // Scancodes are broken in Emscripten SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3259 case SDL_KEYDOWN: - if (event.key.keysym.sym == SDLK_F10) { + if (state[SDL_SCANCODE_RETURN] && state[SDL_SCANCODE_LALT]) { set_fullscreen(!fullscreen_state, true); break; } + gfx_sdl_onkeydown(event.key.keysym.scancode); break; case SDL_KEYUP: gfx_sdl_onkeyup(event.key.keysym.scancode); break; #endif - case SDL_WINDOWEVENT: - if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { - window_width = event.window.data1; - window_height = event.window.data2; - } - break; case SDL_QUIT: exit(0); } @@ -267,18 +295,17 @@ static void gfx_sdl_handle_events(void) { } static bool gfx_sdl_start_frame(void) { + last_time = SDL_GetTicks(); return true; } static void sync_framerate_with_timer(void) { // Number of milliseconds a frame should take (30 fps) const Uint32 FRAME_TIME = 1000 / 30; - static Uint32 last_time; Uint32 elapsed = SDL_GetTicks() - last_time; if (elapsed < FRAME_TIME) SDL_Delay(FRAME_TIME - elapsed); - last_time += FRAME_TIME; } static void gfx_sdl_swap_buffers_begin(void) { diff --git a/src/pc/gfx/hq2x.c b/src/pc/gfx/hq2x.c new file mode 100644 index 0000000..ad8bf5a --- /dev/null +++ b/src/pc/gfx/hq2x.c @@ -0,0 +1,2807 @@ +/* + * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) + * + * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal_hqx_common.h" + +#define PIXEL00_0 *dp = w[5]; +#define PIXEL00_10 Interp1(dp, w[5], w[1]); +#define PIXEL00_11 Interp1(dp, w[5], w[4]); +#define PIXEL00_12 Interp1(dp, w[5], w[2]); +#define PIXEL00_20 Interp2(dp, w[5], w[4], w[2]); +#define PIXEL00_21 Interp2(dp, w[5], w[1], w[2]); +#define PIXEL00_22 Interp2(dp, w[5], w[1], w[4]); +#define PIXEL00_60 Interp6(dp, w[5], w[2], w[4]); +#define PIXEL00_61 Interp6(dp, w[5], w[4], w[2]); +#define PIXEL00_70 Interp7(dp, w[5], w[4], w[2]); +#define PIXEL00_90 Interp9(dp, w[5], w[4], w[2]); +#define PIXEL00_100 Interp10(dp, w[5], w[4], w[2]); +#define PIXEL01_0 *(dp+1) = w[5]; +#define PIXEL01_10 Interp1(dp+1, w[5], w[3]); +#define PIXEL01_11 Interp1(dp+1, w[5], w[2]); +#define PIXEL01_12 Interp1(dp+1, w[5], w[6]); +#define PIXEL01_20 Interp2(dp+1, w[5], w[2], w[6]); +#define PIXEL01_21 Interp2(dp+1, w[5], w[3], w[6]); +#define PIXEL01_22 Interp2(dp+1, w[5], w[3], w[2]); +#define PIXEL01_60 Interp6(dp+1, w[5], w[6], w[2]); +#define PIXEL01_61 Interp6(dp+1, w[5], w[2], w[6]); +#define PIXEL01_70 Interp7(dp+1, w[5], w[2], w[6]); +#define PIXEL01_90 Interp9(dp+1, w[5], w[2], w[6]); +#define PIXEL01_100 Interp10(dp+1, w[5], w[2], w[6]); +#define PIXEL10_0 *(dp+dpL) = w[5]; +#define PIXEL10_10 Interp1(dp+dpL, w[5], w[7]); +#define PIXEL10_11 Interp1(dp+dpL, w[5], w[8]); +#define PIXEL10_12 Interp1(dp+dpL, w[5], w[4]); +#define PIXEL10_20 Interp2(dp+dpL, w[5], w[8], w[4]); +#define PIXEL10_21 Interp2(dp+dpL, w[5], w[7], w[4]); +#define PIXEL10_22 Interp2(dp+dpL, w[5], w[7], w[8]); +#define PIXEL10_60 Interp6(dp+dpL, w[5], w[4], w[8]); +#define PIXEL10_61 Interp6(dp+dpL, w[5], w[8], w[4]); +#define PIXEL10_70 Interp7(dp+dpL, w[5], w[8], w[4]); +#define PIXEL10_90 Interp9(dp+dpL, w[5], w[8], w[4]); +#define PIXEL10_100 Interp10(dp+dpL, w[5], w[8], w[4]); +#define PIXEL11_0 *(dp+dpL+1) = w[5]; +#define PIXEL11_10 Interp1(dp+dpL+1, w[5], w[9]); +#define PIXEL11_11 Interp1(dp+dpL+1, w[5], w[6]); +#define PIXEL11_12 Interp1(dp+dpL+1, w[5], w[8]); +#define PIXEL11_20 Interp2(dp+dpL+1, w[5], w[6], w[8]); +#define PIXEL11_21 Interp2(dp+dpL+1, w[5], w[9], w[8]); +#define PIXEL11_22 Interp2(dp+dpL+1, w[5], w[9], w[6]); +#define PIXEL11_60 Interp6(dp+dpL+1, w[5], w[8], w[6]); +#define PIXEL11_61 Interp6(dp+dpL+1, w[5], w[6], w[8]); +#define PIXEL11_70 Interp7(dp+dpL+1, w[5], w[6], w[8]); +#define PIXEL11_90 Interp9(dp+dpL+1, w[5], w[6], w[8]); +#define PIXEL11_100 Interp10(dp+dpL+1, w[5], w[6], w[8]); + +static void hq2x_32_rb( const uint32_t * sp, uint32_t srb, uint32_t * dp, uint32_t drb, int Xres, int Yres, const xbr_data *data ) +{ + int i, j, k; + int prevline, nextline; + uint32_t w[10]; + int dpL = (drb >> 2); + int spL = (srb >> 2); + const uint8_t *sRowP = (const uint8_t *) sp; + uint8_t *dRowP = (uint8_t *) dp; + uint32_t yuv1, yuv2; + int pattern, flag; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (j=0; j0) prevline = -spL; else prevline = 0; + if (j0) + { + w[1] = *(sp + prevline - 1); + w[4] = *(sp - 1); + w[7] = *(sp + nextline - 1); + } + else + { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (iinput, ctx->inPitch, (uint32_t *)ctx->output, ctx->outPitch, ctx->inWidth, ctx->inHeight, ctx->data); +} + diff --git a/src/pc/gfx/hq3x.c b/src/pc/gfx/hq3x.c new file mode 100644 index 0000000..c0afc9a --- /dev/null +++ b/src/pc/gfx/hq3x.c @@ -0,0 +1,3785 @@ +/* + * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) + * + * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal_hqx_common.h" + +#define PIXEL00_1M Interp1(dp, w[5], w[1]); +#define PIXEL00_1U Interp1(dp, w[5], w[2]); +#define PIXEL00_1L Interp1(dp, w[5], w[4]); +#define PIXEL00_2 Interp2(dp, w[5], w[4], w[2]); +#define PIXEL00_4 Interp4(dp, w[5], w[4], w[2]); +#define PIXEL00_5 Interp5(dp, w[4], w[2]); +#define PIXEL00_C *dp = w[5]; + +#define PIXEL01_1 Interp1(dp+1, w[5], w[2]); +#define PIXEL01_3 Interp3(dp+1, w[5], w[2]); +#define PIXEL01_6 Interp1(dp+1, w[2], w[5]); +#define PIXEL01_C *(dp+1) = w[5]; + +#define PIXEL02_1M Interp1(dp+2, w[5], w[3]); +#define PIXEL02_1U Interp1(dp+2, w[5], w[2]); +#define PIXEL02_1R Interp1(dp+2, w[5], w[6]); +#define PIXEL02_2 Interp2(dp+2, w[5], w[2], w[6]); +#define PIXEL02_4 Interp4(dp+2, w[5], w[2], w[6]); +#define PIXEL02_5 Interp5(dp+2, w[2], w[6]); +#define PIXEL02_C *(dp+2) = w[5]; + +#define PIXEL10_1 Interp1(dp+dpL, w[5], w[4]); +#define PIXEL10_3 Interp3(dp+dpL, w[5], w[4]); +#define PIXEL10_6 Interp1(dp+dpL, w[4], w[5]); +#define PIXEL10_C *(dp+dpL) = w[5]; + +#define PIXEL11 *(dp+dpL+1) = w[5]; + +#define PIXEL12_1 Interp1(dp+dpL+2, w[5], w[6]); +#define PIXEL12_3 Interp3(dp+dpL+2, w[5], w[6]); +#define PIXEL12_6 Interp1(dp+dpL+2, w[6], w[5]); +#define PIXEL12_C *(dp+dpL+2) = w[5]; + +#define PIXEL20_1M Interp1(dp+dpL+dpL, w[5], w[7]); +#define PIXEL20_1D Interp1(dp+dpL+dpL, w[5], w[8]); +#define PIXEL20_1L Interp1(dp+dpL+dpL, w[5], w[4]); +#define PIXEL20_2 Interp2(dp+dpL+dpL, w[5], w[8], w[4]); +#define PIXEL20_4 Interp4(dp+dpL+dpL, w[5], w[8], w[4]); +#define PIXEL20_5 Interp5(dp+dpL+dpL, w[8], w[4]); +#define PIXEL20_C *(dp+dpL+dpL) = w[5]; + +#define PIXEL21_1 Interp1(dp+dpL+dpL+1, w[5], w[8]); +#define PIXEL21_3 Interp3(dp+dpL+dpL+1, w[5], w[8]); +#define PIXEL21_6 Interp1(dp+dpL+dpL+1, w[8], w[5]); +#define PIXEL21_C *(dp+dpL+dpL+1) = w[5]; + +#define PIXEL22_1M Interp1(dp+dpL+dpL+2, w[5], w[9]); +#define PIXEL22_1D Interp1(dp+dpL+dpL+2, w[5], w[8]); +#define PIXEL22_1R Interp1(dp+dpL+dpL+2, w[5], w[6]); +#define PIXEL22_2 Interp2(dp+dpL+dpL+2, w[5], w[6], w[8]); +#define PIXEL22_4 Interp4(dp+dpL+dpL+2, w[5], w[6], w[8]); +#define PIXEL22_5 Interp5(dp+dpL+dpL+2, w[6], w[8]); +#define PIXEL22_C *(dp+dpL+dpL+2) = w[5]; + +static void hq3x_32_rb( const uint32_t * sp, uint32_t srb, uint32_t * dp, uint32_t drb, int Xres, int Yres, const xbr_data *data ) +{ + int i, j, k; + int prevline, nextline; + uint32_t w[10]; + int dpL = (drb >> 2); + int spL = (srb >> 2); + const uint8_t *sRowP = (const uint8_t *) sp; + uint8_t *dRowP = (uint8_t *) dp; + uint32_t yuv1, yuv2; + int pattern, flag; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (j=0; j0) prevline = -spL; else prevline = 0; + if (j0) + { + w[1] = *(sp + prevline - 1); + w[4] = *(sp - 1); + w[7] = *(sp + nextline - 1); + } + else + { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (iinput, ctx->inPitch, (uint32_t *)ctx->output, ctx->outPitch, ctx->inWidth, ctx->inHeight, ctx->data); +} + diff --git a/src/pc/gfx/hq4x.c b/src/pc/gfx/hq4x.c new file mode 100644 index 0000000..c361908 --- /dev/null +++ b/src/pc/gfx/hq4x.c @@ -0,0 +1,5232 @@ +/* + * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) + * + * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal_hqx_common.h" + +#define PIXEL00_0 *dp = w[5]; +#define PIXEL00_11 Interp1(dp, w[5], w[4]); +#define PIXEL00_12 Interp1(dp, w[5], w[2]); +#define PIXEL00_20 Interp2(dp, w[5], w[2], w[4]); +#define PIXEL00_50 Interp5(dp, w[2], w[4]); +#define PIXEL00_80 Interp8(dp, w[5], w[1]); +#define PIXEL00_81 Interp8(dp, w[5], w[4]); +#define PIXEL00_82 Interp8(dp, w[5], w[2]); +#define PIXEL01_0 *(dp+1) = w[5]; +#define PIXEL01_10 Interp1(dp+1, w[5], w[1]); +#define PIXEL01_12 Interp1(dp+1, w[5], w[2]); +#define PIXEL01_14 Interp1(dp+1, w[2], w[5]); +#define PIXEL01_21 Interp2(dp+1, w[2], w[5], w[4]); +#define PIXEL01_31 Interp3(dp+1, w[5], w[4]); +#define PIXEL01_50 Interp5(dp+1, w[2], w[5]); +#define PIXEL01_60 Interp6(dp+1, w[5], w[2], w[4]); +#define PIXEL01_61 Interp6(dp+1, w[5], w[2], w[1]); +#define PIXEL01_82 Interp8(dp+1, w[5], w[2]); +#define PIXEL01_83 Interp8(dp+1, w[2], w[4]); +#define PIXEL02_0 *(dp+2) = w[5]; +#define PIXEL02_10 Interp1(dp+2, w[5], w[3]); +#define PIXEL02_11 Interp1(dp+2, w[5], w[2]); +#define PIXEL02_13 Interp1(dp+2, w[2], w[5]); +#define PIXEL02_21 Interp2(dp+2, w[2], w[5], w[6]); +#define PIXEL02_32 Interp3(dp+2, w[5], w[6]); +#define PIXEL02_50 Interp5(dp+2, w[2], w[5]); +#define PIXEL02_60 Interp6(dp+2, w[5], w[2], w[6]); +#define PIXEL02_61 Interp6(dp+2, w[5], w[2], w[3]); +#define PIXEL02_81 Interp8(dp+2, w[5], w[2]); +#define PIXEL02_83 Interp8(dp+2, w[2], w[6]); +#define PIXEL03_0 *(dp+3) = w[5]; +#define PIXEL03_11 Interp1(dp+3, w[5], w[2]); +#define PIXEL03_12 Interp1(dp+3, w[5], w[6]); +#define PIXEL03_20 Interp2(dp+3, w[5], w[2], w[6]); +#define PIXEL03_50 Interp5(dp+3, w[2], w[6]); +#define PIXEL03_80 Interp8(dp+3, w[5], w[3]); +#define PIXEL03_81 Interp8(dp+3, w[5], w[2]); +#define PIXEL03_82 Interp8(dp+3, w[5], w[6]); +#define PIXEL10_0 *(dp+dpL) = w[5]; +#define PIXEL10_10 Interp1(dp+dpL, w[5], w[1]); +#define PIXEL10_11 Interp1(dp+dpL, w[5], w[4]); +#define PIXEL10_13 Interp1(dp+dpL, w[4], w[5]); +#define PIXEL10_21 Interp2(dp+dpL, w[4], w[5], w[2]); +#define PIXEL10_32 Interp3(dp+dpL, w[5], w[2]); +#define PIXEL10_50 Interp5(dp+dpL, w[4], w[5]); +#define PIXEL10_60 Interp6(dp+dpL, w[5], w[4], w[2]); +#define PIXEL10_61 Interp6(dp+dpL, w[5], w[4], w[1]); +#define PIXEL10_81 Interp8(dp+dpL, w[5], w[4]); +#define PIXEL10_83 Interp8(dp+dpL, w[4], w[2]); +#define PIXEL11_0 *(dp+dpL+1) = w[5]; +#define PIXEL11_30 Interp3(dp+dpL+1, w[5], w[1]); +#define PIXEL11_31 Interp3(dp+dpL+1, w[5], w[4]); +#define PIXEL11_32 Interp3(dp+dpL+1, w[5], w[2]); +#define PIXEL11_70 Interp7(dp+dpL+1, w[5], w[4], w[2]); +#define PIXEL12_0 *(dp+dpL+2) = w[5]; +#define PIXEL12_30 Interp3(dp+dpL+2, w[5], w[3]); +#define PIXEL12_31 Interp3(dp+dpL+2, w[5], w[2]); +#define PIXEL12_32 Interp3(dp+dpL+2, w[5], w[6]); +#define PIXEL12_70 Interp7(dp+dpL+2, w[5], w[6], w[2]); +#define PIXEL13_0 *(dp+dpL+3) = w[5]; +#define PIXEL13_10 Interp1(dp+dpL+3, w[5], w[3]); +#define PIXEL13_12 Interp1(dp+dpL+3, w[5], w[6]); +#define PIXEL13_14 Interp1(dp+dpL+3, w[6], w[5]); +#define PIXEL13_21 Interp2(dp+dpL+3, w[6], w[5], w[2]); +#define PIXEL13_31 Interp3(dp+dpL+3, w[5], w[2]); +#define PIXEL13_50 Interp5(dp+dpL+3, w[6], w[5]); +#define PIXEL13_60 Interp6(dp+dpL+3, w[5], w[6], w[2]); +#define PIXEL13_61 Interp6(dp+dpL+3, w[5], w[6], w[3]); +#define PIXEL13_82 Interp8(dp+dpL+3, w[5], w[6]); +#define PIXEL13_83 Interp8(dp+dpL+3, w[6], w[2]); +#define PIXEL20_0 *(dp+dpL+dpL) = w[5]; +#define PIXEL20_10 Interp1(dp+dpL+dpL, w[5], w[7]); +#define PIXEL20_12 Interp1(dp+dpL+dpL, w[5], w[4]); +#define PIXEL20_14 Interp1(dp+dpL+dpL, w[4], w[5]); +#define PIXEL20_21 Interp2(dp+dpL+dpL, w[4], w[5], w[8]); +#define PIXEL20_31 Interp3(dp+dpL+dpL, w[5], w[8]); +#define PIXEL20_50 Interp5(dp+dpL+dpL, w[4], w[5]); +#define PIXEL20_60 Interp6(dp+dpL+dpL, w[5], w[4], w[8]); +#define PIXEL20_61 Interp6(dp+dpL+dpL, w[5], w[4], w[7]); +#define PIXEL20_82 Interp8(dp+dpL+dpL, w[5], w[4]); +#define PIXEL20_83 Interp8(dp+dpL+dpL, w[4], w[8]); +#define PIXEL21_0 *(dp+dpL+dpL+1) = w[5]; +#define PIXEL21_30 Interp3(dp+dpL+dpL+1, w[5], w[7]); +#define PIXEL21_31 Interp3(dp+dpL+dpL+1, w[5], w[8]); +#define PIXEL21_32 Interp3(dp+dpL+dpL+1, w[5], w[4]); +#define PIXEL21_70 Interp7(dp+dpL+dpL+1, w[5], w[4], w[8]); +#define PIXEL22_0 *(dp+dpL+dpL+2) = w[5]; +#define PIXEL22_30 Interp3(dp+dpL+dpL+2, w[5], w[9]); +#define PIXEL22_31 Interp3(dp+dpL+dpL+2, w[5], w[6]); +#define PIXEL22_32 Interp3(dp+dpL+dpL+2, w[5], w[8]); +#define PIXEL22_70 Interp7(dp+dpL+dpL+2, w[5], w[6], w[8]); +#define PIXEL23_0 *(dp+dpL+dpL+3) = w[5]; +#define PIXEL23_10 Interp1(dp+dpL+dpL+3, w[5], w[9]); +#define PIXEL23_11 Interp1(dp+dpL+dpL+3, w[5], w[6]); +#define PIXEL23_13 Interp1(dp+dpL+dpL+3, w[6], w[5]); +#define PIXEL23_21 Interp2(dp+dpL+dpL+3, w[6], w[5], w[8]); +#define PIXEL23_32 Interp3(dp+dpL+dpL+3, w[5], w[8]); +#define PIXEL23_50 Interp5(dp+dpL+dpL+3, w[6], w[5]); +#define PIXEL23_60 Interp6(dp+dpL+dpL+3, w[5], w[6], w[8]); +#define PIXEL23_61 Interp6(dp+dpL+dpL+3, w[5], w[6], w[9]); +#define PIXEL23_81 Interp8(dp+dpL+dpL+3, w[5], w[6]); +#define PIXEL23_83 Interp8(dp+dpL+dpL+3, w[6], w[8]); +#define PIXEL30_0 *(dp+dpL+dpL+dpL) = w[5]; +#define PIXEL30_11 Interp1(dp+dpL+dpL+dpL, w[5], w[8]); +#define PIXEL30_12 Interp1(dp+dpL+dpL+dpL, w[5], w[4]); +#define PIXEL30_20 Interp2(dp+dpL+dpL+dpL, w[5], w[8], w[4]); +#define PIXEL30_50 Interp5(dp+dpL+dpL+dpL, w[8], w[4]); +#define PIXEL30_80 Interp8(dp+dpL+dpL+dpL, w[5], w[7]); +#define PIXEL30_81 Interp8(dp+dpL+dpL+dpL, w[5], w[8]); +#define PIXEL30_82 Interp8(dp+dpL+dpL+dpL, w[5], w[4]); +#define PIXEL31_0 *(dp+dpL+dpL+dpL+1) = w[5]; +#define PIXEL31_10 Interp1(dp+dpL+dpL+dpL+1, w[5], w[7]); +#define PIXEL31_11 Interp1(dp+dpL+dpL+dpL+1, w[5], w[8]); +#define PIXEL31_13 Interp1(dp+dpL+dpL+dpL+1, w[8], w[5]); +#define PIXEL31_21 Interp2(dp+dpL+dpL+dpL+1, w[8], w[5], w[4]); +#define PIXEL31_32 Interp3(dp+dpL+dpL+dpL+1, w[5], w[4]); +#define PIXEL31_50 Interp5(dp+dpL+dpL+dpL+1, w[8], w[5]); +#define PIXEL31_60 Interp6(dp+dpL+dpL+dpL+1, w[5], w[8], w[4]); +#define PIXEL31_61 Interp6(dp+dpL+dpL+dpL+1, w[5], w[8], w[7]); +#define PIXEL31_81 Interp8(dp+dpL+dpL+dpL+1, w[5], w[8]); +#define PIXEL31_83 Interp8(dp+dpL+dpL+dpL+1, w[8], w[4]); +#define PIXEL32_0 *(dp+dpL+dpL+dpL+2) = w[5]; +#define PIXEL32_10 Interp1(dp+dpL+dpL+dpL+2, w[5], w[9]); +#define PIXEL32_12 Interp1(dp+dpL+dpL+dpL+2, w[5], w[8]); +#define PIXEL32_14 Interp1(dp+dpL+dpL+dpL+2, w[8], w[5]); +#define PIXEL32_21 Interp2(dp+dpL+dpL+dpL+2, w[8], w[5], w[6]); +#define PIXEL32_31 Interp3(dp+dpL+dpL+dpL+2, w[5], w[6]); +#define PIXEL32_50 Interp5(dp+dpL+dpL+dpL+2, w[8], w[5]); +#define PIXEL32_60 Interp6(dp+dpL+dpL+dpL+2, w[5], w[8], w[6]); +#define PIXEL32_61 Interp6(dp+dpL+dpL+dpL+2, w[5], w[8], w[9]); +#define PIXEL32_82 Interp8(dp+dpL+dpL+dpL+2, w[5], w[8]); +#define PIXEL32_83 Interp8(dp+dpL+dpL+dpL+2, w[8], w[6]); +#define PIXEL33_0 *(dp+dpL+dpL+dpL+3) = w[5]; +#define PIXEL33_11 Interp1(dp+dpL+dpL+dpL+3, w[5], w[6]); +#define PIXEL33_12 Interp1(dp+dpL+dpL+dpL+3, w[5], w[8]); +#define PIXEL33_20 Interp2(dp+dpL+dpL+dpL+3, w[5], w[8], w[6]); +#define PIXEL33_50 Interp5(dp+dpL+dpL+dpL+3, w[8], w[6]); +#define PIXEL33_80 Interp8(dp+dpL+dpL+dpL+3, w[5], w[9]); +#define PIXEL33_81 Interp8(dp+dpL+dpL+dpL+3, w[5], w[6]); +#define PIXEL33_82 Interp8(dp+dpL+dpL+dpL+3, w[5], w[8]); + +static void hq4x_32_rb( const uint32_t * sp, uint32_t srb, uint32_t * dp, uint32_t drb, int Xres, int Yres, const xbr_data *data ) +{ + int i, j, k; + int prevline, nextline; + uint32_t w[10]; + int dpL = (drb >> 2); + int spL = (srb >> 2); + const uint8_t *sRowP = (const uint8_t *) sp; + uint8_t *dRowP = (uint8_t *) dp; + uint32_t yuv1, yuv2; + int pattern, flag; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (j=0; j0) prevline = -spL; else prevline = 0; + if (j0) + { + w[1] = *(sp + prevline - 1); + w[4] = *(sp - 1); + w[7] = *(sp + nextline - 1); + } + else + { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (iinput, ctx->inPitch, (uint32_t *)ctx->output, ctx->outPitch, ctx->inWidth, ctx->inHeight, ctx->data); +} + + diff --git a/src/pc/gfx/internal_hqx_common.h b/src/pc/gfx/internal_hqx_common.h new file mode 100644 index 0000000..e4f35d7 --- /dev/null +++ b/src/pc/gfx/internal_hqx_common.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) + * + * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) + * Copyright (C) 2011 Francois Gannaz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __INTERNAL_HQX_COMMON_H_ +#define __INTERNAL_HQX_COMMON_H_ + +#include +#include + +#define XBR_INTERNAL +#include "filters.h" + +#define MASK_2 0x0000FF00 +#define MASK_13 0x00FF00FF +#define MASK_RGB 0x00FFFFFF +#define MASK_ALPHA 0xFF000000 + +#define Ymask 0x00FF0000 +#define Umask 0x0000FF00 +#define Vmask 0x000000FF +#define trY 0x00300000 +#define trU 0x00000700 +#define trV 0x00000006 + +/* RGB to YUV lookup table */ +static XBR_INLINE uint32_t rgb_to_yuv(const xbr_data *data, uint32_t c) +{ + // Mask against MASK_RGB to discard the alpha channel + return data->rgbtoyuv[MASK_RGB & c]; +} + +/* Test if there is difference in color */ +static XBR_INLINE int yuv_diff(uint32_t yuv1, uint32_t yuv2) { + return (( abs((yuv1 & Ymask) - (yuv2 & Ymask)) > trY ) || + ( abs((yuv1 & Umask) - (yuv2 & Umask)) > trU ) || + ( abs((yuv1 & Vmask) - (yuv2 & Vmask)) > trV ) ); +} + +static XBR_INLINE int Diff(const xbr_data *data, uint32_t c1, uint32_t c2) +{ + return yuv_diff(rgb_to_yuv(data, c1), rgb_to_yuv(data, c2)); +} + +/* Interpolate functions */ +static XBR_INLINE uint32_t Interpolate_2(uint32_t c1, int w1, uint32_t c2, int w2, int s) +{ + if (c1 == c2) { + return c1; + } + return + (((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2) << (24-s)) & MASK_ALPHA) + + ((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2) >> s) & MASK_2) + + ((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2) >> s) & MASK_13); +} + +static XBR_INLINE uint32_t Interpolate_3(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s) +{ + return + (((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2 + ((c3 & MASK_ALPHA) >> 24) * w3) << (24-s)) & MASK_ALPHA) + + ((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2 + (c3 & MASK_2) * w3) >> s) & MASK_2) + + ((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2 + (c3 & MASK_13) * w3) >> s) & MASK_13); +} + +static XBR_INLINE void Interp1(uint32_t * pc, uint32_t c1, uint32_t c2) +{ + //*pc = (c1*3+c2) >> 2; + *pc = Interpolate_2(c1, 3, c2, 1, 2); +} + +static XBR_INLINE void Interp2(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) +{ + //*pc = (c1*2+c2+c3) >> 2; + *pc = Interpolate_3(c1, 2, c2, 1, c3, 1, 2); +} + +static XBR_INLINE void Interp3(uint32_t * pc, uint32_t c1, uint32_t c2) +{ + //*pc = (c1*7+c2)/8; + *pc = Interpolate_2(c1, 7, c2, 1, 3); +} + +static XBR_INLINE void Interp4(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) +{ + //*pc = (c1*2+(c2+c3)*7)/16; + *pc = Interpolate_3(c1, 2, c2, 7, c3, 7, 4); +} + +static XBR_INLINE void Interp5(uint32_t * pc, uint32_t c1, uint32_t c2) +{ + //*pc = (c1+c2) >> 1; + *pc = Interpolate_2(c1, 1, c2, 1, 1); +} + +static XBR_INLINE void Interp6(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) +{ + //*pc = (c1*5+c2*2+c3)/8; + *pc = Interpolate_3(c1, 5, c2, 2, c3, 1, 3); +} + +static XBR_INLINE void Interp7(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) +{ + //*pc = (c1*6+c2+c3)/8; + *pc = Interpolate_3(c1, 6, c2, 1, c3, 1, 3); +} + +static XBR_INLINE void Interp8(uint32_t * pc, uint32_t c1, uint32_t c2) +{ + //*pc = (c1*5+c2*3)/8; + *pc = Interpolate_2(c1, 5, c2, 3, 3); +} + +static XBR_INLINE void Interp9(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) +{ + //*pc = (c1*2+(c2+c3)*3)/8; + *pc = Interpolate_3(c1, 2, c2, 3, c3, 3, 3); +} + +static XBR_INLINE void Interp10(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) +{ + //*pc = (c1*14+c2+c3)/16; + *pc = Interpolate_3(c1, 14, c2, 1, c3, 1, 4); +} + +#endif diff --git a/src/pc/gfx/xbr.c b/src/pc/gfx/xbr.c new file mode 100644 index 0000000..ac3782e --- /dev/null +++ b/src/pc/gfx/xbr.c @@ -0,0 +1,337 @@ +/* + * XBR filter extracted from FFmpeg into a separate library. + * + * + * Copyright (c) 2011, 2012 Hyllian/Jararaca + * Copyright (c) 2014 Arwa Arif + * Copyright (c) 2015 Treeki + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * XBR Filter is used for depixelization of image. + * This is based on Hyllian's xBR shader. + * + * @see http://www.libretro.com/forums/viewtopic.php?f=6&t=134 + * @see https://github.com/yoyofr/iFBA/blob/master/fba_src/src/intf/video/scalers/xbr.cpp + */ + +#define XBR_INTERNAL +#include "filters.h" +#include + +#define LB_MASK 0x00FEFEFE +#define RED_BLUE_MASK 0x00FF00FF +#define GREEN_MASK 0x0000FF00 +#define PART_MASK 0x00FF00FF + +static uint32_t pixel_diff(uint32_t x, uint32_t y, const uint32_t *r2y) +{ +#define YMASK 0xff0000 +#define UMASK 0x00ff00 +#define VMASK 0x0000ff + + uint32_t yuv1 = r2y[x & 0xffffff]; + uint32_t yuv2 = r2y[y & 0xffffff]; + + return (abs((x >> 24) - (y >> 24))) + + (abs((yuv1 & YMASK) - (yuv2 & YMASK)) >> 16) + + (abs((yuv1 & UMASK) - (yuv2 & UMASK)) >> 8) + + abs((yuv1 & VMASK) - (yuv2 & VMASK)); +} + +#define ALPHA_BLEND_BASE(a, b, m, s) ( (PART_MASK & (((a) & PART_MASK) + (((((b) & PART_MASK) - ((a) & PART_MASK)) * (m)) >> (s)))) \ + | ((PART_MASK & ((((a) >> 8) & PART_MASK) + ((((((b) >> 8) & PART_MASK) - (((a) >> 8) & PART_MASK)) * (m)) >> (s)))) << 8)) + +#define ALPHA_BLEND_32_W(a, b) ALPHA_BLEND_BASE(a, b, 1, 3) +#define ALPHA_BLEND_64_W(a, b) ALPHA_BLEND_BASE(a, b, 1, 2) +#define ALPHA_BLEND_128_W(a, b) ALPHA_BLEND_BASE(a, b, 1, 1) +#define ALPHA_BLEND_192_W(a, b) ALPHA_BLEND_BASE(a, b, 3, 2) +#define ALPHA_BLEND_224_W(a, b) ALPHA_BLEND_BASE(a, b, 7, 3) + + + +#define df(A, B) pixel_diff(A, B, r2y) +#define eq(A, B) (df(A, B) < 155) + +#define FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \ + N0, N1, N2, N3) do { \ + if (PE != PH && PE != PF) { \ + const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \ + const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \ + if (e <= i) { \ + const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \ + if (e < i && (!eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) \ + && (!eq(PF,I4) && !eq(PH,I5)) \ + || eq(PE,PG) || eq(PE,PC))) { \ + const unsigned ke = df(PF,PG); \ + const unsigned ki = df(PH,PC); \ + const int left = ke<<1 <= ki && PE != PG && PD != PG; \ + const int up = ke >= ki<<1 && PE != PC && PB != PC; \ + if (left && up) { \ + E[N3] = ALPHA_BLEND_224_W(E[N3], px); \ + E[N2] = ALPHA_BLEND_64_W( E[N2], px); \ + E[N1] = E[N2]; \ + } else if (left) { \ + E[N3] = ALPHA_BLEND_192_W(E[N3], px); \ + E[N2] = ALPHA_BLEND_64_W( E[N2], px); \ + } else if (up) { \ + E[N3] = ALPHA_BLEND_192_W(E[N3], px); \ + E[N1] = ALPHA_BLEND_64_W( E[N1], px); \ + } else { /* diagonal */ \ + E[N3] = ALPHA_BLEND_128_W(E[N3], px); \ + } \ + } else { \ + E[N3] = ALPHA_BLEND_128_W(E[N3], px); \ + } \ + } \ + } \ +} while (0) + +#define FILT3(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \ + N0, N1, N2, N3, N4, N5, N6, N7, N8) do { \ + if (PE != PH && PE != PF) { \ + const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \ + const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \ + if (e <= i) { \ + const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \ + if (e < i && (!eq(PF,PB) && !eq(PF,PC) || !eq(PH,PD) && !eq(PH,PG) || eq(PE,PI) \ + && (!eq(PF,F4) && !eq(PF,I4) || !eq(PH,H5) && !eq(PH,I5)) \ + || eq(PE,PG) || eq(PE,PC))) { \ + const unsigned ke = df(PF,PG); \ + const unsigned ki = df(PH,PC); \ + const int left = ke<<1 <= ki && PE != PG && PD != PG; \ + const int up = ke >= ki<<1 && PE != PC && PB != PC; \ + if (left && up) { \ + E[N7] = ALPHA_BLEND_192_W(E[N7], px); \ + E[N6] = ALPHA_BLEND_64_W( E[N6], px); \ + E[N5] = E[N7]; \ + E[N2] = E[N6]; \ + E[N8] = px; \ + } else if (left) { \ + E[N7] = ALPHA_BLEND_192_W(E[N7], px); \ + E[N5] = ALPHA_BLEND_64_W( E[N5], px); \ + E[N6] = ALPHA_BLEND_64_W( E[N6], px); \ + E[N8] = px; \ + } else if (up) { \ + E[N5] = ALPHA_BLEND_192_W(E[N5], px); \ + E[N7] = ALPHA_BLEND_64_W( E[N7], px); \ + E[N2] = ALPHA_BLEND_64_W( E[N2], px); \ + E[N8] = px; \ + } else { /* diagonal */ \ + E[N8] = ALPHA_BLEND_224_W(E[N8], px); \ + E[N5] = ALPHA_BLEND_32_W( E[N5], px); \ + E[N7] = ALPHA_BLEND_32_W( E[N7], px); \ + } \ + } else { \ + E[N8] = ALPHA_BLEND_128_W(E[N8], px); \ + } \ + } \ + } \ +} while (0) + +#define FILT4(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, \ + N15, N14, N11, N3, N7, N10, N13, N12, N9, N6, N2, N1, N5, N8, N4, N0) do { \ + if (PE != PH && PE != PF) { \ + const unsigned e = df(PE,PC) + df(PE,PG) + df(PI,H5) + df(PI,F4) + (df(PH,PF)<<2); \ + const unsigned i = df(PH,PD) + df(PH,I5) + df(PF,I4) + df(PF,PB) + (df(PE,PI)<<2); \ + if (e <= i) { \ + const unsigned px = df(PE,PF) <= df(PE,PH) ? PF : PH; \ + if (e < i && (!eq(PF,PB) && !eq(PH,PD) || eq(PE,PI) \ + && (!eq(PF,I4) && !eq(PH,I5)) \ + || eq(PE,PG) || eq(PE,PC))) { \ + const unsigned ke = df(PF,PG); \ + const unsigned ki = df(PH,PC); \ + const int left = ke<<1 <= ki && PE != PG && PD != PG; \ + const int up = ke >= ki<<1 && PE != PC && PB != PC; \ + if (left && up) { \ + E[N13] = ALPHA_BLEND_192_W(E[N13], px); \ + E[N12] = ALPHA_BLEND_64_W( E[N12], px); \ + E[N15] = E[N14] = E[N11] = px; \ + E[N10] = E[N3] = E[N12]; \ + E[N7] = E[N13]; \ + } else if (left) { \ + E[N11] = ALPHA_BLEND_192_W(E[N11], px); \ + E[N13] = ALPHA_BLEND_192_W(E[N13], px); \ + E[N10] = ALPHA_BLEND_64_W( E[N10], px); \ + E[N12] = ALPHA_BLEND_64_W( E[N12], px); \ + E[N14] = px; \ + E[N15] = px; \ + } else if (up) { \ + E[N14] = ALPHA_BLEND_192_W(E[N14], px); \ + E[N7 ] = ALPHA_BLEND_192_W(E[N7 ], px); \ + E[N10] = ALPHA_BLEND_64_W( E[N10], px); \ + E[N3 ] = ALPHA_BLEND_64_W( E[N3 ], px); \ + E[N11] = px; \ + E[N15] = px; \ + } else { /* diagonal */ \ + E[N11] = ALPHA_BLEND_128_W(E[N11], px); \ + E[N14] = ALPHA_BLEND_128_W(E[N14], px); \ + E[N15] = px; \ + } \ + } else { \ + E[N15] = ALPHA_BLEND_128_W(E[N15], px); \ + } \ + } \ + } \ +} while (0) + +static XBR_INLINE void xbr_filter(const xbr_params *params, int n) +{ + int x, y; + const uint32_t *r2y = params->data->rgbtoyuv; + const int nl = params->outPitch >> 2; + const int nl1 = nl + nl; + const int nl2 = nl1 + nl; + + for (y = 0; y < params->inHeight; y++) { + + uint32_t *E = (uint32_t *)(params->output + y * params->outPitch * n); + const uint32_t *sa2 = (uint32_t *)(params->input + y * params->inPitch - 8); /* center */ + const uint32_t *sa1 = sa2 - (params->inPitch>>2); /* up x1 */ + const uint32_t *sa0 = sa1 - (params->inPitch>>2); /* up x2 */ + const uint32_t *sa3 = sa2 + (params->inPitch>>2); /* down x1 */ + const uint32_t *sa4 = sa3 + (params->inPitch>>2); /* down x2 */ + + if (y <= 1) { + sa0 = sa1; + if (y == 0) { + sa0 = sa1 = sa2; + } + } + + if (y >= params->inHeight - 2) { + sa4 = sa3; + if (y == params->inHeight - 1) { + sa4 = sa3 = sa2; + } + } + + for (x = 0; x < params->inWidth; x++) { + const uint32_t B1 = sa0[2]; + const uint32_t PB = sa1[2]; + const uint32_t PE = sa2[2]; + const uint32_t PH = sa3[2]; + const uint32_t H5 = sa4[2]; + + const int pprev = 2 - (x > 0); + const uint32_t A1 = sa0[pprev]; + const uint32_t PA = sa1[pprev]; + const uint32_t PD = sa2[pprev]; + const uint32_t PG = sa3[pprev]; + const uint32_t G5 = sa4[pprev]; + + const int pprev2 = pprev - (x > 1); + const uint32_t A0 = sa1[pprev2]; + const uint32_t D0 = sa2[pprev2]; + const uint32_t G0 = sa3[pprev2]; + + const int pnext = 3 - (x == params->inWidth - 1); + const uint32_t C1 = sa0[pnext]; + const uint32_t PC = sa1[pnext]; + const uint32_t PF = sa2[pnext]; + const uint32_t PI = sa3[pnext]; + const uint32_t I5 = sa4[pnext]; + + const int pnext2 = pnext + 1 - (x >= params->inWidth - 2); + const uint32_t C4 = sa1[pnext2]; + const uint32_t F4 = sa2[pnext2]; + const uint32_t I4 = sa3[pnext2]; + + if (n == 2) { + E[0] = E[1] = // 0, 1 + E[nl] = E[nl + 1] = PE; // 2, 3 + + FILT2(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, 0, 1, nl, nl+1); + FILT2(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, nl, 0, nl+1, 1); + FILT2(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, nl+1, nl, 1, 0); + FILT2(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, 1, nl+1, 0, nl); + } else if (n == 3) { + E[0] = E[1] = E[2] = // 0, 1, 2 + E[nl] = E[nl+1] = E[nl+2] = // 3, 4, 5 + E[nl1] = E[nl1+1] = E[nl1+2] = PE; // 6, 7, 8 + + FILT3(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, 0, 1, 2, nl, nl+1, nl+2, nl1, nl1+1, nl1+2); + FILT3(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, nl1, nl, 0, nl1+1, nl+1, 1, nl1+2, nl+2, 2); + FILT3(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, nl1+2, nl1+1, nl1, nl+2, nl+1, nl, 2, 1, 0); + FILT3(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, 2, nl+2, nl1+2, 1, nl+1, nl1+1, 0, nl, nl1); + } else if (n == 4) { + E[0] = E[1] = E[2] = E[3] = // 0, 1, 2, 3 + E[nl] = E[nl+1] = E[nl+2] = E[nl+3] = // 4, 5, 6, 7 + E[nl1] = E[nl1+1] = E[nl1+2] = E[nl1+3] = // 8, 9, 10, 11 + E[nl2] = E[nl2+1] = E[nl2+2] = E[nl2+3] = PE; // 12, 13, 14, 15 + + FILT4(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, nl2+3, nl2+2, nl1+3, 3, nl+3, nl1+2, nl2+1, nl2, nl1+1, nl+2, 2, 1, nl+1, nl1, nl, 0); + FILT4(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, 3, nl+3, 2, 0, 1, nl+2, nl1+3, nl2+3, nl1+2, nl+1, nl, nl1, nl1+1, nl2+2, nl2+1, nl2); + FILT4(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, 0, 1, nl, nl2, nl1, nl+1, 2, 3, nl+2, nl1+1, nl2+1, nl2+2, nl1+2, nl+3, nl1+3, nl2+3); + FILT4(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, nl2, nl1, nl2+1, nl2+3, nl2+2, nl1+1, nl, 0, nl+1, nl1+2, nl1+3, nl+3, nl+2, 1, 2, 3); + } + + sa0 += 1; + sa1 += 1; + sa2 += 1; + sa3 += 1; + sa4 += 1; + + E += n; + } + } +} + +#define XBR_FUNC(size) \ +void xbr_filter_xbr##size##x(const xbr_params *params) \ +{ \ + xbr_filter(params, size); \ +} + +XBR_FUNC(2) +XBR_FUNC(3) +XBR_FUNC(4) + + +static XBR_INLINE int _max(int a, int b) +{ + return (a > b) ? a : b; +} + +static XBR_INLINE int _min(int a, int b) +{ + return (a < b) ? a : b; +} + + +void xbr_init_data(xbr_data *data) +{ + uint32_t c; + int bg, rg, g; + + for (bg = -255; bg < 256; bg++) { + for (rg = -255; rg < 256; rg++) { + const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128; + const uint32_t v = (uint32_t)(( 500*rg - 81*bg)/1000) + 128; + int startg = _max(-bg, _max(-rg, 0)); + int endg = _min(255-bg, _min(255-rg, 255)); + uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000); + c = bg + (rg<<16) + 0x010101 * startg; + for (g = startg; g <= endg; g++) { + data->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v; + c+= 0x010101; + } + } + } +} + diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 4fb25fa..ae5e837 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -27,7 +27,7 @@ #include "controller/controller_keyboard.h" -#include "configfile.h" +#include "game/settings.h" #define CONFIG_FILE "sm64config.txt" diff --git a/text/us/dialogs.h b/text/us/dialogs.h index 34a18b2..695ff63 100644 --- a/text/us/dialogs.h +++ b/text/us/dialogs.h @@ -2100,3 +2100,29 @@ Arrgghh!\n\ Anyone entering this cave\n\ without permission will\n\ meet certain disaster.")) + +DEFINE_DIALOG(DIALOG_170, 1, 5, 30, 200, _("\ +Wow! Another Power Star!\n\ +Do you want to keep\n\ +playing this level?\n\ +\n\ +//You Bet//Not Now")) + +DEFINE_DIALOG(DIALOG_171, 1, 4, 30, 200, _("\ +Hey-ey, Mario, buddy,\n\ +howzit goin'?\n\ +Ok, so, basically, I am\n\ +very smol.\n\ +How about a race?\n\ +Ready...\n\ +\n\ +//Go//// Don't Go")) + +DEFINE_DIALOG(DIALOG_172, 1, 4, 30, 200, _("\ +Mario! What's up, pal?\n\ +Ok, so, basically, I am\n\ +even smoller.\n\ +How about a race?\n\ +Ready...set...\n\ +\n\ +//Go//// Don't Go")) \ No newline at end of file diff --git a/tools/asm/crash.s b/tools/asm/crash.s new file mode 100644 index 0000000..0b2a557 --- /dev/null +++ b/tools/asm/crash.s @@ -0,0 +1,153 @@ +# SM64 Crash Handler +# See Readme below. + +.include "macros.inc" + +/* --------------------------------------------------------------- + * IMPORTANT README: + * --------------------------------------------------------------- + * Frame buffer emulation is required. To enable it in GlideN64, + * check "Emulate frame buffer" and "Render frame buffer to output" + * in the "Frame buffer" tab. + * + * Your emulator's CPU core style should be set to interpreter for best results. + * + * See the DEBUG_ASSERT macro on how to call the crash screen for + * detected exceptions. + * + */ + +.set noat +.set noreorder +.set gp=64 + +.set COP0_CAUSE, $13 +.set COP0_EPC, $14 +.set COP0_BADVADDR, $8 + +glabel crashFont + .incbin "enhancements/crash_font.bin" + .align 4 + +glabel exceptionRegContext + .fill 0x108 + +glabel pAssertFile + .dword 0 +glabel nAssertLine + .dword 0 +glabel pAssertExpression + .dword 0 +glabel nAssertStopProgram + .dword 0 + +glabel _n64_assert + lui $at, %hi(pAssertFile) + sw $a0, %lo(pAssertFile)($at) + lui $at, %hi(nAssertLine) + sw $a1, %lo(nAssertLine)($at) + lui $at, %hi(pAssertExpression) + sw $a2, %lo(pAssertExpression)($at) + lui $at, %hi(nAssertStopProgram) + sw $a3, %lo(nAssertStopProgram)($at) + beqz $a3, .end_2 + nop + syscall # trigger crash screen +.end_2: + jr $ra + nop + +glabel cop0_get_cause + jr $ra + mfc0 $v0, COP0_CAUSE + +glabel cop0_get_epc + jr $ra + mfc0 $v0, COP0_EPC + +glabel cop0_get_badvaddr + jr $ra + mfc0 $v0, COP0_BADVADDR + +# If the error code field of cop0's cause register is non-zero, +# draw crash details to the screen and hang +# +# If there wasn't an error, continue to the original handler + +glabel __crash_handler_entry + mfc0 $k1, COP0_CAUSE + andi $k1, $k1, (0x1F << 2) + beqzl $k1, .end2 # exit if ExCode is 0 + lui $k0, %hi(__osException) + la $k0, exceptionRegContext + sd $zero, 0x018 ($k0) + sd $at, 0x020 ($k0) + sd $v0, 0x028 ($k0) + sd $v1, 0x030 ($k0) + sd $a0, 0x038 ($k0) + sd $a1, 0x040 ($k0) + sd $a2, 0x048 ($k0) + sd $a3, 0x050 ($k0) + sd $t0, 0x058 ($k0) + sd $t1, 0x060 ($k0) + sd $t2, 0x068 ($k0) + sd $t3, 0x070 ($k0) + sd $t4, 0x078 ($k0) + sd $t5, 0x080 ($k0) + sd $t6, 0x088 ($k0) + sd $t7, 0x090 ($k0) + sd $s0, 0x098 ($k0) + sd $s1, 0x0A0 ($k0) + sd $s2, 0x0A8 ($k0) + sd $s3, 0x0B0 ($k0) + sd $s4, 0x0B8 ($k0) + sd $s5, 0x0C0 ($k0) + sd $s6, 0x0C8 ($k0) + sd $s7, 0x0D0 ($k0) + sd $t8, 0x0D8 ($k0) + sd $t9, 0x0E0 ($k0) + sd $gp, 0x0E8 ($k0) + sd $sp, 0x0F0 ($k0) + sd $fp, 0x0F8 ($k0) + sd $ra, 0x100 ($k0) + # cop unusable exception fired twice on startup so we'll ignore it for now + li $t0, (0x0B << 2) + beq $k1, $t0, .end + nop + jal show_crash_screen_and_hang + nop + .end: + ld $at, 0x020 ($k0) + ld $v0, 0x028 ($k0) + ld $v1, 0x030 ($k0) + ld $a0, 0x038 ($k0) + ld $a1, 0x040 ($k0) + ld $a2, 0x048 ($k0) + ld $a3, 0x050 ($k0) + ld $t0, 0x058 ($k0) + ld $t1, 0x060 ($k0) + ld $t2, 0x068 ($k0) + ld $t3, 0x070 ($k0) + ld $t4, 0x078 ($k0) + ld $t5, 0x080 ($k0) + ld $t6, 0x088 ($k0) + ld $t7, 0x090 ($k0) + ld $s0, 0x098 ($k0) + ld $s1, 0x0A0 ($k0) + ld $s2, 0x0A8 ($k0) + ld $s3, 0x0B0 ($k0) + ld $s4, 0x0B8 ($k0) + ld $s5, 0x0C0 ($k0) + ld $s6, 0x0C8 ($k0) + ld $s7, 0x0D0 ($k0) + ld $t8, 0x0D8 ($k0) + ld $t9, 0x0E0 ($k0) + ld $gp, 0x0E8 ($k0) + ld $sp, 0x0F0 ($k0) + ld $fp, 0x0F8 ($k0) + ld $ra, 0x100 ($k0) + lui $k0, %hi(__osException) + .end2: + addiu $k0, $k0, %lo(__osException) + jr $k0 # run the original handler + nop diff --git a/tools/src/game/crash.c b/tools/src/game/crash.c new file mode 100644 index 0000000..716adfb --- /dev/null +++ b/tools/src/game/crash.c @@ -0,0 +1,260 @@ +/* SM64 Crash Handler */ + +#include + +#include "crash.h" + +extern u32 exceptionRegContext[]; + +extern char *pAssertFile; +extern int nAssertLine; +extern char *pAssertExpression; +extern int nAssertStopProgram; + +u16 fbFillColor = 0xFFFF; +u16 fbShadeColor = 0x0000; +u16 *fbAddress = NULL; + +extern u8 crashFont[]; + +const char *szErrCodes[] = { + "INTERRUPT", + "TLB MOD", + "UNMAPPED LOAD ADDR", + "UNMAPPED STORE ADDR", + "BAD LOAD ADDR", + "BAD STORE ADDR", + "BUS ERR ON INSTR FETCH", + "BUS ERR ON LOADSTORE", + "SYSCALL", + "BREAKPOINT", + "UNKNOWN INSTR", + "COP UNUSABLE", + "ARITHMETIC OVERFLOW", + "TRAP EXC", + "VIRTUAL COHERENCY INSTR", + "FLOAT EXC", +}; + +const char *szGPRegisters1[] = { "R0", "AT", "V0", "V1", "A0", "A1", "A2", "A3", + "T0", "T1", "T2", "T3", "T4", "T5", "T6", NULL }; + +const char *szGPRegisters2[] = { "T7", "S0", "S1", "S2", "S3", "S4", + "S5", "S6", "S7", "T8", "T9", /*"K0", "K1",*/ + "GP", "SP", "FP", "RA", NULL }; + +int crash_strlen(char *str) { + int len = 0; + while (*str++) { + len++; + } + return len; +} + +void show_crash_screen_and_hang(void) { + u32 cause; + u32 epc; + u8 errno; + + fb_set_address((void *) (*(u32 *) 0xA4400004 | 0x80000000)); // replace me + + cause = cop0_get_cause(); + epc = cop0_get_epc(); + + errno = (cause >> 2) & 0x1F; + + if (nAssertStopProgram == 0) { + fbFillColor = 0x6253; + fb_fill(10, 10, 300, 220); + + fb_print_str(80, 20, "AN ERROR HAS OCCURRED!"); + fb_print_int_hex(80, 30, errno, 8); + fb_print_str(95, 30, szErrCodes[errno]); + + if (errno >= 2 && errno <= 5) { + /* + 2 UNMAPPED LOAD ADDR + 3 UNMAPPED STORE ADDR + 4 BAD LOAD ADDR + 5 BAD STORE ADDR + */ + u32 badvaddr = cop0_get_badvaddr(); + + fb_print_str(145, 50, "VA"); + fb_print_int_hex(160, 50, badvaddr, 32); + } + } else { + int afterFileX; + int exprBoxWidth; + fbFillColor = 0x5263; + fb_fill(10, 10, 300, 220); + + fb_print_str(80, 20, "ASSERTION FAILED!"); + + afterFileX = fb_print_str(80, 30, pAssertFile); + fb_print_str(afterFileX, 30, ":"); + fb_print_uint(afterFileX + 5, 30, nAssertLine); + + exprBoxWidth = (crash_strlen(pAssertExpression) * 5) + 2; + fbFillColor = 0x0001; + fb_fill(80 - 1, 40 - 1, exprBoxWidth, 10); + fb_print_str(80, 40, pAssertExpression); + } + + fb_print_str(80, 50, "PC"); + fb_print_int_hex(95, 50, epc, 32); + + fb_print_gpr_states(80, 70, szGPRegisters1, &exceptionRegContext[6 + 0]); + fb_print_gpr_states(145, 70, szGPRegisters2, &exceptionRegContext[6 + 15 * 2]); + + fb_swap(); + osWritebackDCacheAll(); + + while (1) // hang forever + { + UNUSED volatile int t = 0; // keep pj64 happy + } +} + +u8 ascii_to_idx(char c) { + return c - 0x20; +} + +void fb_set_address(void *address) { + fbAddress = (u16 *) address; +} + +void fb_swap() { + // update VI frame buffer register + // todo other registers + *(u32 *) (0xA4400004) = (u32) fbAddress & 0x00FFFFFF; +} + +void fb_fill(int baseX, int baseY, int width, int height) { + int y, x; + + for (y = baseY; y < baseY + height; y++) { + for (x = baseX; x < baseX + width; x++) { + fbAddress[y * 320 + x] = fbFillColor; + } + } +} + +void fb_draw_char(int x, int y, u8 idx) { + u16 *out = &fbAddress[y * 320 + x]; + const u8 *in = &crashFont[idx * 3]; + int nbyte; + int nrow; + int ncol; + + for (nbyte = 0; nbyte < 3; nbyte++) { + u8 curbyte = in[nbyte]; + for (nrow = 0; nrow < 2; nrow++) { + for (ncol = 0; ncol < 4; ncol++) { + u8 px = curbyte & (1 << (7 - (nrow * 4 + ncol))); + if (px != 0) { + out[ncol] = fbFillColor; + } + } + out += 320; + } + } +} + +void fb_draw_char_shaded(int x, int y, u8 idx) { + fbFillColor = 0x0001; + fb_draw_char(x - 1, y + 1, idx); + + fbFillColor = 0xFFFF; + fb_draw_char(x, y, idx); +} + +int fb_print_str(int x, int y, const char *str) { + while (1) { + int yoffs = 0; + u8 idx; + char c = *str++; + + if (c == '\0') { + break; + } + + if (c == ' ') { + x += 5; + continue; + } + + switch (c) { + case 'j': + case 'g': + case 'p': + case 'q': + case 'y': + case 'Q': + yoffs = 1; + break; + case ',': + yoffs = 2; + break; + } + + idx = ascii_to_idx(c); + fb_draw_char_shaded(x, y + yoffs, idx); + x += 5; + } + + return x; +} + +void fb_print_int_hex(int x, int y, u32 value, int nbits) { + nbits -= 4; + + while (nbits >= 0) { + int nib = ((value >> nbits) & 0xF); + u8 idx; + + if (nib > 9) { + idx = ('A' - 0x20) + (nib - 0xa); + } else { + idx = ('0' - 0x20) + nib; + } + + fb_draw_char_shaded(x, y, idx); + x += 5; + + nbits -= 4; + } +} + +int fb_print_uint(int x, int y, u32 value) { + int nchars = 0; + + int v = value; + int i; + while (v /= 10) { + nchars++; + } + + x += nchars * 5; + + for (i = nchars; i >= 0; i--) { + fb_draw_char_shaded(x, y, ('0' - 0x20) + (value % 10)); + value /= 10; + x -= 5; + } + + return (x + nchars * 5); +} + +void fb_print_gpr_states(int x, int y, const char *regNames[], u32 *regContext) { + int i; + for (i = 0;; i++) { + if (regNames[i] == NULL) { + break; + } + + fb_print_str(x, y, regNames[i]); + fb_print_int_hex(x + 15, y, regContext[i * 2 + 1], 32); + y += 10; + } +} diff --git a/tools/src/game/crash.h b/tools/src/game/crash.h new file mode 100644 index 0000000..1386930 --- /dev/null +++ b/tools/src/game/crash.h @@ -0,0 +1,28 @@ +#ifndef _CRASH_H_ +#define _CRASH_H_ + +#include + +#define CRASH_SCREEN_INCLUDED 1 + +extern u32 cop0_get_cause(void); +extern u32 cop0_get_epc(void); +extern u32 cop0_get_badvaddr(void); + +extern void _n64_assert(const char* pFile, int nLine, const char *pExpression, int nStopProgram); + +extern u8 __crash_handler_entry[]; + +void show_crash_screen_and_hang(void); +u8 ascii_to_idx(char c); +void fb_set_address(void *address); +void fb_swap(void); +void fb_fill(int baseX, int baseY, int width, int height); +void fb_draw_char(int x, int y, u8 idx); +void fb_draw_char_shaded(int x, int y, u8 idx); +int fb_print_str(int x, int y, const char *str); +int fb_print_uint(int x, int y, u32 value); +void fb_print_int_hex(int x, int y, u32 value, int nbits); +void fb_print_gpr_states(int x, int y, const char* regStrs[], u32 *regContext); + +#endif /* _CRASH_H_ */