Attempting to update to the latest version of the port

This commit is contained in:
Mors 2021-03-18 19:37:19 +03:00
parent 982af95fc2
commit 1c474865a9
92 changed files with 14909 additions and 598 deletions

View File

@ -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)

194
README.md
View File

@ -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.<VERSION>.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=<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.<VERSION>.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/<VERSION>_pc/sm64.<VERSION>.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.<VERSION>.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=<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.<VERSION>.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/<VERSION>_pc/sm64.<VERSION>.f3dex2e.exe`.
### Debugging
The code can be debugged using `gdb`. On Linux install the `gdb` package and execute `gdb <executable>`. On MSYS2 install by executing `pacman -S winpty gdb` and execute `winpty gdb <executable>`. 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.<VERSION>.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=<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.<VERSION>.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

View File

@ -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;

View File

@ -172,6 +172,9 @@ enum DialogId {
DIALOG_167,
DIALOG_168,
DIALOG_169,
DIALOG_170,
DIALOG_171,
DIALOG_172,
DIALOG_COUNT
};

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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),
};

View File

@ -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, _)

View File

@ -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(),
};

View File

@ -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");

View File

@ -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,6 +990,14 @@ 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 (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.
@ -1000,4 +1010,5 @@ void cur_obj_update(void) {
}
}
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
if (!gHideHud) {
render_hud();
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
render_text_labels();

View File

@ -460,10 +460,12 @@ 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 (!gBooDialogueWasSaid) {
if (cur_obj_update_dialog(2, 2, dialogID, 0)) {
create_sound_spawner(SOUND_OBJ_DYING_ENEMY1);
obj_mark_for_deletion(o);
@ -471,6 +473,9 @@ static void boo_act_4(void) {
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;
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,6 +550,16 @@ static void big_boo_act_1(void) {
s16 sp22;
f32 sp1C;
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) {
@ -547,6 +567,7 @@ static void big_boo_act_1(void) {
} else {
sp22 = 0x300; sp1C = 0.8f;
}
}
boo_chase_mario(-100.0f, sp22, sp1C);

View File

@ -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];
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;

View File

@ -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;

View File

@ -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
{

View File

@ -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)
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) {

View File

@ -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 {

View File

@ -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) {

View File

@ -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:

View File

@ -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();

View File

@ -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,6 +43,8 @@ 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(!gDisableDrawDistance)
{
if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) {
for (i = 0; i < schoolQuantity; i++) {
fishObject = spawn_object(o, model, bhvFish);
@ -51,6 +54,7 @@ void fish_act_spawn(void) {
}
o->oAction = FISH_ACT_ACTIVE;
}
}
}
/**

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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();
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) {
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 (!gDisableDrawDistance) {
if (o->oDistanceToMario < 5000.0f)
cur_obj_enable_rendering();
else
cur_obj_disable_rendering();
}
}
void bhv_king_bobomb_loop(void) {

View File

@ -614,6 +614,17 @@ static void koopa_the_quick_act_race(void) {
case KOOPA_THE_QUICK_SUB_ACT_RUN:
koopa_the_quick_animate_footsteps();
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 = 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
@ -624,6 +635,7 @@ static void koopa_the_quick_act_race(void) {
} else {
o->oKoopaAgility = 4.0f;
}
}
obj_forward_vel_approach(o->oKoopaAgility * 6.0f * downhillSteepness,
o->oKoopaAgility * 0.1f);

View File

@ -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:

View File

@ -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);
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;
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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 (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;
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);

View File

@ -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);

View File

@ -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) {

View File

@ -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

View File

@ -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,8 +27,13 @@ void whomp_act_0(void) {
func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40);
} else {
cur_obj_set_pos_to_home();
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;
} else if (o->oDistanceToMario < 500.0f)
@ -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) {
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;
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 {
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 (!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();
}
}
load_object_collision_model();
}

View File

@ -316,9 +316,17 @@ static void wiggler_act_jumped_on(void) {
if (o->oHealth == 2) {
cur_obj_play_sound_2(SOUND_OBJ_WIGGLER_JUMP);
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;
}
}
}
}

View File

@ -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) {
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,8 +144,13 @@ void yoshi_give_present_loop(void) {
if ((sp1C & 0x03) == 0) {
play_sound(SOUND_MENU_YOSHI_GAIN_LIVES, gDefaultSoundArgs);
if (gLifeMode) {
gMarioState->numLives--;
}
else {
gMarioState->numLives++;
}
}
}
void bhv_yoshi_loop(void) {

View File

@ -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);
@ -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,7 +1101,7 @@ void radial_camera_move(struct Camera *c) {
}
}
}
if (!gImprovedCamera) {
// Bound sModeOffsetYaw within (-120, 120) degrees
if (sModeOffsetYaw > 0x5554) {
sModeOffsetYaw = 0x5554;
@ -1078,6 +1109,7 @@ void radial_camera_move(struct Camera *c) {
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;
}

View File

@ -21,6 +21,8 @@
#include "thread6.h"
#include <prevent_bss_reordering.h>
#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);
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;
}
}

View File

@ -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;

View File

@ -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;
}
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,6 +268,7 @@ void handle_power_meter_actions(s16 numHealthWedges) {
void render_hud_power_meter(void) {
s16 shownHealthWedges = gHudDisplay.wedges;
if ((gNewHud < 2) && (!gAlwaysShowHealth)) {
if (sPowerMeterHUD.animation != POWER_METER_HIDING) {
handle_power_meter_actions(shownHealthWedges);
}
@ -250,10 +290,17 @@ void render_hud_power_meter(void) {
default:
break;
}
}
else {
sPowerMeterHUD.y = 200;
handle_power_meter_actions(shownHealthWedges);
}
render_dl_power_meter(shownHealthWedges);
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.
@ -297,16 +383,50 @@ void render_hud_stars(void) {
return;
}
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);
}
}
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(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X), HUD_TOP_Y, "-"); // 'Star' glyph
print_text(get_right(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
print_text(get_right(HUD_STARS_X) + 16, HUD_TOP_Y, "*"); // 'X' glyph
}
print_text_fmt_int((showX * 14) + GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(HUD_STARS_X - 16),
print_text_fmt_int((showX * 14) + get_right(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;
#ifndef VERSION_EU
print_text(GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(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);
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(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]);
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(get_right(150), 185, "TIME");
#endif
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);
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();
}
}
}

View File

@ -23,6 +23,8 @@
#include "text_strings.h"
#include "types.h"
#include "settings.h"
u16 gDialogColorFadeTimer;
s8 gLastDialogLineNum;
s32 gDialogVariable;
@ -2251,8 +2253,13 @@ 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) {
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,9 +2774,11 @@ void print_hud_course_complete_coins(s16 x, s16 y) {
if (gCourseCompleteCoins == 50 || gCourseCompleteCoins == 100 || gCourseCompleteCoins == 150) {
play_sound(SOUND_GENERAL_COLLECT_1UP, gDefaultSoundArgs);
if (!gLifeMode) {
gMarioState[0].numLives++;
}
}
}
if (gHudDisplay.coins == gCourseCompleteCoins && gGotFileCoinHiScore != 0) {
play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -34,6 +34,8 @@
#include "sound_init.h"
#include "thread6.h"
#include "settings.h"
u32 unused80339F10;
s8 filler80339F1C[20];
@ -810,8 +812,13 @@ 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) {
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;
case ACT_BURNING_JUMP:
@ -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:
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) {
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;
if (gLifeMode) {
gMarioState->numLives = 0;
}
else {
gMarioState->numLives = 4;
}
gMarioState->health = 0x880;
gMarioState->prevNumStarsForDialog = gMarioState->numStars;

View File

@ -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) {
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)) {
if (!gDisableFallDamage) {
m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 8 : 12;
}
m->squishTimer = 30;
#ifdef VERSION_SH
queue_rumble_data(5, 80);
@ -104,8 +110,16 @@ 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) {
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,9 +207,21 @@ void update_air_with_turn(struct MarioState *m) {
intendedDYaw = m->intendedYaw - m->faceAngle[1];
intendedMag = m->intendedMag / 32.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;
}
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.
if (m->forwardVel > dragThreshold) {
@ -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,9 +250,39 @@ void update_air_without_turn(struct MarioState *m) {
intendedDYaw = m->intendedYaw - m->faceAngle[1];
intendedMag = m->intendedMag / 32.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.
if (m->forwardVel > dragThreshold) {
@ -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,9 +875,16 @@ s32 act_air_throw(struct MarioState *m) {
}
s32 act_water_jump(struct MarioState *m) {
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);
set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP);
@ -830,7 +896,12 @@ s32 act_water_jump(struct MarioState *m) {
break;
case AIR_STEP_HIT_WALL:
if (gBetterControls) {
mario_set_forward_vel(m, 20.0f);
}
else {
mario_set_forward_vel(m, 15.0f);
}
break;
case AIR_STEP_GRABBED_LEDGE:
@ -854,9 +925,16 @@ s32 act_hold_water_jump(struct MarioState *m) {
return drop_and_set_mario_action(m, ACT_FREEFALL, 0);
}
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);
set_mario_animation(m, MARIO_ANIM_JUMP_WITH_LIGHT_OBJ);
@ -868,7 +946,12 @@ s32 act_hold_water_jump(struct MarioState *m) {
break;
case AIR_STEP_HIT_WALL:
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) {
}
}
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,7 +1439,24 @@ 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;
@ -1330,6 +1482,7 @@ s32 act_air_hit_wall(struct MarioState *m) {
}
return set_mario_action(m, ACT_SOFT_BONK, 0);
}
}
#ifdef AVOID_UB
return
@ -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 */

View File

@ -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:
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,12 +729,22 @@ s32 act_in_cannon(struct MarioState *m) {
m->faceAngle[0] = 0;
}
if (gFlexibleCannons) {
if (marioObj->oMarioCannonInputYaw > 0x8000) {
marioObj->oMarioCannonInputYaw = 0x8000;
}
if (marioObj->oMarioCannonInputYaw < -0x8000) {
marioObj->oMarioCannonInputYaw = -0x8000;
}
}
else {
if (marioObj->oMarioCannonInputYaw > 0x4000) {
marioObj->oMarioCannonInputYaw = 0x4000;
}
if (marioObj->oMarioCannonInputYaw < -0x4000) {
marioObj->oMarioCannonInputYaw = -0x4000;
}
}
m->faceAngle[1] = marioObj->oMarioCannonObjectYaw + marioObj->oMarioCannonInputYaw;
if (m->input & INPUT_A_PRESSED) {

View File

@ -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,16 +620,30 @@ void general_star_dance_handler(struct MarioState *m, s32 isInWater) {
if ((m->actionArg & 1) == 0) {
level_trigger_warp(m, WARP_OP_STAR_EXIT);
} else {
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;
}
} else if (m->actionState == 1 && gDialogResponse) {
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
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
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
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
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) {

View File

@ -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,9 +812,21 @@ s32 act_walking(struct MarioState *m) {
return begin_braking_action(m);
}
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) {
return set_mario_action(m, ACT_CROUCH_SLIDE, 0);
@ -1047,9 +1065,16 @@ s32 act_braking(struct MarioState *m) {
return check_common_action_exits(m);
}
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) {
return set_mario_action(m, ACT_MOVE_PUNCHING, 0);
@ -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,9 +1594,16 @@ s32 act_dive_slide(struct MarioState *m) {
#ifdef VERSION_SH
queue_rumble_data(5, 80);
#endif
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;
}

View File

@ -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,6 +1049,34 @@ s32 act_twirl_land(struct MarioState *m) {
s32 act_ground_pound_land(struct MarioState *m) {
m->actionState = 1;
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);
}
@ -1057,6 +1088,7 @@ s32 act_ground_pound_land(struct MarioState *m) {
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);
return 0;

View File

@ -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,10 +61,20 @@ static f32 get_buoyancy(struct MarioState *m) {
buoyancy = -18.0f;
}
} else if (swimming_near_surface(m)) {
if (gBetterControls) {
buoyancy = 2.0f;
}
else {
buoyancy = 1.25f;
}
} else if (!(m->action & ACT_FLAG_MOVING)) {
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);
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,12 +545,22 @@ static s32 act_breaststroke(struct MarioState *m) {
}
if (m->actionTimer < 6) {
if (gBetterControls) {
m->forwardVel += 1.0f;
}
else {
m->forwardVel += 0.5f;
}
}
if (m->actionTimer >= 9) {
if (gBetterControls) {
m->forwardVel += 3.0f;
}
else {
m->forwardVel += 1.5f;
}
}
if (m->actionTimer >= 2) {
if (m->actionTimer < 6 && (m->input & INPUT_A_PRESSED)) {

View File

@ -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);
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,6 +438,7 @@ 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 (!gCollisionFixes || gMarioObject->platform == NULL | (m->vel[1]<=0)) {
if (ceilHeight - floorHeight > 160.0f) {
m->pos[0] = nextPos[0];
m->pos[2] = nextPos[2];
@ -442,6 +452,7 @@ s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepAr
m->pos[1] = floorHeight;
return AIR_STEP_LANDED;
}
}
if (nextPos[1] + 160.0f > ceilHeight) {
if (m->vel[1] >= 0.0f) {

View File

@ -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;

View File

@ -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);

View File

@ -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.

View File

@ -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);

83
src/game/settings.c Normal file
View File

@ -0,0 +1,83 @@
#include <ultra64.h>
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;

83
src/game/settings.h Normal file
View File

@ -0,0 +1,83 @@
#include <ultra64.h>
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;

View File

@ -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.

View File

@ -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);
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);
}

View File

@ -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;
}
}
/**

View File

@ -7,6 +7,8 @@
#include <ctype.h>
#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

View File

@ -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);

View File

@ -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++) {

View File

@ -7,7 +7,7 @@
#include "controller_emscripten_keyboard.h"
#endif
#include "../configfile.h"
#include "game/settings.h"
static int keyboard_buttons_down;

View File

@ -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 (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;

View File

@ -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 (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);

View File

@ -6,8 +6,9 @@
#include <ultra64.h>
#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->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;
}
}

81
src/pc/gfx/filters.h Normal file
View File

@ -0,0 +1,81 @@
/*
* XBR filter: from the FFmpeg project
*
* Copyright (c) 2011, 2012 Hyllian/Jararaca <sergiogdb@gmail.com>
* Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
* Copyright (c) 2015 Treeki <treeki@gmail.com>
*
*
* 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 <treeki@gmail.com>
*
*
* 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 <stdint.h>
#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

View File

@ -27,6 +27,8 @@
#include "gfx_screen_config.h"
#include "./game/settings.h"
#define THREE_POINT_FILTERING 0
#define DEBUG_D3D 0
@ -143,9 +145,15 @@ static void create_render_target_views(bool is_resize) {
// Resize swap chain buffers
ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1));
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));
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;
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));

View File

@ -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;
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;
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();
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();

View File

@ -14,6 +14,7 @@
#include <shellscalingapi.h>
#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,13 +581,19 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea
}
ComPtr<IDXGISwapChain1> 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;
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;
@ -550,6 +601,7 @@ ComPtr<IDXGISwapChain1> gfx_dxgi_create_swap_chain(IUnknown *device) {
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)
// have been divided by the current scaling factor. By making this call dpi aware, no division will be performed.

View File

@ -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

View File

@ -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) {
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) {

View File

@ -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;
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;
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 (get_mirror()) {
if (cross >= 0) return;
}
else {
if (cross <= 0) return;
}
break;
case G_CULL_BACK:
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:
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) {
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;
}

View File

@ -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

View File

@ -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;
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);
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) {

2807
src/pc/gfx/hq2x.c Normal file

File diff suppressed because it is too large Load Diff

3785
src/pc/gfx/hq3x.c Normal file

File diff suppressed because it is too large Load Diff

5232
src/pc/gfx/hq4x.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 <mytskine@gmail.com>
*
* 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 <stdlib.h>
#include <stdint.h>
#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

337
src/pc/gfx/xbr.c Normal file
View File

@ -0,0 +1,337 @@
/*
* XBR filter extracted from FFmpeg into a separate library.
*
*
* Copyright (c) 2011, 2012 Hyllian/Jararaca <sergiogdb@gmail.com>
* Copyright (c) 2014 Arwa Arif <arwaarif1994@gmail.com>
* Copyright (c) 2015 Treeki <treeki@gmail.com>
*
* 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 <stdlib.h>
#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;
}
}
}
}

View File

@ -27,7 +27,7 @@
#include "controller/controller_keyboard.h"
#include "configfile.h"
#include "game/settings.h"
#define CONFIG_FILE "sm64config.txt"

View File

@ -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"))

153
tools/asm/crash.s Normal file
View File

@ -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

260
tools/src/game/crash.c Normal file
View File

@ -0,0 +1,260 @@
/* SM64 Crash Handler */
#include <sm64.h>
#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;
}
}

28
tools/src/game/crash.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _CRASH_H_
#define _CRASH_H_
#include <types.h>
#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_ */