Merge branch 'master' into master

This commit is contained in:
fgsfds 2020-05-16 22:47:39 +03:00 committed by GitHub
commit ff844643d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 1877 additions and 1014 deletions

View file

@ -28,6 +28,12 @@ TARGET_RPI ?= 0
# Disable better camera by default # Disable better camera by default
BETTERCAMERA ?= 0 BETTERCAMERA ?= 0
# Disable no drawing distance by default
NODRAWINGDISTANCE ?= 0
# Disable texture fixes by default (helps with them purists)
TEXTURE_FIX ?= 0
# Enable extended options menu by default
EXT_OPTIONS_MENU ?= 1
# Build for Emscripten/WebGL # Build for Emscripten/WebGL
TARGET_WEB ?= 0 TARGET_WEB ?= 0
@ -42,8 +48,6 @@ else
endif endif
# Automatic settings for PC port(s) # Automatic settings for PC port(s)
# WINDOWS_BUILD IS NOT FOR COMPILING A WINDOWS EXECUTABLE UNDER LINUX OR WSL!
# USE THE WIKI GUIDE WITH MSYS2 FOR COMPILING A WINDOWS EXECUTABLE!
NON_MATCHING := 1 NON_MATCHING := 1
GRUCODE := f3dex2e GRUCODE := f3dex2e
@ -420,9 +424,14 @@ else
LD := $(CC) LD := $(CC)
endif endif
CPP := $(CROSS)cpp -P ifeq ($(WINDOWS_BUILD),1) # fixes compilation in MXE on Linux and WSL
CPP := cpp -P
OBJCOPY := objcopy
else
CPP := $(CROSS)cpp -P
OBJCOPY := $(CROSS)objcopy
endif
OBJDUMP := $(CROSS)objdump OBJDUMP := $(CROSS)objdump
OBJCOPY := $(CROSS)objcopy
PYTHON := python3 PYTHON := python3
SDLCONFIG := $(CROSS)sdl2-config SDLCONFIG := $(CROSS)sdl2-config
@ -440,33 +449,53 @@ CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(INCLUDE_CFLAGS) -Wall -Wextra -W
CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv `$(SDLCONFIG) --cflags` CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv `$(SDLCONFIG) --cflags`
endif endif
# Check for better camera option # Check for enhancement options
# Check for Puppycam option
ifeq ($(BETTERCAMERA),1) ifeq ($(BETTERCAMERA),1)
CC_CHECK += -DBETTERCAMERA CC_CHECK += -DBETTERCAMERA
CFLAGS += -DBETTERCAMERA CFLAGS += -DBETTERCAMERA
EXT_OPTIONS_MENU := 1
endif
# Check for no drawing distance option
ifeq ($(NODRAWINGDISTANCE),1)
CC_CHECK += -DNODRAWINGDISTANCE
CFLAGS += -DNODRAWINGDISTANCE
endif
# Check for texture fix option
ifeq ($(TEXTURE_FIX),1)
CC_CHECK += -DTEXTURE_FIX
CFLAGS += -DTEXTURE_FIX
endif
# Check for extended options menu option
ifeq ($(EXT_OPTIONS_MENU),1)
CC_CHECK += -DEXT_OPTIONS_MENU
CFLAGS += -DEXT_OPTIONS_MENU
endif endif
ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS) ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS)
ifeq ($(TARGET_WEB),1) ifeq ($(TARGET_WEB),1)
LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY=20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']" LDFLAGS := -lm -lGL -lSDL2 -no-pie -s TOTAL_MEMORY=20MB -g4 --source-map-base http://localhost:8080/ -s "EXTRA_EXPORTED_RUNTIME_METHODS=['callMain']"
else else ifeq ($(WINDOWS_BUILD),1)
LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread -lglew32 `$(SDLCONFIG) --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -static
ifeq ($(WINDOWS_BUILD),1) ifneq ($(CROSS),i686-w64-mingw32.static-)
LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -Llib -lpthread -lglew32 `$(SDLCONFIG) --static-libs` -lm -lglu32 -lsetupapi -ldinput8 -luser32 -lgdi32 -limm32 -lole32 -loleaut32 -lshell32 -lwinmm -lversion -luuid -lopengl32 -no-pie -static ifneq ($(CROSS),x86_64-w64-mingw32.static-)
ifeq ($(WINDOWS_CONSOLE),1) LDFLAGS += -no-pie
LDFLAGS += -mconsole endif
endif endif
else ifeq ($(WINDOWS_CONSOLE),1)
LDFLAGS += -mconsole
endif
else ifeq ($(TARGET_RPI),1)
# Linux / Other builds below # Linux / Other builds below
ifeq ($(TARGET_RPI),1)
LDFLAGS := $(OPT_FLAGS) -lm -lGLESv2 `$(SDLCONFIG) --libs` -no-pie LDFLAGS := $(OPT_FLAGS) -lm -lGLESv2 `$(SDLCONFIG) --libs` -no-pie
else else
LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm -lGL `$(SDLCONFIG) --libs` -no-pie -lpthread LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm -lGL `$(SDLCONFIG) --libs` -no-pie -lpthread
endif endif
endif
endif #Added for Pi ifeq
# Prevent a crash with -sopt # Prevent a crash with -sopt
@ -576,11 +605,13 @@ ifeq ($(VERSION),eu)
$(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
else else
$(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h
endif endif
################################################################ ################################################################

View file

@ -2,7 +2,9 @@
OpenGL adaptation of [n64decomp/sm64](https://github.com/n64decomp/sm64). OpenGL adaptation of [n64decomp/sm64](https://github.com/n64decomp/sm64).
Feel free to report bugs and contribute, but remember, there must be **no upload of any copyrighted asset**. Feel free to report bugs and contribute, but remember, there must be **no upload of any copyrighted asset**.
Run `./extract-assets.py --clean && make clean` or `make distclean` to remove ROM-originated content. This port has been made possible thanks to [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) by [Emill](https://github.com/Emill). Run `./extract_assets.py --clean && make clean` or `make distclean` to remove ROM-originated content. This port has been made possible mostly thanks to [Emill](https://github.com/Emill) and his [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) renderer.
*Read this in other languages: [Español](README_es_ES.md) [简体中文](README_zh_CN.md).*
## Features ## Features
@ -10,6 +12,9 @@ Run `./extract-assets.py --clean && make clean` or `make distclean` to remove RO
* Variable aspect ratio and resolution. The game can now correctly render at basically any window size. * Variable aspect ratio and resolution. The game can now correctly render at basically any window size.
* Native xinput controller support. On Linux, DualShock 4 has been confirmed to work plug-and-play. * Native xinput controller support. On Linux, DualShock 4 has been confirmed to work plug-and-play.
* Analog camera control and mouse look. (Activate with `make BETTERCAMERA=1`.) * Analog camera control and mouse look. (Activate with `make BETTERCAMERA=1`.)
* An option to disable drawing distances. (Activate with `make NODRAWINGDISTANCE=1`.)
* In-game control binding, currently available on the `testing` branch.
* Skip introductory Peach & Lakitu cutscenes with the `--skip-intro` CLI option
## Building ## Building
For building instructions, please refer to the [wiki](https://github.com/sm64pc/sm64pc/wiki). For building instructions, please refer to the [wiki](https://github.com/sm64pc/sm64pc/wiki).

204
README_es_ES.md Normal file
View file

@ -0,0 +1,204 @@
# sm64pc
Adaptación a OpenGL de [n64decomp/sm64](https://github.com/n64decomp/sm64).
No dudes en contribuir o reportar bugs, pero recuerda: **no se debe subir nada con copyright**.
Ejecuta `./extract_assets.py --clean && make clean` o `make distclean` para borrar todo el contenido proveniente de la ROM. Este port es posible gracias a [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) creado por [Emill](https://github.com/Emill).
## Funcionalidades
* Renderizado nativo. Podrás jugar a Super Mario 64 sin necesidad de un emulador.
* Resolución y relación de aspecto variables. Puedes jugar a Super Mario 64 a básicamente cualquier resolución o tamaño de ventana.
* Soporte nativo para mandos XInput. En Linux, se ha confirmado que el DualShock 4 funciona sin más.
* Cámara analógica y cámara controlada con el ratón. (Se activa con `make BETTERCAMERA=1`.)
* Opción para desactivar el límite de distancia de renderizado. (Se activa con `make NODRAWINGDISTANCE=1`.)
* Configurar los controles desde el juego, actualmente solo en la rama `testing`.
* Posibilidad de saltarte la intro con la opción de línea de comandos `--skip-intro`, actualmente solo en las ramas `testing` y `skip-intro`.
## Compilar en Windows
**No intentes compilar ejecutables para Windows bajo Linux usando `WINDOWS_BUILD=1`. No va a funcionar. Sigue la guía.**
#### 1. Instalación y configuración de MSYS2.
1. Descarga [msys2-x86_64-latest.exe](http://repo.msys2.org/distrib/msys2-x86_64-latest.exe) y ejecútalo. Si tu sistema operativo es de 32 bits (¿por qué?) descarga [msys2-i686-latest.exe](http://repo.msys2.org/distrib/msys2-i686-latest.exe) en su lugar. Asegúrate de que lo instalas en `C:\dev\msys64` (o `C:\dev\msys32` para 32 bits...). Ejecuta MSYS2.
2. En la ventana de comandos de MSYS2, ejecuta el siguiente comando:
```
pacman -Syuu
```
3. Abre "MSYS2 MinGW 64-Bit". Ejecuta este comando __repetidamente__ hasta que MSYS diga que ya no hay más actualizaciones. Es posible que tengas que volver a cerrar y abrir MSYS2.
```
pacman -Syuu
```
5. Ejecuta este comando y cuando te pida confirmación pulsa intro:
```
pacman -S --needed base-devel mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain \
git subversion mercurial \
mingw-w64-i686-cmake mingw-w64-x86_64-cmake
```
6. Listo.
#### Instala las dependencias
```
pacman -S mingw-w64-i686-glew mingw-w64-x86_64-glew mingw-w64-i686-SDL2 mingw-w64-x86_64-SDL2 python3
```
### Crea el directorio en el que preparar todo
Desde el explorador de Windows, navega a `C:\msys64\home\(nombre de usuario)\` y crea una carpeta con el nombre que te apetezca. Aquí es donde vamos a preparar todo.
### Clona el repositorio
En MSYS2, introduce el siguiente comando:
```
git clone https://github.com/sm64pc/sm64pc/
```
(Si no funciona, prueba a escribirlo manualmente, en lugar de copiar y pegar)
#### Copia la ROM base al directorio correspondiente
El paso anterior tiene que haber creado una carpeta llamada sm64pc. Dentro de esa carpeta, y para cada version de la ROM (jp/us/eu) de la cual quieras compilar un ejecutable, coloca la ROM con el nombre `baserom.<version>.z64` para extraer sus assets. Por ejemplo, `baserom.us.z64` para la versión americana, o `baserom.eu.z64` para la versión europea.
#### En MSYS2, vamos a navegar a la carpeta `./tools/audiofile-0.3.6/` y ejecutar el `autoreconf-i`. Introduce los siguientes comandos, en orden, uno a uno:
```
cd sm64pc/tools/audiofile-0.3.6/
autoreconf -i
```
No te vayas de este directorio hasta el paso 9.
#### Ejecuta el script `configure`
```
PATH=/mingw64/bin:/mingw32/bin:$PATH LIBS=-lstdc++ ./configure --disable-docs
```
#### Ejecuta el script `make`
```
PATH=/mingw64/bin:/mingw32/bin:$PATH make
```
#### Crea un directorio `lib` en `tools/`
```
mkdir ../lib
```
#### Acabas de compilar `libaudiofile`. Ahora cópialo a `tools/lib/`
```
cp libaudiofile/.libs/libaudiofile.a ../lib/
cp libaudiofile/.libs/libaudiofile.la ../lib/
```
#### Ahora toca hacer algo desde Windows.
En el explorador de Windows, ve a sm64pc\tools y edita el archivo Makefile desde un editor de texto (es recomendable usar un editor decente como Notepad++ o Sublime Text, en lugar del bloc de notas, para asegurarte de que no rompes el formato del texto) Busca la línea que contiene esto:
```tabledesign_CFLAGS := -Wno-uninitialized -laudiofile```
Y añade ` -lstdc++` al final, de manera que quede así (¡no olvides el espacio!)
```tabledesign_CFLAGS := -Wno-uninitialized -laudiofile -lstdc++```
Guarda el archivo.
#### Vuelve a la carpeta tools y ejecuta `make` con los siguientes comandos.
```
cd ..
PATH=/mingw64/bin:/mingw32/bin:$PATH make
```
#### Vuelve al directorio sm64pc
```
cd ..
```
#### Finalmente, ejecuta ```make``` de nuevo.
(Ten en cuenta que mingw32 y mingw64 han sido intercambiados. Esto es para que puedas compilar la versión de 32 bits si quieres.)
Aquí pones las opciones que quieras según la versión que quieras compilar. Por ejemplo, si quieres activar la cámara analógica, añade al final BETTERCAMERA=1. Si quieres la opción de distancia de renderizado ilimitada, añade NODRAWINGDISTANCE=1.
Por ejemplo:
```
PATH=/mingw32/bin:/mingw64/bin:$PATH make BETTERCAMERA=1 NODRAWINGDISTANCE=1
```
Listo. El .exe estará en sm64pc\build\. Disfruta.
## Compilar en Linux
### Nota para usuarios de Windows
No intentes compilar un ejecutable para Windows desde Linux o WSL. No funciona. Sigue la guía para Windows.
#### Copia la(s) ROM(s) base para la extracción de assets.
Por cada versión de la cual quieras compilar un ejecutable, copia la ROM en `./baserom.<versión>.z64` para extraer los assets.
#### Instala las dependencias.
Para compilar necesitas las sigueintes dependencias.
* python3 >= 3.6
* libsdl2-dev
* [audiofile](https://audiofile.68k.org/)
* libglew-dev
* git
Puedes instalarlas con este comando:
##### Debian / Ubuntu - (compilando para 32 bits)
```
sudo apt install build-essential git python3 libaudiofile-dev libglew-dev:i386 libsdl2-dev:i386
```
##### Debian / Ubuntu - (compilando para 64 bits)
```
sudo apt install build-essential git python3 libaudiofile-dev libglew-dev libsdl2-dev
```
##### Arch Linux
Hay un paquete AUR (cortesía de @narukeh) disponible bajo el nombre [sm64pc-git](https://aur.archlinux.org/packages/sm64pc-git/). Instálalo con tu gestor de AURs preferido.
Si quieres compilarlo por tu cuenta:
```
sudo pacman -S base-devel python audiofile sdl2 glew
```
##### Void Linux - (compilando para 64 bits)
```
sudo xbps-install -S base-devel python3 audiofile-devel SDL2-devel glew-devel
```
##### Void Linux - (compilando para 32 bits)
```
sudo xbps-install -S base-devel python3 audiofile-devel-32bit SDL2-devel-32bit glew-devel-32bit
```
#### Compila el ejecutable.
Ejecuta `make` para compilar (por defecto `VERSION=us`)
```
make VERSION=jp -j6 # Compila la versión (J) usando 6 hilos
make VERSION=us MARCH=i686 TARGET_BITS=32 # Compila un ejecutable de la versión (U) de 32 bits
make TARGET_RPI=1 # Compila un ejecutable para Raspberry Pi
```
## Compilar para la web
Puedes compilar el juego para navegadores que admitan WebGL usando [Emscripten](https://github.com/emscripten-core). Para hacerlo, instala [emsdk](https://github.com/emscripten-core/emsdk) y ejecuta `make TARGET_WEB=1`.
## Script para compilar para Raspberry Pi
[Hyenadae](https://github.com/Hyenadae/) ha creado un script que ayuda a compilar el juego para Raspberry Pi. Estos son los pasos que hace el script:
* Instala las dependencias;
* Cambia VC4_DRM en la RPi de 0 a 3;
* Cambia ajustes en la memoria de las RPis 0 y 1 para que se pueda completar la compilación;
* Permite la instalación de un SDL2 con KMS, lo que elimina la necesidad de usar X11 y garantiza el máximo rendimiento de cualquier RPi que ejecute VC4;
* Clona sm64pc si no encuentra los archivos necesarios;
* Comprueba si existen los assets y la ROM base necesaria (baserom.*.z64);
* Compila sm64pc.
El script está incluído en la rama master, pero también puede descargarse [aquí](https://raw.githubusercontent.com/sm64pc/sm64pc/master/pisetup.sh).
# Problemas conocidos
### Problemas ya conocidos:
* La versión EU tiene bugs en los textos y no tiene audio.
* El movimiento analógico horizontal de la cámara vuelve al estilo antiguo en el nivel Bowser in the Dark World (#72)
* La cámara con el ratón falla cuando disparas a Mario hacia un árbol o un tubo. (#71)
* "make: Nothing to be done for 'default'" al compilar para web. (#67)
### Estos problemas están marcados como solucionados. Por favor, contacta si sigues teniendo estos problemas.
* El juego se llena de flags aleatorias en las builds de 64 bits para Windows
* Hazy Maze Cave se cuelga en pantalla completa (#57)
* La pantalla de título no tiene el cursor para manipular a Mario en pantalla completa. (#28)
## Parches
En la carpeta `./enhancements` hay varios archivos `patch`, que pueden aplicarse de la siguiente manera:
```
git apply fps.patch --ignore-whitespace --reject
```
Si ocurre un rechazo, puedes buscarlo con el comando `find | grep .rej`.
Intenta resolver los rechazos a través de [wiggle](https://github.com/neilbrown/wiggle).
```
wiggle rejection.rej --replace
```

21
README_zh_CN.md Normal file
View file

@ -0,0 +1,21 @@
# sm64pc
本项目是 [n64decomp/sm64](https://github.com/n64decomp/sm64) 的 OpenGL 移植版本。
我们欢迎贡献代码与 bug 报告,但请切记,**不得上传任何被版权保护(来自 ROM 文件)的资源**。
提交前请运行 `./extract_assets.py --clean && make clean``make distclean` 来清除所有从 ROM 文件中提取的内容。
本移植是基于 [Emill](https://github.com/Emill) 的工作 [n64-fast32-engine](https://github.com/Emill/n64-fast3d-engine/) 才得以实现的。
## 主要功能
* 原生渲染。现在不用任何模拟器就可以运行 马力欧64 了。
* 长宽比和分辨率可以自由改变。本游戏目前可以在几乎任何窗口尺寸下正确渲染。
* 原生 xinput 手柄支持。在 Linux 下,已经确认 PS4 手柄可以即插即用。
* 支持模拟量视点控制、鼠标控制视点。(请使用 `make BETTERCAMERA=1` 编译)
* 可取消可视距离限制。(请使用 `make NODRAWINGDISTANCE=1` 编译)
* 游戏内操作设定功能,目前在 `testing` 分支下可用。
* 使用 `--skip-intro` 命令行选项跳过碧奇公主与 Lakitu 的片头剧情。目前在 `testing``skip-intro` 分支下可用。
## 编译方法
关于如何编译,请参考 [wiki](https://github.com/sm64pc/sm64pc/wiki)。
**请勿在 Linux 或者 WSL 下使用 `WINDOWS_BUILD=1` 参数尝试编译 Windows 版本,这样无法编译成功。请参考 Wiki。

View file

@ -11,6 +11,9 @@ static const Vtx burn_smoke_seg4_vertex_040217C0[] = {
// //! Wrong texture format. Called as rgba16, which makes the burn smoke appear // //! Wrong texture format. Called as rgba16, which makes the burn smoke appear
// as a transparent black burn smoke. Probably meant to show up as white-ish // as a transparent black burn smoke. Probably meant to show up as white-ish
// burn smoke, but mistakened for being intended as black smoke. // burn smoke, but mistakened for being intended as black smoke.
// Due to debate in the Koopa shorts PR surrounding the fix to a similar bug,
// said fix is on a compile-time variable. Use TEXTURE_FIX=1 at compile time
// to fix this.
// 0x04021800 // 0x04021800
ALIGNED8 static const u8 burn_smoke_seg4_texture_04021800[] = { ALIGNED8 static const u8 burn_smoke_seg4_texture_04021800[] = {
#include "actors/burn_smoke/burn_smoke.ia16.inc.c" #include "actors/burn_smoke/burn_smoke.ia16.inc.c"
@ -44,7 +47,11 @@ const Gfx burn_smoke_seg4_dl_04022048[] = {
// 0x04022070 - 0x040220C8 // 0x04022070 - 0x040220C8
const Gfx burn_smoke_seg4_dl_04022070[] = { const Gfx burn_smoke_seg4_dl_04022070[] = {
gsSPDisplayList(burn_smoke_seg4_dl_04022000), gsSPDisplayList(burn_smoke_seg4_dl_04022000),
#ifdef TEXTURE_FIX
gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_IA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD),
#else
gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), gsDPLoadTextureBlock(burn_smoke_seg4_texture_04021800, G_IM_FMT_RGBA, G_IM_SIZ_16b, 32, 32, 0, G_TX_CLAMP, G_TX_CLAMP, 5, 5, G_TX_NOLOD, G_TX_NOLOD),
#endif
gsSPDisplayList(burn_smoke_seg4_dl_04022028), gsSPDisplayList(burn_smoke_seg4_dl_04022028),
gsSPDisplayList(burn_smoke_seg4_dl_04022048), gsSPDisplayList(burn_smoke_seg4_dl_04022048),
gsSPEndDisplayList(), gsSPEndDisplayList(),

View file

@ -54,6 +54,8 @@ static const Lights1 koopa_seg6_lights_06002630 = gdSPDefLights1(
// beneath its shell, despite the fact it was intended to be white like // beneath its shell, despite the fact it was intended to be white like
// the rest of its body. This is evident because once the mistake is corrected // the rest of its body. This is evident because once the mistake is corrected
// it turns back to being white like the other polygons. // it turns back to being white like the other polygons.
// Due to debate in the PR surrounding the fix to this, said fix is on
// a compile-time variable. Use TEXTURE_FIX=1 at compile time to fix this.
// 0x06002648 // 0x06002648
ALIGNED8 static const u8 koopa_seg6_texture_06002648[] = { ALIGNED8 static const u8 koopa_seg6_texture_06002648[] = {
#include "actors/koopa/koopa_shell_front.rgba16.inc.c" #include "actors/koopa/koopa_shell_front.rgba16.inc.c"
@ -2077,8 +2079,13 @@ const Gfx koopa_seg6_dl_0600C498[] = {
gsSPVertex(koopa_seg6_vertex_0600B560, 9, 0), gsSPVertex(koopa_seg6_vertex_0600B560, 9, 0),
gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0), gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0),
gsSP1Triangle( 6, 7, 8, 0x0), gsSP1Triangle( 6, 7, 8, 0x0),
#ifdef TEXTURE_FIX
gsSPLight(&koopa_seg6_lights_06002630.l, 1),
gsSPLight(&koopa_seg6_lights_06002630.a, 2),
#else
gsSPLight(koopa_seg6_texture_06002648 + 0x20, 1), // this malformed light results in a gsSPLight(koopa_seg6_texture_06002648 + 0x20, 1), // this malformed light results in a
gsSPLight(koopa_seg6_texture_06002648 + 0x18, 2), // koopa appearing to wear pink shorts. gsSPLight(koopa_seg6_texture_06002648 + 0x18, 2), // koopa appearing to wear pink shorts.
#endif
gsSPVertex(koopa_seg6_vertex_0600B5F0, 15, 0), gsSPVertex(koopa_seg6_vertex_0600B5F0, 15, 0),
gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0), gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 0x0),
gsSP2Triangles( 6, 7, 0, 0x0, 8, 5, 9, 0x0), gsSP2Triangles( 6, 7, 0, 0x0, 8, 5, 9, 0x0),

141
c2obj.py Normal file
View file

@ -0,0 +1,141 @@
"""
This module attempts to parse the ``model.inc.c`` files and extract the
3D models within as standard Wavefront OBJ files.
Example:
Specify the path to the ``.inc.c`` file and a directory where to save
the extracted ``.obj`` files.
$ python c2obj.py ./actors/mario/model.inc.c ./actors/mario/obj/
This is a work in progress and it currently has some serious limitations:
* It only extracts geometry information, so no textures or any other info
* It makes assumptions about the layout of the code in the C source
* It hasn't been properly tested.
"""
def parse(filename, output_directory):
from os import path, mkdir
if not path.isdir(output_directory):
try:
mkdir(output_directory)
except OSError:
print(f'Could not use output directory {output_directory}.')
vtx_def = 'static const Vtx '
vtx_data = {}
reading_vtx = False
current_vtx_name = ''
current_vtx_data = []
current_vtx_vertices = 0
gfx_def = 'const Gfx '
reading_gfx = False
current_gfx_vertices = 0
current_gfx_faces = 0
insert_vert_call = 'gsSPVertex('
insert_1tri_call = 'gsSP1Triangle('
insert_2tri_call = 'gsSP2Triangles('
gfx_count = 0
end_of_block = '};'
with open(filename, 'r') as f:
for line in f:
line = line.strip()
if line.startswith(vtx_def):
vtx_name = line.split(' ')[3][:-2]
current_vtx_name = vtx_name
current_vtx_data = []
reading_vtx = True
continue
if line.startswith(gfx_def):
from datetime import datetime
current_gfx_name = line.split(' ')[2][:-2]
current_gfx_file = open(path.join(output_directory, current_gfx_name + '.obj'), 'w')
current_gfx_file.write("# Armando Arredondo's SM64 Wavefront OBJ Geometry Converter\n")
current_gfx_file.write('# File Created: {}\n\n'.format(datetime.now()))
reading_gfx = True
continue
if line == end_of_block:
if reading_vtx:
vtx_data[current_vtx_name] = current_vtx_data
reading_vtx = False
elif reading_gfx:
current_gfx_file.write(f'# {current_gfx_faces} faces\n\n')
current_gfx_file.close()
current_gfx_vertices = 0
reading_gfx = False
gfx_count += 1
continue
if reading_vtx:
line = line.replace('{', '[').replace('}', ']')
tri = eval(line[:-1])[0]
current_vtx_data.append(tri)
continue
if reading_gfx:
if line.startswith(insert_vert_call):
args = line[len(insert_vert_call):].split(',')
current_vtx_name = args[0]
if current_gfx_vertices > 0:
current_gfx_file.write(f'# {current_gfx_faces} faces\n\n')
current_gfx_faces = 0
current_vtx_vertices = len(vtx_data[current_vtx_name])
current_gfx_vertices += current_vtx_vertices
current_gfx_file.write(f'#\n# object {current_vtx_name}\n#\n\n')
current_vtx_data = vtx_data[current_vtx_name]
for tri in current_vtx_data:
v = tri[0]
current_gfx_file.write('v {:.3f} {:.3f} {:.3f}\n'.format(*v))
current_gfx_file.write(f'# {current_vtx_vertices} vertices\n\n')
for tri in current_vtx_data:
n = [_decode_normal(u) for u in tri[3][:3]]
current_gfx_file.write('vn {:.3f} {:.3f} {:.3f}\n'.format(*n))
current_gfx_file.write(f'# {current_vtx_vertices} vertex normals\n\n')
current_gfx_file.write(f'g {current_vtx_name}\n\n')
elif line.startswith(insert_2tri_call):
args = line[len(insert_2tri_call):].split(',')
correction = current_gfx_vertices - current_vtx_vertices + 1
indexes = [eval(args[i]) + correction for i in [0, 1, 2, 4, 5, 6]]
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[:3]))
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes[3:]))
current_gfx_faces += 2
elif line.startswith(insert_1tri_call):
args = line[len(insert_1tri_call):].split(',')
correction = current_gfx_vertices - current_vtx_vertices + 1
indexes = [eval(args[i]) + correction for i in [0, 1, 2]]
current_gfx_file.write('f {0}//{0} {1}//{1} {2}//{2}\n'.format(*indexes))
current_gfx_faces += 1
continue
print(f'{gfx_count} models extracted.')
def _decode_normal(x):
y = x if x <= 127 else x - 255
return y / 127
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('filename', help = 'filename of the .inc.c source file')
parser.add_argument('output_directory', help = 'directory where to put the extracted .obj files')
args = parser.parse_args()
parse(args.filename, args.output_directory)

View file

@ -114,7 +114,7 @@
// Often used to end behavior scripts that do not contain an infinite loop. // Often used to end behavior scripts that do not contain an infinite loop.
#define BREAK() \ #define BREAK() \
BC_B(0x0A) BC_B(0x0A)
// Exits the behavior script, unused. // Exits the behavior script, unused.
#define BREAK_UNUSED() \ #define BREAK_UNUSED() \
BC_B(0x0B) BC_B(0x0B)
@ -175,15 +175,15 @@
#define ADD_INT_RAND_RSHIFT(field, min, rshift) \ #define ADD_INT_RAND_RSHIFT(field, min, rshift) \
BC_BBH(0x17, field, min), \ BC_BBH(0x17, field, min), \
BC_H(rshift) BC_H(rshift)
// No operation. Unused. // No operation. Unused.
#define CMD_NOP_1(field) \ #define CMD_NOP_1(field) \
BC_BB(0x18, field) BC_BB(0x18, field)
// No operation. Unused. // No operation. Unused.
#define CMD_NOP_2(field) \ #define CMD_NOP_2(field) \
BC_BB(0x19, field) BC_BB(0x19, field)
// No operation. Unused. // No operation. Unused.
#define CMD_NOP_3(field) \ #define CMD_NOP_3(field) \
BC_BB(0x1A, field) BC_BB(0x1A, field)
@ -219,6 +219,9 @@
#define BILLBOARD() \ #define BILLBOARD() \
BC_B(0x21) BC_B(0x21)
#define CYLBOARD() \
BC_B(0x38)
// Hides the current object. // Hides the current object.
#define HIDE() \ #define HIDE() \
BC_B(0x22) BC_B(0x22)
@ -3180,7 +3183,7 @@ const BehaviorScript bhvFloorTrapInCastle[] = {
const BehaviorScript bhvTree[] = { const BehaviorScript bhvTree[] = {
BEGIN(OBJ_LIST_POLELIKE), BEGIN(OBJ_LIST_POLELIKE),
BILLBOARD(), CYLBOARD(),
OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE), OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE),
SET_INT(oInteractType, INTERACT_POLE), SET_INT(oInteractType, INTERACT_POLE),
SET_HITBOX(/*Radius*/ 80, /*Height*/ 500), SET_HITBOX(/*Radius*/ 80, /*Height*/ 500),
@ -6105,5 +6108,3 @@ const BehaviorScript bhvIntroScene[] = {
CALL_NATIVE(bhv_intro_scene_loop), CALL_NATIVE(bhv_intro_scene_loop),
END_LOOP(), END_LOOP(),
}; };

View file

@ -1,10 +0,0 @@
# No Draw Distances
This is a work-in-progress by [wabberz](https://github.com/wabberz) that disables the drawing distance for most objects and enemies.
**This will crash some levels in the 32-bit version**.
[Related Push Request](https://github.com/sm64pc/sm64pc/pull/75).
For instructions on how to apply patches, please refer to [the Wiki](https://github.com/sm64pc/sm64pc/wiki/Patches).

View file

@ -1,406 +0,0 @@
From c98a263cf40520bf0d131eb2d1a2f90240787c98 Mon Sep 17 00:00:00 2001
From: uwabami <uwabami@localhost>
Date: Tue, 12 May 2020 09:26:16 +0200
Subject: [PATCH] adding option to disable draw distance
---
Makefile | 8 ++++++++
src/engine/behavior_script.c | 4 ++++
src/engine/surface_load.c | 4 ++++
src/game/behaviors/butterfly.inc.c | 3 ++-
src/game/behaviors/chain_chomp.inc.c | 8 ++++++++
src/game/behaviors/coin.inc.c | 6 ++++++
src/game/behaviors/fish.inc.c | 4 ++++
src/game/behaviors/goomba.inc.c | 4 ++++
src/game/behaviors/heave_ho.inc.c | 4 ++++
src/game/behaviors/king_bobomb.inc.c | 4 ++++
src/game/behaviors/pokey.inc.c | 6 ++++++
src/game/behaviors/snufit.inc.c | 4 ++++
src/game/behaviors/triplet_butterfly.inc.c | 4 ++++
src/game/behaviors/water_bomb_cannon.inc.c | 8 ++++++++
src/game/behaviors/whirlpool.inc.c | 4 ++++
15 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index d4bd284..efeb63e 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,8 @@ COMPILER ?= ido
# Disable better camera by default
BETTERCAMERA ?= 0
+# Disable no drawing distance by default
+NODRAWINGDISTANCE ?= 0
# Build for Emscripten/WebGL
TARGET_WEB ?= 0
@@ -449,6 +451,12 @@ CC_CHECK += -DBETTERCAMERA
CFLAGS += -DBETTERCAMERA
endif
+# Check for no drawing distance option
+ifeq ($(NODRAWINGDISTANCE),1)
+CC_CHECK += -DNODRAWINGDISTANCE
+CFLAGS += -DNODRAWINGDISTANCE
+endif
+
ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS)
ifeq ($(TARGET_WEB),1)
diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c
index edd5247..feb6fef 100644
--- a/src/engine/behavior_script.c
+++ b/src/engine/behavior_script.c
@@ -987,11 +987,15 @@ void cur_obj_update(void) {
} else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) {
if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) {
// If the object has a render distance, check if it should be shown.
+#ifndef NODRAWINGDISTANCE
if (distanceFromMario > gCurrentObject->oDrawingDistance) {
// Out of render distance, hide the object.
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY;
} else if (gCurrentObject->oHeldState == HELD_FREE) {
+#else
+ if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) {
+#endif
// In render distance (and not being held), show the object.
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY;
diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c
index 363f9af..498fae0 100644
--- a/src/engine/surface_load.c
+++ b/src/engine/surface_load.c
@@ -789,9 +789,13 @@ void load_object_collision_model(void) {
}
}
+#ifndef NODRAWINGDISTANCE
if (marioDist < gCurrentObject->oDrawingDistance) {
+#endif
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
+#ifndef NODRAWINGDISTANCE
} else {
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
}
+#endif
}
diff --git a/src/game/behaviors/butterfly.inc.c b/src/game/behaviors/butterfly.inc.c
index d435d8d..9296ed5 100644
--- a/src/game/behaviors/butterfly.inc.c
+++ b/src/game/behaviors/butterfly.inc.c
@@ -107,6 +107,7 @@ void bhv_butterfly_loop(void) {
butterfly_act_return_home();
break;
}
-
+#ifndef NODRAWINGDISTANCE
set_object_visibility(o, 3000);
+#endif
}
diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c
index a77c5d5..9b9c342 100644
--- a/src/game/behaviors/chain_chomp.inc.c
+++ b/src/game/behaviors/chain_chomp.inc.c
@@ -53,7 +53,9 @@ static void chain_chomp_act_uninitialized(void) {
struct ChainSegment *segments;
s32 i;
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 3000.0f) {
+#endif
segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment));
if (segments != NULL) {
// Each segment represents the offset of a chain part to the pivot.
@@ -81,7 +83,9 @@ static void chain_chomp_act_uninitialized(void) {
cur_obj_unhide();
}
}
+#ifndef NODRAWINGDISTANCE
}
+#endif
}
/**
@@ -359,10 +363,12 @@ static void chain_chomp_act_move(void) {
f32 maxDistToPivot;
// Unload chain if mario is far enough
+#ifndef NODRAWINGDISTANCE
if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) {
o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;
o->oForwardVel = o->oVelY = 0.0f;
} else {
+#endif
cur_obj_update_floor_and_walls();
switch (o->oChainChompReleaseStatus) {
@@ -446,7 +452,9 @@ static void chain_chomp_act_move(void) {
o->oGravity = -4.0f;
o->oChainChompTargetPitch = -0x3000;
}
+#ifndef NODRAWINGDISTANCE
}
+#endif
}
/**
diff --git a/src/game/behaviors/coin.inc.c b/src/game/behaviors/coin.inc.c
index 913c583..05619b9 100644
--- a/src/game/behaviors/coin.inc.c
+++ b/src/game/behaviors/coin.inc.c
@@ -184,17 +184,23 @@ void bhv_coin_formation_loop(void) {
s32 bitIndex;
switch (o->oAction) {
case 0:
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) {
+#endif
for (bitIndex = 0; bitIndex < 8; bitIndex++) {
if (!(o->oCoinUnkF4 & (1 << bitIndex)))
spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte);
}
o->oAction++;
+#ifndef NODRAWINGDISTANCE
}
+#endif
break;
case 1:
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2100.0f)
o->oAction++;
+#endif
break;
case 2:
o->oAction = 0;
diff --git a/src/game/behaviors/fish.inc.c b/src/game/behaviors/fish.inc.c
index 839ab8d..f652ef4 100644
--- a/src/game/behaviors/fish.inc.c
+++ b/src/game/behaviors/fish.inc.c
@@ -42,7 +42,9 @@ 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.
*/
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) {
+#endif
for (i = 0; i < schoolQuantity; i++) {
fishObject = spawn_object(o, model, bhvFish);
fishObject->oBehParams2ndByte = o->oBehParams2ndByte;
@@ -50,7 +52,9 @@ void fish_act_spawn(void) {
obj_translate_xyz_random(fishObject, 700.0f);
}
o->oAction = FISH_ACT_ACTIVE;
+#ifndef NODRAWINGDISTANCE
}
+#endif
}
/**
diff --git a/src/game/behaviors/goomba.inc.c b/src/game/behaviors/goomba.inc.c
index 2dab2fe..bf47dda 100644
--- a/src/game/behaviors/goomba.inc.c
+++ b/src/game/behaviors/goomba.inc.c
@@ -78,7 +78,9 @@ 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) {
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 3000.0f) {
+#endif
// The spawner is capable of spawning more than 3 goombas, but this
// is not used in the game
dAngle =
@@ -98,11 +100,13 @@ void bhv_goomba_triplet_spawner_update(void) {
}
o->oAction += 1;
+#ifndef NODRAWINGDISTANCE
}
} else if (o->oDistanceToMario > 4000.0f) {
// 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;
+#endif
}
}
diff --git a/src/game/behaviors/heave_ho.inc.c b/src/game/behaviors/heave_ho.inc.c
index 662bb0b..2f9da86 100644
--- a/src/game/behaviors/heave_ho.inc.c
+++ b/src/game/behaviors/heave_ho.inc.c
@@ -73,14 +73,18 @@ void heave_ho_act_3(void) {
void heave_ho_act_0(void) {
cur_obj_set_pos_to_home();
+#ifndef NODRAWINGDISTANCE
if (find_water_level(o->oPosX, o->oPosZ) < o->oPosY && o->oDistanceToMario < 4000.0f) {
+#endif
cur_obj_become_tangible();
cur_obj_unhide();
o->oAction = 1;
+#ifndef NODRAWINGDISTANCE
} else {
cur_obj_become_intangible();
cur_obj_hide();
}
+#endif
}
void (*sHeaveHoActions[])(void) = { heave_ho_act_0, heave_ho_act_1, heave_ho_act_2, heave_ho_act_3 };
diff --git a/src/game/behaviors/king_bobomb.inc.c b/src/game/behaviors/king_bobomb.inc.c
index 63a7575..af1cb0a 100644
--- a/src/game/behaviors/king_bobomb.inc.c
+++ b/src/game/behaviors/king_bobomb.inc.c
@@ -295,10 +295,14 @@ void king_bobomb_move(void) {
cur_obj_move_using_fvel_and_gravity();
cur_obj_call_action_function(sKingBobombActions);
exec_anim_sound_state(sKingBobombSoundStates);
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 5000.0f)
+#endif
cur_obj_enable_rendering();
+#ifndef NODRAWINGDISTANCE
else
cur_obj_disable_rendering();
+#endif
}
void bhv_king_bobomb_loop(void) {
diff --git a/src/game/behaviors/pokey.inc.c b/src/game/behaviors/pokey.inc.c
index df5d11f..cfcc92c 100644
--- a/src/game/behaviors/pokey.inc.c
+++ b/src/game/behaviors/pokey.inc.c
@@ -151,7 +151,9 @@ static void pokey_act_uninitialized(void) {
s32 i;
s16 partModel;
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) {
+#endif
partModel = MODEL_POKEY_HEAD;
for (i = 0; i < 5; i++) {
@@ -170,7 +172,9 @@ static void pokey_act_uninitialized(void) {
o->oPokeyNumAliveBodyParts = 5;
o->oPokeyBottomBodyPartSize = 1.0f;
o->oAction = POKEY_ACT_WANDER;
+#ifndef NODRAWINGDISTANCE
}
+#endif
}
/**
@@ -185,9 +189,11 @@ static void pokey_act_wander(void) {
if (o->oPokeyNumAliveBodyParts == 0) {
obj_mark_for_deletion(o);
+#ifndef NODRAWINGDISTANCE
} else if (o->oDistanceToMario > 2500.0f) {
o->oAction = POKEY_ACT_UNLOAD_PARTS;
o->oForwardVel = 0.0f;
+#endif
} else {
treat_far_home_as_mario(1000.0f);
cur_obj_update_floor_and_walls();
diff --git a/src/game/behaviors/snufit.inc.c b/src/game/behaviors/snufit.inc.c
index f3a0c9e..76e78c0 100644
--- a/src/game/behaviors/snufit.inc.c
+++ b/src/game/behaviors/snufit.inc.c
@@ -180,7 +180,11 @@ void bhv_snufit_loop(void) {
void bhv_snufit_balls_loop(void) {
// If far from Mario or in a different room, despawn.
if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)
+#ifndef NODRAWINGDISTANCE
|| (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) {
+#else
+ || (o->oTimer != 0)) {
+#endif
obj_mark_for_deletion(o);
}
diff --git a/src/game/behaviors/triplet_butterfly.inc.c b/src/game/behaviors/triplet_butterfly.inc.c
index 1c2b926..3d16a9d 100644
--- a/src/game/behaviors/triplet_butterfly.inc.c
+++ b/src/game/behaviors/triplet_butterfly.inc.c
@@ -54,9 +54,11 @@ static void triplet_butterfly_act_init(void) {
}
static void triplet_butterfly_act_wander(void) {
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 1500.0f) {
obj_mark_for_deletion(o);
} else {
+#endif
approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f);
if (o->oTimer < 60) {
o->oTripletButterflyTargetYaw = cur_obj_angle_to_home();
@@ -82,7 +84,9 @@ static void triplet_butterfly_act_wander(void) {
obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400);
cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800));
+#ifndef NODRAWINGDISTANCE
}
+#endif
}
static void triplet_butterfly_act_activate(void) {
diff --git a/src/game/behaviors/water_bomb_cannon.inc.c b/src/game/behaviors/water_bomb_cannon.inc.c
index 8e9ba33..fb82e43 100644
--- a/src/game/behaviors/water_bomb_cannon.inc.c
+++ b/src/game/behaviors/water_bomb_cannon.inc.c
@@ -38,19 +38,27 @@ void bhv_bubble_cannon_barrel_loop(void) {
}
void water_bomb_cannon_act_0(void) {
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) {
+#endif
spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles);
cur_obj_unhide();
o->oAction = 1;
o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00;
+#ifndef NODRAWINGDISTANCE
}
+#endif
}
void water_bomb_cannon_act_1(void) {
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2500.0f) {
o->oAction = 2;
} else if (o->oBehParams2ndByte == 0) {
+#else
+ if (o->oBehParams2ndByte == 0) {
+#endif
if (o->oWaterCannonUnkF4 != 0) {
o->oWaterCannonUnkF4 -= 1;
} else {
diff --git a/src/game/behaviors/whirlpool.inc.c b/src/game/behaviors/whirlpool.inc.c
index 405e051..5aebebd 100644
--- a/src/game/behaviors/whirlpool.inc.c
+++ b/src/game/behaviors/whirlpool.inc.c
@@ -35,7 +35,9 @@ void whirpool_orient_graph(void) {
}
void bhv_whirlpool_loop(void) {
+#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 5000.0f) {
+#endif
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
// not sure if actually an array
@@ -52,10 +54,12 @@ void bhv_whirlpool_loop(void) {
whirpool_orient_graph();
o->oFaceAngleYaw += 0x1F40;
+#ifndef NODRAWINGDISTANCE
} else {
o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0;
}
+#endif
cur_obj_play_sound_1(SOUND_ENV_WATER);

View file

@ -3,20 +3,47 @@
#include "text_menu_strings.h" #include "text_menu_strings.h"
#define NC_CAMX _("Camera X Sensitivity") #define TEXT_OPT_CAMX _("Camera X Sensitivity")
#define NC_CAMY _("Camera Y Sensitivity") #define TEXT_OPT_CAMY _("Camera Y Sensitivity")
#define NC_INVERTX _("Invert X Axis") #define TEXT_OPT_INVERTX _("Invert X Axis")
#define NC_INVERTY _("Invert Y Axis") #define TEXT_OPT_INVERTY _("Invert Y Axis")
#define NC_CAMC _("Camera Centre Aggression") #define TEXT_OPT_CAMC _("Camera Centre Aggression")
#define NC_CAMP _("Camera Pan Level") #define TEXT_OPT_CAMP _("Camera Pan Level")
#define NC_ENABLED _("Enabled") #define TEXT_OPT_CAMD _("Camera Deceleration")
#define NC_DISABLED _("Disabled") #define TEXT_OPT_ENABLED _("Enabled")
#define NC_BUTTON _("[R]: Options") #define TEXT_OPT_DISABLED _("Disabled")
#define NC_BUTTON2 _("[R]: Return") #define TEXT_OPT_BUTTON1 _("[R]: Options")
#define NC_OPTION _("OPTIONS") #define TEXT_OPT_BUTTON2 _("[R]: Return")
#define NC_HIGHLIGHT _("O") #define TEXT_OPT_OPTIONS _("OPTIONS")
#define NC_ANALOGUE _("Analogue Camera") #define TEXT_OPT_CAMERA _("CAMERA")
#define NC_MOUSE _("Mouse Look") #define TEXT_OPT_CONTROLS _("CONTROLS")
#define TEXT_OPT_VIDEO _("DISPLAY")
#define TEXT_OPT_AUDIO _("SOUND")
#define TEXT_OPT_HIGHLIGHT _("O")
#define TEXT_OPT_ANALOGUE _("Analogue Camera")
#define TEXT_OPT_MOUSE _("Mouse Look")
#define TEXT_OPT_TEXFILTER _("Texture Filtering")
#define TEXT_OPT_FSCREEN _("Fullscreen")
#define TEXT_OPT_NEAREST _("Nearest")
#define TEXT_OPT_LINEAR _("Linear")
#define TEXT_OPT_MVOLUME _("Master Volume")
#define TEXT_OPT_UNBOUND _("NONE")
#define TEXT_OPT_PRESSKEY _("...")
#define TEXT_BIND_A _("A Button")
#define TEXT_BIND_B _("B Button")
#define TEXT_BIND_START _("Start Button")
#define TEXT_BIND_L _("L Trigger")
#define TEXT_BIND_R _("R Trigger")
#define TEXT_BIND_Z _("Z Trigger")
#define TEXT_BIND_C_UP _("C-Up")
#define TEXT_BIND_C_DOWN _("C-Down")
#define TEXT_BIND_C_LEFT _("C-Left")
#define TEXT_BIND_C_RIGHT _("C-Right")
#define TEXT_BIND_UP _("Stick Up")
#define TEXT_BIND_DOWN _("Stick Down")
#define TEXT_BIND_LEFT _("Stick Left")
#define TEXT_BIND_RIGHT _("Stick Right")
/** /**
* Global Symbols * Global Symbols

107
obj2c.py Normal file
View file

@ -0,0 +1,107 @@
"""
This module generates a fragment of C code, in the style of that found in
the ``model.inc.c`` files, that encodes the geometry of the model specified
by the Wavefront OBJ file.
Example:
Specify the path to the ``.obj`` file and pipe the output of the script
into the desired destination ``.c`` file.
$ python obj2c.py left_hand_closed.obj > left_hand_closed.inc.c
This is a work in progress and it currently has some serious limitations:
* It only encodes the geometry information of the OBJ file, so no
texture mapping or any other info.
* The generated fragment of C code has to be manually pasted into the
desired source file. Make sure that the name of the Gfx structure
you're pasting matches the one you're replacing.
* It hasn't been properly tested.
"""
def parse(filename):
from os.path import basename, splitext
from re import sub
# WARNIGN!
# `gfx_name` is just a guess. You have to manually check that the name
# of the Gfx structure you're pasting matches the one you're replacing.
clean = lambda fn: sub('\W|^(?=\d)','_', fn)
gfx_name = clean(splitext(basename(filename))[0])
gfx_vertices = []
gfx_normals = []
vertex_to_normal = {}
gfx_v_count = 0
vtx_name = ''
vtx_faces = []
vtx_v_count = 0
output_upper = []
output_lower = [f'const Gfx {gfx_name}[] = {{']
with open(filename, 'r') as obj:
for line in obj:
line = line.strip()
if line.startswith('v '):
coordinates = [eval(x) for x in line.split()[1:4]]
gfx_vertices.append(coordinates)
vtx_v_count += 1
gfx_v_count += 1
if line.startswith('vn '):
coordinates = [eval(x) for x in line.split()[1:4]]
gfx_normals.append([_encode_normal(x) for x in coordinates])
if line.startswith('g '):
vtx_name = line.split()[1]
if line.startswith('f '):
pairs = [pair.split('//') for pair in line.split()[1:4]]
vtx_faces.append([int(pair[0]) for pair in pairs])
for (x, y) in pairs:
vertex_to_normal[int(x) - 1] = int(y) - 1
if line.startswith('# ') and line.endswith('faces'):
output_upper.append(f'static const Vtx {vtx_name}[] = {{')
for i in range(gfx_v_count - vtx_v_count, gfx_v_count):
v_string = '[{}, {}, {}]'.format(*gfx_vertices[i])
n_string = '[{}, {}, {}, 0x00]'.format(*gfx_normals[vertex_to_normal[i]])
combined = f' [[{v_string}, 0, [0, 0], {n_string}]],'
output_upper.append(combined.replace('[', '{').replace(']', '}'))
output_upper.append('};\n')
output_lower.append(f' gsSPVertex({vtx_name}, {vtx_v_count}, 0),')
n = len(vtx_faces)
correction = vtx_v_count - gfx_v_count - 1
for i in range(int(n / 2)):
f1 = [vtx_faces[2 * i][j] + correction for j in range(3)]
f2 = [vtx_faces[2 * i + 1][j] + correction for j in range(3)]
output_lower.append(' gsSP2Triangles({}, {}, {}, 0x0, {}, {}, {}, 0x0),'.format(*f1, *f2))
if n % 2 != 0:
f3 = [vtx_faces[-1][j] + correction for j in range(3)]
output_lower.append(' gsSP1Triangle({}, {}, {}, 0x0),'.format(*f3))
vtx_v_count = 0
vtx_faces = []
output_lower.append(' gsSPEndDisplayList(),')
output_lower.append('};')
for line in output_upper + output_lower:
print(line)
def _encode_normal(x):
x *= 127
if x <= 0: x += 255
return hex(int(x))
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('filename', help = 'filename of the .obj file to parse')
args = parser.parse_args()
parse(args.filename)

View file

@ -1,4 +1,8 @@
#!/bin/bash #!/bin/bash
# Super Mario 64 PC on Raspberry Pi
# Find latest updates and code on https://www.github.com/sm64pc/sm64pc
# ToDo: Test on more Pi models with fresh Raspbian and allow existing src folders to be updated.
#
clear clear
echo "This script will assist with compiling Super Mario 64 on Raspbian 10" echo "This script will assist with compiling Super Mario 64 on Raspbian 10"
echo "Note that accelerated OpenGL (vc4_drm) is required for maximum performance" echo "Note that accelerated OpenGL (vc4_drm) is required for maximum performance"
@ -65,7 +69,8 @@ clear
echo "Super Mario 64 RPi Initial Setup" echo "Super Mario 64 RPi Initial Setup"
if [[ $pi != 4 ]] if [[ $pi != 4 ]]
then #Dumb idea, but quick hack. We CANNOT enable VC4 for Pi4. then #Dumb idea, but quick hack.
#We CANNOT enable VC4 for Pi4 as it uses VC6
inxinf=$(inxi -Gx) inxinf=$(inxi -Gx)
echo "Checking for pre-enabled VC4 acceleration (inxi -Gx)" echo "Checking for pre-enabled VC4 acceleration (inxi -Gx)"
@ -119,24 +124,24 @@ fixmem=$(cat /boot/cmdline.txt | grep cma=128M)
else else
echo "" echo ""
echo "Warning: VC4 enabled, but your Rasp Pi has 512MB or less RAM" echo "Warning: VC4 enabled, but your RasPi has 512MB or less RAM"
echo "To ensure VC4_DRM and game compilation is succesful, video memory will be reduced" echo "To ensure VC4_DRM and game compilation is succesful, video memory will be reduced"
echo "gpu_mem=48M (config.txt) | cma=128M (cmdline.txt) will be written to /boot " echo "gpu_mem=48M (config.txt) | cma=128M (cmdline.txt) will be written to /boot "
echo "" echo ""
read -p "Fix mem? (Y/N): " fixmem read -p "Fix mem? (Y/N): " fixmem
if [[ $fixmem =~ "Y" ]] if [[ $fixmem =~ "Y" ]]
then then
sudo sh -c "echo 'gpu_mem=48' >> /boot/config.txt" sudo sh -c "echo 'gpu_mem=48' >> /boot/config.txt"
sudo sh -c "echo 'cma=128M' >> /boot/cmdline.txt" sudo sh -c "echo 'cma=128M' >> /boot/cmdline.txt"
sync sync
echo "Wrote configuration changes to SD card." echo "Wrote configuration changes to SD card."
sleep 2 sleep 2
else else
echo "" echo ""
echo "Warning: Compilation freezes & errors are likely to occur on your Pi" echo "Warning: Compilation freezes & errors are likely to occur on your Pi"
echo "" echo ""
sleep 3 sleep 3
fi fi
fi fi
fi fi
@ -149,11 +154,12 @@ read -p "Reboot to enable changes? (Y/N): " fixstart
if [[ $fixstart =~ "Y" ]] if [[ $fixstart =~ "Y" ]]
then then
echo "" echo ""
echo "Rebooting RasPi!" echo "Rebooting RasPi in 4 seconds! Press Control-C to cancel."
sleep 4
sudo reboot sudo reboot
fi fi
fi fi
fi #This should never run on a Pi 4 fi # "Should never run on a Pi 4" part ends here
#-------------------------------------------------------------------------------- #--------------------------------------------------------------------------------
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@ -182,7 +188,7 @@ fi
#------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------
clear clear
echo "Optional: Compile SDL2 with 'KMSDRM' for enhanced performance?" echo "Optional: Compile SDL2 with 'KMSDRM' for enhanced performance?"
echo "KMSDRM allows Super Mario 64 to be run without GUI/Desktop enabled on boot." echo "KMSDRM allows Super Mario 64 to be run without GUI/Desktop (Xorg) enabled on boot"
echo "" echo ""
echo "Warning: Compile could take up to an hour on older Raspberry Pi models" echo "Warning: Compile could take up to an hour on older Raspberry Pi models"
read -p "Proceed? (Y/N): " sdlcomp read -p "Proceed? (Y/N): " sdlcomp
@ -293,7 +299,12 @@ echo ""
echo "Step 3. Compiling Super Mario 64 for the Raspberry Pi" echo "Step 3. Compiling Super Mario 64 for the Raspberry Pi"
echo "" echo ""
echo "Warning: Super Mario 64 assets are required in order to compile" echo "Warning: Super Mario 64 assets are required in order to compile"
if [[ $curdir ==1 ]]
then
echo "Assets will be extracted from "$PWD" "
else
echo "Assets will be extracted from $HOME/src/sm64pi/sm64pc/baserom.(us/eu/jp).z64 " echo "Assets will be extracted from $HOME/src/sm64pi/sm64pc/baserom.(us/eu/jp).z64 "
fi
if [[ $curdir == 1 ]] if [[ $curdir == 1 ]]
then then
@ -312,12 +323,15 @@ else
echo "" echo ""
echo "Please satisfy this requirement before continuing." echo "Please satisfy this requirement before continuing."
echo "Exiting Super Mario 64 RasPi setup and compilation script." echo "Exiting Super Mario 64 RasPi setup and compilation script."
echo ""
echo "Note: Re-run script once baserom(s) are inserted into" echo "Note: Re-run script once baserom(s) are inserted into"
if [[ $curdir == 1 ]] if [[ $curdir == 1 ]]
then then
echo $PWD echo $PWD
echo ""
else else
echo ""
echo $HOME/src/sm64pi/sm64pc/ echo $HOME/src/sm64pi/sm64pc/
fi fi

View file

@ -98,10 +98,10 @@ static void cur_obj_bhv_stack_push(uintptr_t bhvAddr) {
// Retrieve the last behavior command address from the object's behavior stack. // Retrieve the last behavior command address from the object's behavior stack.
static uintptr_t cur_obj_bhv_stack_pop(void) { static uintptr_t cur_obj_bhv_stack_pop(void) {
uintptr_t bhvAddr; uintptr_t bhvAddr;
gCurrentObject->bhvStackIndex--; gCurrentObject->bhvStackIndex--;
bhvAddr = gCurrentObject->bhvStack[gCurrentObject->bhvStackIndex]; bhvAddr = gCurrentObject->bhvStack[gCurrentObject->bhvStackIndex];
return bhvAddr; return bhvAddr;
} }
@ -115,7 +115,7 @@ static void stub_behavior_script_1(void) {
// Usage: HIDE() // Usage: HIDE()
static s32 bhv_cmd_hide(void) { static s32 bhv_cmd_hide(void) {
cur_obj_hide(); cur_obj_hide();
gCurBhvCommand++; gCurBhvCommand++;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -124,7 +124,7 @@ static s32 bhv_cmd_hide(void) {
// Usage: DISABLE_RENDERING() // Usage: DISABLE_RENDERING()
static s32 bhv_cmd_disable_rendering(void) { static s32 bhv_cmd_disable_rendering(void) {
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
gCurBhvCommand++; gCurBhvCommand++;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -133,7 +133,15 @@ static s32 bhv_cmd_disable_rendering(void) {
// Usage: BILLBOARD() // Usage: BILLBOARD()
static s32 bhv_cmd_billboard(void) { static s32 bhv_cmd_billboard(void) {
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD; gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD;
gCurBhvCommand++;
return BHV_PROC_CONTINUE;
}
// Command 0x
static s32 bhv_cmd_cylboard(void) {
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD;
gCurBhvCommand++; gCurBhvCommand++;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -142,9 +150,9 @@ static s32 bhv_cmd_billboard(void) {
// Usage: SET_MODEL(modelID) // Usage: SET_MODEL(modelID)
static s32 bhv_cmd_set_model(void) { static s32 bhv_cmd_set_model(void) {
s32 modelID = BHV_CMD_GET_2ND_S16(0); s32 modelID = BHV_CMD_GET_2ND_S16(0);
gCurrentObject->header.gfx.sharedChild = gLoadedGraphNodes[modelID]; gCurrentObject->header.gfx.sharedChild = gLoadedGraphNodes[modelID];
gCurBhvCommand++; gCurBhvCommand++;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -216,7 +224,7 @@ static s32 bhv_cmd_break_unused(void) {
static s32 bhv_cmd_call(void) { static s32 bhv_cmd_call(void) {
const BehaviorScript *jumpAddress; const BehaviorScript *jumpAddress;
gCurBhvCommand++; gCurBhvCommand++;
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the next bhv command in the stack. cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the next bhv command in the stack.
jumpAddress = segmented_to_virtual(BHV_CMD_GET_VPTR(0)); jumpAddress = segmented_to_virtual(BHV_CMD_GET_VPTR(0));
gCurBhvCommand = jumpAddress; // Jump to the new address. gCurBhvCommand = jumpAddress; // Jump to the new address.
@ -300,7 +308,7 @@ static s32 bhv_cmd_begin_repeat(void) {
static s32 bhv_cmd_end_repeat(void) { static s32 bhv_cmd_end_repeat(void) {
u32 count = cur_obj_bhv_stack_pop(); // Retrieve loop count from the stack. u32 count = cur_obj_bhv_stack_pop(); // Retrieve loop count from the stack.
count--; count--;
if (count != 0) { if (count != 0) {
gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop
// Save address and count to the stack again // Save address and count to the stack again
@ -320,7 +328,7 @@ static s32 bhv_cmd_end_repeat(void) {
static s32 bhv_cmd_end_repeat_continue(void) { static s32 bhv_cmd_end_repeat_continue(void) {
u32 count = cur_obj_bhv_stack_pop(); u32 count = cur_obj_bhv_stack_pop();
count--; count--;
if (count != 0) { if (count != 0) {
gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop
// Save address and count to the stack again // Save address and count to the stack again
@ -546,7 +554,7 @@ static s32 bhv_cmd_drop_to_floor(void) {
f32 x = gCurrentObject->oPosX; f32 x = gCurrentObject->oPosX;
f32 y = gCurrentObject->oPosY; f32 y = gCurrentObject->oPosY;
f32 z = gCurrentObject->oPosZ; f32 z = gCurrentObject->oPosZ;
f32 floor = find_floor_height(x, y + 200.0f, z); f32 floor = find_floor_height(x, y + 200.0f, z);
gCurrentObject->oPosY = floor; gCurrentObject->oPosY = floor;
gCurrentObject->oMoveFlags |= OBJ_MOVE_ON_GROUND; gCurrentObject->oMoveFlags |= OBJ_MOVE_ON_GROUND;
@ -665,7 +673,7 @@ static s32 bhv_cmd_nop_4(void) {
static s32 bhv_cmd_begin(void) { static s32 bhv_cmd_begin(void) {
// These objects were likely very early objects, which is why this code is here // These objects were likely very early objects, which is why this code is here
// instead of in the respective behavior scripts. // instead of in the respective behavior scripts.
// Initiate the room if the object is a haunted chair or the mad piano. // Initiate the room if the object is a haunted chair or the mad piano.
if (cur_obj_has_behavior(bhvHauntedChair)) { if (cur_obj_has_behavior(bhvHauntedChair)) {
bhv_init_room(); bhv_init_room();
@ -696,7 +704,7 @@ static void bhv_cmd_set_int_random_from_table(s32 tableSize) {
} }
cur_obj_set_int(field, table[(s32)(tableSize * random_float())]); cur_obj_set_int(field, table[(s32)(tableSize * random_float())]);
// Does not increment gCurBhvCommand or return a bhv status // Does not increment gCurBhvCommand or return a bhv status
} }
@ -719,7 +727,7 @@ static s32 bhv_cmd_set_int_random_from_table(void) {
// Set the field to a random entry of the table. // Set the field to a random entry of the table.
cur_obj_set_int(field, table[(s32)(tableSize * random_float())]); cur_obj_set_int(field, table[(s32)(tableSize * random_float())]);
gCurBhvCommand += (tableSize / 2) + 1; gCurBhvCommand += (tableSize / 2) + 1;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -729,9 +737,9 @@ static s32 bhv_cmd_set_int_random_from_table(void) {
// Usage: LOAD_COLLISION_DATA(collisionData) // Usage: LOAD_COLLISION_DATA(collisionData)
static s32 bhv_cmd_load_collision_data(void) { static s32 bhv_cmd_load_collision_data(void) {
u32 *collisionData = segmented_to_virtual(BHV_CMD_GET_VPTR(1)); u32 *collisionData = segmented_to_virtual(BHV_CMD_GET_VPTR(1));
gCurrentObject->collisionData = collisionData; gCurrentObject->collisionData = collisionData;
gCurBhvCommand += 2; gCurBhvCommand += 2;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -742,7 +750,7 @@ static s32 bhv_cmd_set_home(void) {
gCurrentObject->oHomeX = gCurrentObject->oPosX; gCurrentObject->oHomeX = gCurrentObject->oPosX;
gCurrentObject->oHomeY = gCurrentObject->oPosY; gCurrentObject->oHomeY = gCurrentObject->oPosY;
gCurrentObject->oHomeZ = gCurrentObject->oPosZ; gCurrentObject->oHomeZ = gCurrentObject->oPosZ;
gCurBhvCommand++; gCurBhvCommand++;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -815,9 +823,9 @@ static s32 bhv_cmd_parent_bit_clear(void) {
// Usage: SPAWN_WATER_DROPLET(dropletParams) // Usage: SPAWN_WATER_DROPLET(dropletParams)
static s32 bhv_cmd_spawn_water_droplet(void) { static s32 bhv_cmd_spawn_water_droplet(void) {
struct WaterDropletParams *dropletParams = BHV_CMD_GET_VPTR(1); struct WaterDropletParams *dropletParams = BHV_CMD_GET_VPTR(1);
spawn_water_droplet(gCurrentObject, dropletParams); spawn_water_droplet(gCurrentObject, dropletParams);
gCurBhvCommand += 2; gCurBhvCommand += 2;
return BHV_PROC_CONTINUE; return BHV_PROC_CONTINUE;
} }
@ -842,62 +850,63 @@ void stub_behavior_script_2(void) {
typedef s32 (*BhvCommandProc)(void); typedef s32 (*BhvCommandProc)(void);
static BhvCommandProc BehaviorCmdTable[] = { static BhvCommandProc BehaviorCmdTable[] = {
bhv_cmd_begin, bhv_cmd_begin, //00
bhv_cmd_delay, bhv_cmd_delay, //01
bhv_cmd_call, bhv_cmd_call, //02
bhv_cmd_return, bhv_cmd_return, //03
bhv_cmd_goto, bhv_cmd_goto, //04
bhv_cmd_begin_repeat, bhv_cmd_begin_repeat, //05
bhv_cmd_end_repeat, bhv_cmd_end_repeat, //06
bhv_cmd_end_repeat_continue, bhv_cmd_end_repeat_continue, //07
bhv_cmd_begin_loop, bhv_cmd_begin_loop, //08
bhv_cmd_end_loop, bhv_cmd_end_loop, //09
bhv_cmd_break, bhv_cmd_break, //0A
bhv_cmd_break_unused, bhv_cmd_break_unused, //0B
bhv_cmd_call_native, bhv_cmd_call_native, //0C
bhv_cmd_add_float, bhv_cmd_add_float, //0D
bhv_cmd_set_float, bhv_cmd_set_float, //0E
bhv_cmd_add_int, bhv_cmd_add_int, //0F
bhv_cmd_set_int, bhv_cmd_set_int, //10
bhv_cmd_or_int, bhv_cmd_or_int, //11
bhv_cmd_bit_clear, bhv_cmd_bit_clear, //12
bhv_cmd_set_int_rand_rshift, bhv_cmd_set_int_rand_rshift, //13
bhv_cmd_set_random_float, bhv_cmd_set_random_float, //14
bhv_cmd_set_random_int, bhv_cmd_set_random_int, //15
bhv_cmd_add_random_float, bhv_cmd_add_random_float, //16
bhv_cmd_add_int_rand_rshift, bhv_cmd_add_int_rand_rshift, //17
bhv_cmd_nop_1, bhv_cmd_nop_1, //18
bhv_cmd_nop_2, bhv_cmd_nop_2, //19
bhv_cmd_nop_3, bhv_cmd_nop_3, //1A
bhv_cmd_set_model, bhv_cmd_set_model, //1B
bhv_cmd_spawn_child, bhv_cmd_spawn_child, //1C
bhv_cmd_deactivate, bhv_cmd_deactivate, //1D
bhv_cmd_drop_to_floor, bhv_cmd_drop_to_floor, //1E
bhv_cmd_sum_float, bhv_cmd_sum_float, //1F
bhv_cmd_sum_int, bhv_cmd_sum_int, //20
bhv_cmd_billboard, bhv_cmd_billboard, //21
bhv_cmd_hide, bhv_cmd_hide, //22
bhv_cmd_set_hitbox, bhv_cmd_set_hitbox, //23
bhv_cmd_nop_4, bhv_cmd_nop_4, //24
bhv_cmd_delay_var, bhv_cmd_delay_var, //25
bhv_cmd_begin_repeat_unused, bhv_cmd_begin_repeat_unused, //26
bhv_cmd_load_animations, bhv_cmd_load_animations, //27
bhv_cmd_animate, bhv_cmd_animate, //28
bhv_cmd_spawn_child_with_param, bhv_cmd_spawn_child_with_param, //29
bhv_cmd_load_collision_data, bhv_cmd_load_collision_data, //2A
bhv_cmd_set_hitbox_with_offset, bhv_cmd_set_hitbox_with_offset, //2B
bhv_cmd_spawn_obj, bhv_cmd_spawn_obj, //2C
bhv_cmd_set_home, bhv_cmd_set_home, //2D
bhv_cmd_set_hurtbox, bhv_cmd_set_hurtbox, //2E
bhv_cmd_set_interact_type, bhv_cmd_set_interact_type, //2F
bhv_cmd_set_obj_physics, bhv_cmd_set_obj_physics, //30
bhv_cmd_set_interact_subtype, bhv_cmd_set_interact_subtype, //31
bhv_cmd_scale, bhv_cmd_scale, //32
bhv_cmd_parent_bit_clear, bhv_cmd_parent_bit_clear, //33
bhv_cmd_animate_texture, bhv_cmd_animate_texture, //34
bhv_cmd_disable_rendering, bhv_cmd_disable_rendering, //35
bhv_cmd_set_int_unused, bhv_cmd_set_int_unused, //36
bhv_cmd_spawn_water_droplet, bhv_cmd_spawn_water_droplet, //37
bhv_cmd_cylboard //38
}; };
// Execute the behavior script of the current object, process the object flags, and other miscellaneous code for updating objects. // Execute the behavior script of the current object, process the object flags, and other miscellaneous code for updating objects.
@ -987,11 +996,15 @@ void cur_obj_update(void) {
} else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) { } else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) {
if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) { if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) {
// If the object has a render distance, check if it should be shown. // If the object has a render distance, check if it should be shown.
#ifndef NODRAWINGDISTANCE
if (distanceFromMario > gCurrentObject->oDrawingDistance) { if (distanceFromMario > gCurrentObject->oDrawingDistance) {
// Out of render distance, hide the object. // Out of render distance, hide the object.
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY; gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY;
} else if (gCurrentObject->oHeldState == HELD_FREE) { } else if (gCurrentObject->oHeldState == HELD_FREE) {
#else
if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE) {
#endif
// In render distance (and not being held), show the object. // In render distance (and not being held), show the object.
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY; gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY;

View file

@ -30,12 +30,13 @@ extern Vec3s gVec3sOne;
#define GRAPH_RENDER_Z_BUFFER (1 << 3) #define GRAPH_RENDER_Z_BUFFER (1 << 3)
#define GRAPH_RENDER_INVISIBLE (1 << 4) #define GRAPH_RENDER_INVISIBLE (1 << 4)
#define GRAPH_RENDER_HAS_ANIMATION (1 << 5) #define GRAPH_RENDER_HAS_ANIMATION (1 << 5)
#define GRAPH_RENDER_CYLBOARD (1 << 6)
// Whether the node type has a function pointer of type GraphNodeFunc // Whether the node type has a function pointer of type GraphNodeFunc
#define GRAPH_NODE_TYPE_FUNCTIONAL 0x100 #define GRAPH_NODE_TYPE_FUNCTIONAL 0x100
// Type used for Bowser and an unused geo function in obj_behaviors.c // Type used for Bowser and an unused geo function in obj_behaviors.c
#define GRAPH_NODE_TYPE_400 0x400 #define GRAPH_NODE_TYPE_400 0x400
// The discriminant for different types of geo nodes // The discriminant for different types of geo nodes
#define GRAPH_NODE_TYPE_ROOT 0x001 #define GRAPH_NODE_TYPE_ROOT 0x001

View file

@ -377,6 +377,31 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) {
dest[3][3] = 1; dest[3][3] = 1;
} }
void mtxf_cylboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) { //straight up mtxf_billboard but minus the dest[1][n] lines. transform for cylindrical billboards
dest[0][0] = coss(angle);
dest[0][1] = sins(angle);
dest[0][2] = 0;
dest[0][3] = 0;
dest[1][0] = mtx[1][0];
dest[1][1] = mtx[1][1];
dest[1][2] = mtx[1][2];
dest[1][3] = 0;
dest[2][0] = 0;
dest[2][1] = 0;
dest[2][2] = 1;
dest[2][3] = 0;
dest[3][0] =
mtx[0][0] * position[0] + mtx[1][0] * position[1] + mtx[2][0] * position[2] + mtx[3][0];
dest[3][1] =
mtx[0][1] * position[0] + mtx[1][1] * position[1] + mtx[2][1] * position[2] + mtx[3][1];
dest[3][2] =
mtx[0][2] * position[0] + mtx[1][2] * position[1] + mtx[2][2] * position[2] + mtx[3][2];
dest[3][3] = 1;
}
/** /**
* Set 'dest' to a transformation matrix that aligns an object with the terrain * Set 'dest' to a transformation matrix that aligns an object with the terrain
* based on the normal. Used for enemies. * based on the normal. Used for enemies.

View file

@ -56,6 +56,7 @@ void mtxf_lookat(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d);
void mtxf_rotate_zxy_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c); void mtxf_rotate_zxy_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c);
void mtxf_rotate_xyz_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c); void mtxf_rotate_xyz_and_translate(f32 mtx[4][4], Vec3f b, Vec3s c);
void mtxf_billboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d); void mtxf_billboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d);
void mtxf_cylboard(f32 mtx1[4][4], f32 mtx2[4][4], Vec3f c, s16 d);
void mtxf_align_terrain_normal(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d); void mtxf_align_terrain_normal(f32 mtx[4][4], Vec3f b, Vec3f c, s16 d);
void mtxf_align_terrain_triangle(f32 mtx[4][4], Vec3f b, s16 c, f32 d); void mtxf_align_terrain_triangle(f32 mtx[4][4], Vec3f b, s16 c, f32 d);
void mtxf_mul(f32 dest[4][4], f32 a[4][4], f32 b[4][4]); void mtxf_mul(f32 dest[4][4], f32 a[4][4], f32 b[4][4]);

View file

@ -786,9 +786,13 @@ void load_object_collision_model(void) {
} }
} }
#ifndef NODRAWINGDISTANCE
if (marioDist < gCurrentObject->oDrawingDistance) { if (marioDist < gCurrentObject->oDrawingDistance) {
#endif
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
#ifndef NODRAWINGDISTANCE
} else { } else {
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
} }
#endif
} }

View file

@ -8,11 +8,15 @@
void bub_spawner_act_0(void) { void bub_spawner_act_0(void) {
s32 i; s32 i;
s32 sp18 = o->oBirdChirpChirpUnkF4; s32 sp18 = o->oBirdChirpChirpUnkF4;
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 1500.0f) { if (o->oDistanceToMario < 1500.0f) {
#endif
for (i = 0; i < sp18; i++) for (i = 0; i < sp18; i++)
spawn_object(o, MODEL_BUB, bhvBub); spawn_object(o, MODEL_BUB, bhvBub);
o->oAction = 1; o->oAction = 1;
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
void bub_spawner_act_1(void) { void bub_spawner_act_1(void) {

View file

@ -17,9 +17,11 @@ void opened_cannon_act_0(void) {
cur_obj_enable_rendering(); cur_obj_enable_rendering();
cur_obj_become_tangible(); cur_obj_become_tangible();
} }
cur_obj_become_tangible();
cur_obj_enable_rendering();
if (o->oDistanceToMario < 500.0f) { if (o->oDistanceToMario < 500.0f) {
cur_obj_become_tangible(); //cur_obj_become_tangible();
cur_obj_enable_rendering(); //cur_obj_enable_rendering();
if (o->oInteractStatus & INT_STATUS_INTERACTED if (o->oInteractStatus & INT_STATUS_INTERACTED
&& (!(o->oInteractStatus && (!(o->oInteractStatus
& INT_STATUS_TOUCHED_BOB_OMB))) // bob-omb explodes when it gets into a cannon & INT_STATUS_TOUCHED_BOB_OMB))) // bob-omb explodes when it gets into a cannon
@ -30,8 +32,8 @@ void opened_cannon_act_0(void) {
} else } else
o->oInteractStatus = 0; o->oInteractStatus = 0;
} else { } else {
cur_obj_become_intangible(); //cur_obj_become_intangible();
cur_obj_disable_rendering(); //cur_obj_disable_rendering();
o->oCannonUnk10C = 0; o->oCannonUnk10C = 0;
} }
} }

View file

@ -53,7 +53,9 @@ static void chain_chomp_act_uninitialized(void) {
struct ChainSegment *segments; struct ChainSegment *segments;
s32 i; s32 i;
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 3000.0f) { if (o->oDistanceToMario < 3000.0f) {
#endif
segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment));
if (segments != NULL) { if (segments != NULL) {
// Each segment represents the offset of a chain part to the pivot. // Each segment represents the offset of a chain part to the pivot.
@ -81,7 +83,9 @@ static void chain_chomp_act_uninitialized(void) {
cur_obj_unhide(); cur_obj_unhide();
} }
} }
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**
@ -359,10 +363,12 @@ static void chain_chomp_act_move(void) {
f32 maxDistToPivot; f32 maxDistToPivot;
// Unload chain if mario is far enough // Unload chain if mario is far enough
#ifndef NODRAWINGDISTANCE
if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) { if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f) {
o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN; o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;
o->oForwardVel = o->oVelY = 0.0f; o->oForwardVel = o->oVelY = 0.0f;
} else { } else {
#endif
cur_obj_update_floor_and_walls(); cur_obj_update_floor_and_walls();
switch (o->oChainChompReleaseStatus) { switch (o->oChainChompReleaseStatus) {
@ -446,7 +452,9 @@ static void chain_chomp_act_move(void) {
o->oGravity = -4.0f; o->oGravity = -4.0f;
o->oChainChompTargetPitch = -0x3000; o->oChainChompTargetPitch = -0x3000;
} }
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**

View file

@ -47,10 +47,14 @@ static void cloud_act_spawn_parts(void) {
* Wait for mario to approach, then unhide and enter the spawn parts action. * Wait for mario to approach, then unhide and enter the spawn parts action.
*/ */
static void cloud_act_fwoosh_hidden(void) { static void cloud_act_fwoosh_hidden(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) { if (o->oDistanceToMario < 2000.0f) {
#endif
cur_obj_unhide(); cur_obj_unhide();
o->oAction = CLOUD_ACT_SPAWN_PARTS; o->oAction = CLOUD_ACT_SPAWN_PARTS;
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**
@ -58,9 +62,11 @@ static void cloud_act_fwoosh_hidden(void) {
* long enough, blow wind at him. * long enough, blow wind at him.
*/ */
static void cloud_fwoosh_update(void) { static void cloud_fwoosh_update(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2500.0f) { if (o->oDistanceToMario > 2500.0f) {
o->oAction = CLOUD_ACT_UNLOAD; o->oAction = CLOUD_ACT_UNLOAD;
} else { } else {
#endif
if (o->oCloudBlowing) { if (o->oCloudBlowing) {
o->header.gfx.scale[0] += o->oCloudGrowSpeed; o->header.gfx.scale[0] += o->oCloudGrowSpeed;
@ -95,7 +101,9 @@ static void cloud_fwoosh_update(void) {
} }
cur_obj_scale(o->header.gfx.scale[0]); cur_obj_scale(o->header.gfx.scale[0]);
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**

View file

@ -184,17 +184,23 @@ void bhv_coin_formation_loop(void) {
s32 bitIndex; s32 bitIndex;
switch (o->oAction) { switch (o->oAction) {
case 0: case 0:
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) { if (o->oDistanceToMario < 2000.0f) {
#endif
for (bitIndex = 0; bitIndex < 8; bitIndex++) { for (bitIndex = 0; bitIndex < 8; bitIndex++) {
if (!(o->oCoinUnkF4 & (1 << bitIndex))) if (!(o->oCoinUnkF4 & (1 << bitIndex)))
spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte); spawn_coin_in_formation(bitIndex, o->oBehParams2ndByte);
} }
o->oAction++; o->oAction++;
#ifndef NODRAWINGDISTANCE
} }
#endif
break; break;
case 1: case 1:
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2100.0f) if (o->oDistanceToMario > 2100.0f)
o->oAction++; o->oAction++;
#endif
break; break;
case 2: case 2:
o->oAction = 0; o->oAction = 0;

View file

@ -24,12 +24,16 @@ static struct ObjectHitbox sEnemyLakituHitbox = {
* Wait for mario to approach, then spawn the cloud and become visible. * Wait for mario to approach, then spawn the cloud and become visible.
*/ */
static void enemy_lakitu_act_uninitialized(void) { static void enemy_lakitu_act_uninitialized(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) { if (o->oDistanceToMario < 2000.0f) {
#endif
spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud); spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud);
cur_obj_unhide(); cur_obj_unhide();
o->oAction = ENEMY_LAKITU_ACT_MAIN; o->oAction = ENEMY_LAKITU_ACT_MAIN;
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**

View file

@ -42,7 +42,9 @@ void fish_act_spawn(void) {
* If the current level is Secret Aquarium, ignore this requirement. * If the current level is Secret Aquarium, ignore this requirement.
* Fish moves at random with a max-range of 700.0f. * Fish moves at random with a max-range of 700.0f.
*/ */
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) {
#endif
for (i = 0; i < schoolQuantity; i++) { for (i = 0; i < schoolQuantity; i++) {
fishObject = spawn_object(o, model, bhvFish); fishObject = spawn_object(o, model, bhvFish);
fishObject->oBehParams2ndByte = o->oBehParams2ndByte; fishObject->oBehParams2ndByte = o->oBehParams2ndByte;
@ -50,7 +52,9 @@ void fish_act_spawn(void) {
obj_translate_xyz_random(fishObject, 700.0f); obj_translate_xyz_random(fishObject, 700.0f);
} }
o->oAction = FISH_ACT_ACTIVE; o->oAction = FISH_ACT_ACTIVE;
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**

View file

@ -78,7 +78,9 @@ void bhv_goomba_triplet_spawner_update(void) {
// If mario is close enough and the goombas aren't currently loaded, then // If mario is close enough and the goombas aren't currently loaded, then
// spawn them // spawn them
if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) { if (o->oAction == GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 3000.0f) { if (o->oDistanceToMario < 3000.0f) {
#endif
// The spawner is capable of spawning more than 3 goombas, but this // The spawner is capable of spawning more than 3 goombas, but this
// is not used in the game // is not used in the game
dAngle = dAngle =
@ -98,11 +100,13 @@ void bhv_goomba_triplet_spawner_update(void) {
} }
o->oAction += 1; o->oAction += 1;
#ifndef NODRAWINGDISTANCE
} }
} else if (o->oDistanceToMario > 4000.0f) { } else if (o->oDistanceToMario > 4000.0f) {
// If mario is too far away, enter the unloaded action. The goombas // If mario is too far away, enter the unloaded action. The goombas
// will detect this and unload themselves // will detect this and unload themselves
o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED; o->oAction = GOOMBA_TRIPLET_SPAWNER_ACT_UNLOADED;
#endif
} }
} }

View file

@ -72,8 +72,12 @@ void heave_ho_act_3(void) {
} }
void heave_ho_act_0(void) { void heave_ho_act_0(void) {
cur_obj_set_pos_to_home(); #ifndef NODRAWINGDISTANCE
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) {
#else
if (find_water_level(o->oPosX, o->oPosZ) < (o->oPosY - 50.0f)) {
#endif
cur_obj_set_pos_to_home();
cur_obj_become_tangible(); cur_obj_become_tangible();
cur_obj_unhide(); cur_obj_unhide();
o->oAction = 1; o->oAction = 1;

View file

@ -295,10 +295,14 @@ void king_bobomb_move(void) {
cur_obj_move_using_fvel_and_gravity(); cur_obj_move_using_fvel_and_gravity();
cur_obj_call_action_function(sKingBobombActions); cur_obj_call_action_function(sKingBobombActions);
exec_anim_sound_state(sKingBobombSoundStates); exec_anim_sound_state(sKingBobombSoundStates);
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 5000.0f) if (o->oDistanceToMario < 5000.0f)
#endif
cur_obj_enable_rendering(); cur_obj_enable_rendering();
#ifndef NODRAWINGDISTANCE
else else
cur_obj_disable_rendering(); cur_obj_disable_rendering();
#endif
} }
void bhv_king_bobomb_loop(void) { void bhv_king_bobomb_loop(void) {

View file

@ -14,18 +14,24 @@ void bhv_lll_floating_wood_bridge_loop(void) {
s32 i; s32 i;
switch (o->oAction) { switch (o->oAction) {
case 0: case 0:
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2500.0f) { if (o->oDistanceToMario < 2500.0f) {
#endif
for (i = 1; i < 4; i++) { for (i = 1; i < 4; i++) {
sp3C = spawn_object_relative(0, (i - 2) * 300, 0, 0, o, MODEL_LLL_WOOD_BRIDGE, sp3C = spawn_object_relative(0, (i - 2) * 300, 0, 0, o, MODEL_LLL_WOOD_BRIDGE,
bhvLllWoodPiece); bhvLllWoodPiece);
sp3C->oLllWoodPieceUnkF4 = i * 4096; sp3C->oLllWoodPieceUnkF4 = i * 4096;
} }
o->oAction = 1; o->oAction = 1;
#ifndef NODRAWINGDISTANCE
} }
#endif
break; break;
case 1: case 1:
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2600.0f) if (o->oDistanceToMario > 2600.0f)
o->oAction = 2; o->oAction = 2;
#endif
break; break;
case 2: case 2:
o->oAction = 0; o->oAction = 0;

View file

@ -30,7 +30,9 @@ void fire_bar_spawn_flames(s16 a0) {
} }
void fire_bar_act_0(void) { void fire_bar_act_0(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 3000.0f) if (o->oDistanceToMario < 3000.0f)
#endif
o->oAction = 1; o->oAction = 1;
} }
@ -45,8 +47,10 @@ void fire_bar_act_1(void) {
void fire_bar_act_2(void) { void fire_bar_act_2(void) {
o->oAngleVelYaw = -0x100; o->oAngleVelYaw = -0x100;
o->oMoveAngleYaw += o->oAngleVelYaw; o->oMoveAngleYaw += o->oAngleVelYaw;
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 3200.0f) if (o->oDistanceToMario > 3200.0f)
o->oAction = 3; o->oAction = 3;
#endif
} }
void fire_bar_act_3(void) { void fire_bar_act_3(void) {

View file

@ -328,7 +328,7 @@ void (*TablePiranhaPlantActions[])(void) = {
*/ */
void bhv_piranha_plant_loop(void) { void bhv_piranha_plant_loop(void) {
cur_obj_call_action_function(TablePiranhaPlantActions); cur_obj_call_action_function(TablePiranhaPlantActions);
#ifndef NODRAWINGDISTANCE
// In WF, hide all Piranha Plants once high enough up. // In WF, hide all Piranha Plants once high enough up.
if (gCurrLevelNum == LEVEL_WF) { if (gCurrLevelNum == LEVEL_WF) {
if (gMarioObject->oPosY > 3400.0f) if (gMarioObject->oPosY > 3400.0f)
@ -336,5 +336,6 @@ void bhv_piranha_plant_loop(void) {
else else
cur_obj_unhide(); cur_obj_unhide();
} }
#endif
o->oInteractStatus = 0; o->oInteractStatus = 0;
} }

View file

@ -151,7 +151,9 @@ static void pokey_act_uninitialized(void) {
s32 i; s32 i;
s16 partModel; s16 partModel;
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) { if (o->oDistanceToMario < 2000.0f) {
#endif
partModel = MODEL_POKEY_HEAD; partModel = MODEL_POKEY_HEAD;
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
@ -170,7 +172,9 @@ static void pokey_act_uninitialized(void) {
o->oPokeyNumAliveBodyParts = 5; o->oPokeyNumAliveBodyParts = 5;
o->oPokeyBottomBodyPartSize = 1.0f; o->oPokeyBottomBodyPartSize = 1.0f;
o->oAction = POKEY_ACT_WANDER; o->oAction = POKEY_ACT_WANDER;
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
/** /**
@ -185,9 +189,11 @@ static void pokey_act_wander(void) {
if (o->oPokeyNumAliveBodyParts == 0) { if (o->oPokeyNumAliveBodyParts == 0) {
obj_mark_for_deletion(o); obj_mark_for_deletion(o);
#ifndef NODRAWINGDISTANCE
} else if (o->oDistanceToMario > 2500.0f) { } else if (o->oDistanceToMario > 2500.0f) {
o->oAction = POKEY_ACT_UNLOAD_PARTS; o->oAction = POKEY_ACT_UNLOAD_PARTS;
o->oForwardVel = 0.0f; o->oForwardVel = 0.0f;
#endif
} else { } else {
treat_far_home_as_mario(1000.0f); treat_far_home_as_mario(1000.0f);
cur_obj_update_floor_and_walls(); cur_obj_update_floor_and_walls();

View file

@ -97,7 +97,9 @@ void bhv_sl_walking_penguin_loop(void) {
} }
cur_obj_move_standard(-78); cur_obj_move_standard(-78);
if (!cur_obj_hide_if_mario_far_away_y(1000.0f)) #ifndef NODRAWINGDISTANCE
if (!cur_obj_hide_if_mario_far_away_y(1000.0f))
#endif
play_penguin_walking_sound(PENGUIN_WALK_BIG); play_penguin_walking_sound(PENGUIN_WALK_BIG);
// Adjust the position to get a point better lined up with the visual model, for stopping the wind. // Adjust the position to get a point better lined up with the visual model, for stopping the wind.

View file

@ -180,7 +180,11 @@ void bhv_snufit_loop(void) {
void bhv_snufit_balls_loop(void) { void bhv_snufit_balls_loop(void) {
// If far from Mario or in a different room, despawn. // If far from Mario or in a different room, despawn.
if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM) if ((o->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)
#ifndef NODRAWINGDISTANCE
|| (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) { || (o->oTimer != 0 && o->oDistanceToMario > 1500.0f)) {
#else
|| (o->oTimer != 0)) {
#endif
obj_mark_for_deletion(o); obj_mark_for_deletion(o);
} }

View file

@ -54,9 +54,11 @@ static void triplet_butterfly_act_init(void) {
} }
static void triplet_butterfly_act_wander(void) { static void triplet_butterfly_act_wander(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 1500.0f) { if (o->oDistanceToMario > 1500.0f) {
obj_mark_for_deletion(o); obj_mark_for_deletion(o);
} else { } else {
#endif
approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f); approach_f32_ptr(&o->oTripletButterflySpeed, 8.0f, 0.5f);
if (o->oTimer < 60) { if (o->oTimer < 60) {
o->oTripletButterflyTargetYaw = cur_obj_angle_to_home(); o->oTripletButterflyTargetYaw = cur_obj_angle_to_home();
@ -82,7 +84,9 @@ static void triplet_butterfly_act_wander(void) {
obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400); obj_move_pitch_approach(o->oTripletButterflyTargetPitch, 400);
cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800)); cur_obj_rotate_yaw_toward(o->oTripletButterflyTargetYaw, random_linear_offset(400, 800));
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
static void triplet_butterfly_act_activate(void) { static void triplet_butterfly_act_activate(void) {

View file

@ -38,19 +38,27 @@ void bhv_bubble_cannon_barrel_loop(void) {
} }
void water_bomb_cannon_act_0(void) { void water_bomb_cannon_act_0(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) { if (o->oDistanceToMario < 2000.0f) {
#endif
spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles); spawn_object(o, MODEL_CANNON_BARREL, bhvCannonBarrelBubbles);
cur_obj_unhide(); cur_obj_unhide();
o->oAction = 1; o->oAction = 1;
o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00; o->oMoveAnglePitch = o->oWaterCannonUnkFC = 0x1C00;
#ifndef NODRAWINGDISTANCE
} }
#endif
} }
void water_bomb_cannon_act_1(void) { void water_bomb_cannon_act_1(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2500.0f) { if (o->oDistanceToMario > 2500.0f) {
o->oAction = 2; o->oAction = 2;
} else if (o->oBehParams2ndByte == 0) { } else if (o->oBehParams2ndByte == 0) {
#else
if (o->oBehParams2ndByte == 0) {
#endif
if (o->oWaterCannonUnkF4 != 0) { if (o->oWaterCannonUnkF4 != 0) {
o->oWaterCannonUnkF4 -= 1; o->oWaterCannonUnkF4 -= 1;
} else { } else {

View file

@ -35,7 +35,9 @@ void whirpool_orient_graph(void) {
} }
void bhv_whirlpool_loop(void) { void bhv_whirlpool_loop(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 5000.0f) { if (o->oDistanceToMario < 5000.0f) {
#endif
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
// not sure if actually an array // not sure if actually an array
@ -52,10 +54,12 @@ void bhv_whirlpool_loop(void) {
whirpool_orient_graph(); whirpool_orient_graph();
o->oFaceAngleYaw += 0x1F40; o->oFaceAngleYaw += 0x1F40;
#ifndef NODRAWINGDISTANCE
} else { } else {
o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0; gEnvFxBubbleConfig[ENVFX_STATE_PARTICLECOUNT] = 0;
} }
#endif
cur_obj_play_sound_1(SOUND_ENV_WATER); cur_obj_play_sound_1(SOUND_ENV_WATER);

View file

@ -246,10 +246,14 @@ void bhv_whomp_loop(void) {
cur_obj_call_action_function(sWhompActions); cur_obj_call_action_function(sWhompActions);
cur_obj_move_standard(-20); cur_obj_move_standard(-20);
if (o->oAction != 9) { if (o->oAction != 9) {
#ifndef NODRAWINGDISTANCE
// o->oBehParams2ndByte here seems to be a flag
// indicating whether this is a normal or king whomp
if (o->oBehParams2ndByte != 0) if (o->oBehParams2ndByte != 0)
cur_obj_hide_if_mario_far_away_y(2000.0f); cur_obj_hide_if_mario_far_away_y(2000.0f);
else else
cur_obj_hide_if_mario_far_away_y(1000.0f); cur_obj_hide_if_mario_far_away_y(1000.0f);
#endif
load_object_collision_model(); load_object_collision_model();
} }
} }

View file

@ -26,14 +26,9 @@ enum newcam_flagvalues
}; };
extern void newcam_display_options(void);
extern void newcam_check_pause_buttons(void);
extern void newcam_init_settings(void); extern void newcam_init_settings(void);
extern void newcam_save_settings(void);
extern void newcam_render_option_text(void);
extern void newcam_diagnostics(void); extern void newcam_diagnostics(void);
extern u8 newcam_option_open;
extern u8 newcam_sensitivityX; //How quick the camera works. extern u8 newcam_sensitivityX; //How quick the camera works.
extern u8 newcam_sensitivityY; extern u8 newcam_sensitivityY;

View file

@ -28,8 +28,6 @@ NC_MODE_NOTURN: Disables horizontal and vertical control of the camera.
//#define NEWCAM_DEBUG //Some print values for puppycam. Not useful anymore, but never hurts to keep em around. //#define NEWCAM_DEBUG //Some print values for puppycam. Not useful anymore, but never hurts to keep em around.
//#define nosound //If for some reason you hate the concept of audio, you can disable it. //#define nosound //If for some reason you hate the concept of audio, you can disable it.
//#define noaccel //Disables smooth movement of the camera with the C buttons. //#define noaccel //Disables smooth movement of the camera with the C buttons.
#define DEGRADE 0.1f //What percent of the remaining camera movement is degraded. Default is 10%
//!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes. //!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes.
///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons. ///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons.
@ -88,6 +86,7 @@ s16 newcam_yaw_target; // The yaw value the camera tries to set itself to when t
f32 newcam_turnwait; // The amount of time to wait after landing before allowing the camera to turn again f32 newcam_turnwait; // The amount of time to wait after landing before allowing the camera to turn again
f32 newcam_pan_x; f32 newcam_pan_x;
f32 newcam_pan_z; f32 newcam_pan_z;
f32 newcam_degrade = 0.1f; //What percent of the remaining camera movement is degraded. Default is 10%
u8 newcam_cstick_down = 0; //Just a value that triggers true when the player 2 stick is moved in 8 direction move to prevent holding it down. u8 newcam_cstick_down = 0; //Just a value that triggers true when the player 2 stick is moved in 8 direction move to prevent holding it down.
u8 newcam_target; u8 newcam_target;
@ -105,17 +104,6 @@ u16 newcam_mode;
u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another. u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another.
u16 newcam_modeflags; u16 newcam_modeflags;
u8 newcam_option_open = 0;
s8 newcam_option_selection = 0;
f32 newcam_option_timer = 0;
u8 newcam_option_index = 0;
u8 newcam_option_scroll = 0;
u8 newcam_option_scroll_last = 0;
u8 newcam_total = 8; //How many options there are in newcam_uptions.
u8 newcam_options[][64] = {{NC_ANALOGUE}, {NC_MOUSE}, {NC_CAMX}, {NC_CAMY}, {NC_INVERTX}, {NC_INVERTY}, {NC_CAMC}, {NC_CAMP}};
u8 newcam_flags[][64] = {{NC_DISABLED}, {NC_ENABLED}};
u8 newcam_strings[][64] = {{NC_BUTTON}, {NC_BUTTON2}, {NC_OPTION}, {NC_HIGHLIGHT}};
extern int mouse_x; extern int mouse_x;
extern int mouse_y; extern int mouse_y;
@ -166,18 +154,7 @@ void newcam_init_settings(void)
newcam_invertY = (u8)configCameraInvertY; newcam_invertY = (u8)configCameraInvertY;
newcam_mouse = (u8)configCameraMouse; newcam_mouse = (u8)configCameraMouse;
newcam_analogue = (u8)configEnableCamera; newcam_analogue = (u8)configEnableCamera;
} newcam_degrade = (f32)configCameraDegrade / 100.0f;
void newcam_save_settings(void)
{
configCameraXSens = newcam_sensitivityX;
configCameraYSens = newcam_sensitivityY;
configCameraAggr = newcam_aggression;
configCameraPan = newcam_panlevel;
configCameraInvertX = newcam_invertX != 0;
configCameraInvertY = newcam_invertY != 0;
configEnableCamera = newcam_analogue != 0;
configCameraMouse = newcam_mouse != 0;
} }
/** Mathematic calculations. This stuffs so basic even *I* understand it lol /** Mathematic calculations. This stuffs so basic even *I* understand it lol
@ -291,7 +268,7 @@ static void newcam_rotate_button(void)
#ifdef noaccel #ifdef noaccel
newcam_yaw_acc = 0; newcam_yaw_acc = 0;
#else #else
newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade);
#endif #endif
} }
@ -303,7 +280,7 @@ static void newcam_rotate_button(void)
#ifdef noaccel #ifdef noaccel
newcam_tilt_acc = 0; newcam_tilt_acc = 0;
#else #else
newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade);
#endif #endif
newcam_framessincec[0] += 1; newcam_framessincec[0] += 1;
@ -369,13 +346,13 @@ static void newcam_rotate_button(void)
else else
{ {
newcam_cstick_down = 0; newcam_cstick_down = 0;
newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE)); newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade);
} }
if (ABS(gPlayer2Controller->stickY) > 20 && newcam_modeflags & NC_FLAG_YTURN) if (ABS(gPlayer2Controller->stickY) > 20 && newcam_modeflags & NC_FLAG_YTURN)
newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->stickY/4)); newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->stickY/4));
else else
newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE)); newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade);
} }
if (newcam_mouse == 1) if (newcam_mouse == 1)
@ -401,8 +378,8 @@ static void newcam_zoom_button(void)
newcam_distance = newcam_distance_target; newcam_distance = newcam_distance_target;
} }
//When you press L and R together, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time. //When you press L, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time.
if (gPlayer1Controller->buttonDown & L_TRIG && gPlayer1Controller->buttonDown & R_TRIG && newcam_modeflags & NC_FLAG_ZOOM) if (gPlayer1Controller->buttonDown & L_TRIG && newcam_modeflags & NC_FLAG_ZOOM)
{ {
newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000; newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000;
newcam_centering = 1; newcam_centering = 1;
@ -670,234 +647,10 @@ void newcam_loop(struct Camera *c)
newcam_position_cam(); newcam_position_cam();
newcam_find_fixed(); newcam_find_fixed();
if (gMarioObject) if (gMarioObject)
newcam_apply_values(c); newcam_apply_values(c);
//Just some visual information on the values of the camera. utilises ifdef because it's better at runtime. //Just some visual information on the values of the camera. utilises ifdef because it's better at runtime.
#ifdef NEWCAM_DEBUG #ifdef NEWCAM_DEBUG
newcam_diagnostics(); newcam_diagnostics();
#endif // NEWCAM_DEBUG #endif // NEWCAM_DEBUG
} }
//Displays a box.
void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b)
{
gDPPipeSync(gDisplayListHead++);
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255));
gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1);
gDPPipeSync(gDisplayListHead++);
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
}
//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :(
void newcam_change_setting(u8 toggle)
{
switch (newcam_option_selection)
{
case 0:
newcam_analogue ^= 1;
break;
case 1:
newcam_mouse ^= 1;
break;
case 2:
newcam_sensitivityX = newcam_clamp(newcam_sensitivityX + toggle, 10, 250);
break;
case 3:
newcam_sensitivityY = newcam_clamp(newcam_sensitivityY + toggle, 10, 250);
break;
case 4:
newcam_invertX ^= 1;
break;
case 5:
newcam_invertY ^= 1;
break;
case 6:
newcam_aggression = newcam_clamp(newcam_aggression + toggle, 0, 100);
break;
case 7:
newcam_panlevel = newcam_clamp(newcam_panlevel + toggle, 0, 100);
break;
}
}
void newcam_text(s16 x, s16 y, u8 str[], u8 col)
{
u8 textX;
textX = get_str_x_pos_from_center(x,str,10.0f);
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
print_generic_string(textX+1,y-1,str);
if (col != 0)
{
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
}
else
{
gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255);
}
print_generic_string(textX,y,str);
}
//Options menu
void newcam_display_options()
{
u8 i = 0;
u8 newstring[32];
s16 scroll;
s16 scrollpos;
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
print_hud_lut_string(HUD_LUT_GLOBAL, 118, 40, newcam_strings[2]);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
if (newcam_total>4)
{
newcam_display_box(272,90,280,208,0x80,0x80,0x80);
scrollpos = (54)*((f32)newcam_option_scroll/(newcam_total-4));
newcam_display_box(272,90+scrollpos,280,154+scrollpos,0xFF,0xFF,0xFF);
}
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT);
for (i = 0; i < newcam_total; i++)
{
scroll = 140-(32*i)+(newcam_option_scroll*32);
if (scroll <= 140 && scroll > 32)
{
newcam_text(160,scroll,newcam_options[i],newcam_option_selection-i);
switch (i)
{
case 0:
newcam_text(160,scroll-12,newcam_flags[newcam_analogue],newcam_option_selection-i);
break;
case 1:
newcam_text(160,scroll-12,newcam_flags[newcam_mouse],newcam_option_selection-i);
break;
case 2:
int_to_str(newcam_sensitivityX,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
case 3:
int_to_str(newcam_sensitivityY,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
case 4:
newcam_text(160,scroll-12,newcam_flags[newcam_invertX],newcam_option_selection-i);
break;
case 5:
newcam_text(160,scroll-12,newcam_flags[newcam_invertY],newcam_option_selection-i);
break;
case 6:
int_to_str(newcam_aggression,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
case 7:
int_to_str(newcam_panlevel,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
}
}
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]);
print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
}
//This has been separated for interesting reasons. Don't question it.
void newcam_render_option_text(void)
{
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
newcam_text(278,212,newcam_strings[newcam_option_open],1);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
void newcam_check_pause_buttons()
{
if (gPlayer1Controller->buttonPressed & R_TRIG)
{
if (newcam_option_open == 0)
{
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
newcam_option_open = 1;
}
else
{
#ifndef nosound
play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs);
#endif
newcam_option_open = 0;
newcam_save_settings();
}
}
if (newcam_option_open)
{
if (ABS(gPlayer1Controller->stickY) > 60)
{
newcam_option_timer -= 1;
if (newcam_option_timer <= 0)
{
switch (newcam_option_index)
{
case 0: newcam_option_index++; newcam_option_timer += 10; break;
default: newcam_option_timer += 3; break;
}
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickY >= 60)
{
newcam_option_selection--;
if (newcam_option_selection < 0)
newcam_option_selection = newcam_total-1;
}
else
{
newcam_option_selection++;
if (newcam_option_selection >= newcam_total)
newcam_option_selection = 0;
}
}
}
else
if (ABS(gPlayer1Controller->stickX) > 60)
{
newcam_option_timer -= 1;
if (newcam_option_timer <= 0)
{
switch (newcam_option_index)
{
case 0: newcam_option_index++; newcam_option_timer += 10; break;
default: newcam_option_timer += 3; break;
}
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickX >= 60)
newcam_change_setting(1);
else
newcam_change_setting(-1);
}
}
else
{
newcam_option_timer = 0;
newcam_option_index = 0;
}
while (newcam_option_scroll - newcam_option_selection < -3 && newcam_option_selection > newcam_option_scroll)
newcam_option_scroll +=1;
while (newcam_option_scroll + newcam_option_selection > 0 && newcam_option_selection < newcam_option_scroll)
newcam_option_scroll -=1;
}
}

View file

@ -22,6 +22,9 @@
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
#include "bettercamera.h" #include "bettercamera.h"
#endif #endif
#ifdef EXT_OPTIONS_MENU
#include "options_menu.h"
#endif
extern Gfx *gDisplayListHead; extern Gfx *gDisplayListHead;
extern s16 gCurrCourseNum; extern s16 gCurrCourseNum;
@ -2371,12 +2374,6 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) {
{ TEXT_EXIT_COURSE_DE } { TEXT_EXIT_COURSE_DE }
}; };
u8 textExitGame[][22] ={
{ TEXT_EXIT_GAME },
{ TEXT_EXIT_GAME_FR },
{ TEXT_EXIT_GAME_DE }
};
u8 textCameraAngleR[][24] = { u8 textCameraAngleR[][24] = {
{ TEXT_CAMERA_ANGLE_R }, { TEXT_CAMERA_ANGLE_R },
{ TEXT_CAMERA_ANGLE_R_FR }, { TEXT_CAMERA_ANGLE_R_FR },
@ -2384,27 +2381,23 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) {
}; };
#define textContinue textContinue[gInGameLanguage] #define textContinue textContinue[gInGameLanguage]
#define textExitCourse textExitCourse[gInGameLanguage] #define textExitCourse textExitCourse[gInGameLanguage]
#define textExitGame textExitGame[gInGameLanguage]
#define textCameraAngleR textCameraAngleR[gInGameLanguage] #define textCameraAngleR textCameraAngleR[gInGameLanguage]
#else #else
u8 textContinue[] = { TEXT_CONTINUE }; u8 textContinue[] = { TEXT_CONTINUE };
u8 textExitCourse[] = { TEXT_EXIT_COURSE }; u8 textExitCourse[] = { TEXT_EXIT_COURSE };
u8 textExitGame[] = { TEXT_EXIT_GAME };
u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R }; u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R };
#endif #endif
handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 4); // Index max raised to 4 from 3 handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3);
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha);
print_generic_string(x + 10, y - 2, textContinue); print_generic_string(x + 10, y - 2, textContinue);
print_generic_string(x + 10, y - 17, textExitCourse); print_generic_string(x + 10, y - 17, textExitCourse);
print_generic_string(x + 10, y - 33, textExitGame);
if (index[0] != 3) {
if (index[0] != 4) { print_generic_string(x + 10, y - 33, textCameraAngleR);
print_generic_string(x + 10, y - 48, textCameraAngleR);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end); gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
create_dl_translation_matrix(MENU_MTX_PUSH, x - X_VAL8, (y - ((index[0] - 1) * yIndex)) - Y_VAL8, 0); create_dl_translation_matrix(MENU_MTX_PUSH, x - X_VAL8, (y - ((index[0] - 1) * yIndex)) - Y_VAL8, 0);
@ -2412,10 +2405,8 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) {
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha);
gSPDisplayList(gDisplayListHead++, dl_draw_triangle); gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
} } else {
render_pause_camera_options(x - 42, y - 42, &gDialogCameraAngleIndex, 110);
if (index[0] == 4) {
render_pause_camera_options(x - 42, y - 57, &gDialogCameraAngleIndex, 110);
} }
} }
@ -2612,9 +2603,8 @@ s16 render_pause_courses_and_castle(void) {
#ifdef VERSION_EU #ifdef VERSION_EU
gInGameLanguage = eu_get_language(); gInGameLanguage = eu_get_language();
#endif #endif
#ifdef BETTERCAMERA #ifdef EXT_OPTIONS_MENU
if (newcam_option_open == 0) if (optmenu_open == 0) {
{
#endif #endif
switch (gDialogBoxState) { switch (gDialogBoxState) {
case DIALOG_STATE_OPENING: case DIALOG_STATE_OPENING:
@ -2656,7 +2646,7 @@ s16 render_pause_courses_and_castle(void) {
gDialogBoxState = DIALOG_STATE_OPENING; gDialogBoxState = DIALOG_STATE_OPENING;
gMenuMode = -1; gMenuMode = -1;
if (gDialogLineNum == 2 || gDialogLineNum == 3) { if (gDialogLineNum == 2) {
num = gDialogLineNum; num = gDialogLineNum;
} else { } else {
num = 1; num = 1;
@ -2691,15 +2681,13 @@ s16 render_pause_courses_and_castle(void) {
if (gDialogTextAlpha < 250) { if (gDialogTextAlpha < 250) {
gDialogTextAlpha += 25; gDialogTextAlpha += 25;
} }
#ifdef BETTERCAMERA #ifdef EXT_OPTIONS_MENU
} } else {
else
{
shade_screen(); shade_screen();
newcam_display_options(); optmenu_draw();
} }
newcam_check_pause_buttons(); optmenu_check_buttons();
newcam_render_option_text(); optmenu_draw_prompt();
#endif #endif
return 0; return 0;

View file

@ -32,6 +32,8 @@
#include "../pc/configfile.h" #include "../pc/configfile.h"
#define CONFIG_FILE "sm64config.txt" #define CONFIG_FILE "sm64config.txt"
#include "pc/cliopts.h"
#define PLAY_MODE_NORMAL 0 #define PLAY_MODE_NORMAL 0
#define PLAY_MODE_PAUSED 2 #define PLAY_MODE_PAUSED 2
#define PLAY_MODE_CHANGE_AREA 3 #define PLAY_MODE_CHANGE_AREA 3
@ -176,7 +178,8 @@ s8 D_8032C9E0 = 0;
u8 unused3[4]; u8 unused3[4];
u8 unused4[2]; u8 unused4[2];
// For configfile intro skipping
extern unsigned int configSkipIntro;
void basic_update(s16 *arg); void basic_update(s16 *arg);
@ -1214,7 +1217,7 @@ s32 init_level(void) {
if (gMarioState->action != ACT_UNINITIALIZED) { if (gMarioState->action != ACT_UNINITIALIZED) {
if (save_file_exists(gCurrSaveFileNum - 1)) { if (save_file_exists(gCurrSaveFileNum - 1)) {
set_mario_action(gMarioState, ACT_IDLE, 0); set_mario_action(gMarioState, ACT_IDLE, 0);
} else { } else if (gCLIOpts.SkipIntro == 0 && configSkipIntro == 0) {
set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0);
val4 = 1; val4 = 1;
} }
@ -1284,7 +1287,7 @@ s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum) {
#endif #endif
sWarpDest.type = WARP_TYPE_NOT_WARPING; sWarpDest.type = WARP_TYPE_NOT_WARPING;
sDelayedWarpOp = WARP_OP_NONE; sDelayedWarpOp = WARP_OP_NONE;
gShouldNotPlayCastleMusic = !save_file_exists(gCurrSaveFileNum - 1); gShouldNotPlayCastleMusic = !save_file_exists(gCurrSaveFileNum - 1) && gCLIOpts.SkipIntro == 0 && configSkipIntro == 0;
gCurrLevelNum = levelNum; gCurrLevelNum = levelNum;
gCurrCourseNum = COURSE_NONE; gCurrCourseNum = COURSE_NONE;

View file

@ -527,11 +527,15 @@ void set_object_visibility(struct Object *obj, s32 dist) {
f32 objY = obj->oPosY; f32 objY = obj->oPosY;
f32 objZ = obj->oPosZ; f32 objZ = obj->oPosZ;
#ifndef NODRAWINGDISTANCE
if (is_point_within_radius_of_mario(objX, objY, objZ, dist) == TRUE) { if (is_point_within_radius_of_mario(objX, objY, objZ, dist) == TRUE) {
#endif
obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
#ifndef NODRAWINGDISTANCE
} else { } else {
obj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; obj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
} }
#endif
} }
/** /**

View file

@ -984,7 +984,7 @@ BAD_RETURN(s16) cur_obj_reverse_animation(void) {
BAD_RETURN(s32) cur_obj_extend_animation_if_at_end(void) { BAD_RETURN(s32) cur_obj_extend_animation_if_at_end(void) {
s32 sp4 = o->header.gfx.unk38.animFrame; s32 sp4 = o->header.gfx.unk38.animFrame;
s32 sp0 = o->header.gfx.unk38.curAnim->unk08 - 2; s32 sp0 = o->header.gfx.unk38.curAnim->unk08 - 2;
if (sp4 == sp0) o->header.gfx.unk38.animFrame--; if (sp4 == sp0) o->header.gfx.unk38.animFrame--;
} }
@ -1595,6 +1595,10 @@ void obj_set_billboard(struct Object *obj) {
obj->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD; obj->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD;
} }
void obj_set_cylboard(struct Object *obj) {
obj->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD;
}
void cur_obj_set_hitbox_radius_and_height(f32 radius, f32 height) { void cur_obj_set_hitbox_radius_and_height(f32 radius, f32 height) {
o->hitboxRadius = radius; o->hitboxRadius = radius;
o->hitboxHeight = height; o->hitboxHeight = height;

View file

@ -292,6 +292,7 @@ extern void cur_obj_shake_y(f32);
void cur_obj_start_cam_event(struct Object *obj, s32 cameraEvent); void cur_obj_start_cam_event(struct Object *obj, s32 cameraEvent);
// extern ? set_mario_interact_hoot_if_in_range(?); // extern ? set_mario_interact_hoot_if_in_range(?);
void obj_set_billboard(struct Object *a0); void obj_set_billboard(struct Object *a0);
void obj_set_cylboard(struct Object *a0);
void cur_obj_set_hitbox_radius_and_height(f32,f32); void cur_obj_set_hitbox_radius_and_height(f32,f32);
void cur_obj_set_hurtbox_radius_and_height(f32,f32); void cur_obj_set_hurtbox_radius_and_height(f32,f32);
// extern ? obj_spawn_loot_coins(?); // extern ? obj_spawn_loot_coins(?);

525
src/game/options_menu.c Normal file
View file

@ -0,0 +1,525 @@
#ifdef EXT_OPTIONS_MENU
#include "sm64.h"
#include "include/text_strings.h"
#include "engine/math_util.h"
#include "audio/external.h"
#include "game/camera.h"
#include "game/level_update.h"
#include "game/print.h"
#include "game/segment2.h"
#include "game/save_file.h"
#ifdef BETTERCAMERA
#include "game/bettercamera.h"
#endif
#include "game/mario_misc.h"
#include "game/game_init.h"
#include "game/ingame_menu.h"
#include "game/options_menu.h"
#include "pc/configfile.h"
#include "pc/controller/controller_api.h"
#include <stdbool.h>
u8 optmenu_open = 0;
static u8 optmenu_binding = 0;
static u8 optmenu_bind_idx = 0;
// How to add stuff:
// strings: add them to include/text_strings.h.in
// and to menuStr[] / opts*Str[]
// options: add them to the relevant options list
// menus: add a new submenu definition and a new
// option to the optsMain list
static const u8 toggleStr[][16] = {
{ TEXT_OPT_DISABLED },
{ TEXT_OPT_ENABLED },
};
static const u8 menuStr[][32] = {
{ TEXT_OPT_HIGHLIGHT },
{ TEXT_OPT_BUTTON1 },
{ TEXT_OPT_BUTTON2 },
{ TEXT_OPT_OPTIONS },
{ TEXT_OPT_CAMERA },
{ TEXT_OPT_CONTROLS },
{ TEXT_OPT_VIDEO },
{ TEXT_OPT_AUDIO },
{ TEXT_EXIT_GAME },
};
static const u8 optsCameraStr[][32] = {
{ TEXT_OPT_CAMX },
{ TEXT_OPT_CAMY },
{ TEXT_OPT_INVERTX },
{ TEXT_OPT_INVERTY },
{ TEXT_OPT_CAMC },
{ TEXT_OPT_CAMP },
{ TEXT_OPT_ANALOGUE },
{ TEXT_OPT_MOUSE },
{ TEXT_OPT_CAMD },
};
static const u8 optsVideoStr[][32] = {
{ TEXT_OPT_FSCREEN },
{ TEXT_OPT_TEXFILTER },
{ TEXT_OPT_NEAREST },
{ TEXT_OPT_LINEAR },
};
static const u8 optsAudioStr[][32] = {
{ TEXT_OPT_MVOLUME },
};
static const u8 bindStr[][32] = {
{ TEXT_OPT_UNBOUND },
{ TEXT_OPT_PRESSKEY },
{ TEXT_BIND_A },
{ TEXT_BIND_B },
{ TEXT_BIND_START },
{ TEXT_BIND_L },
{ TEXT_BIND_R },
{ TEXT_BIND_Z },
{ TEXT_BIND_C_UP },
{ TEXT_BIND_C_DOWN },
{ TEXT_BIND_C_LEFT },
{ TEXT_BIND_C_RIGHT },
{ TEXT_BIND_UP },
{ TEXT_BIND_DOWN },
{ TEXT_BIND_LEFT },
{ TEXT_BIND_RIGHT },
};
static const u8 *filterChoices[] = {
optsVideoStr[2],
optsVideoStr[3],
};
enum OptType {
OPT_INVALID = 0,
OPT_TOGGLE,
OPT_CHOICE,
OPT_SCROLL,
OPT_SUBMENU,
OPT_BIND,
OPT_BUTTON,
};
struct SubMenu;
struct Option {
enum OptType type;
const u8 *label;
union {
u32 *uval;
bool *bval;
};
union {
struct {
const u8 **choices;
u32 numChoices;
};
struct {
u32 scrMin;
u32 scrMax;
u32 scrStep;
};
struct SubMenu *nextMenu;
void (*actionFn)(struct Option *, s32);
};
};
struct SubMenu {
struct SubMenu *prev; // this is set at runtime to avoid needless complication
const u8 *label;
struct Option *opts;
s32 numOpts;
s32 select;
s32 scroll;
};
/* helper macros */
#define DEF_OPT_TOGGLE(lbl, bv) \
{ .type = OPT_TOGGLE, .label = lbl, .bval = bv }
#define DEF_OPT_SCROLL(lbl, uv, min, max, st) \
{ .type = OPT_SCROLL, .label = lbl, .uval = uv, .scrMin = min, .scrMax = max, .scrStep = st }
#define DEF_OPT_CHOICE(lbl, uv, ch) \
{ .type = OPT_CHOICE, .label = lbl, .uval = uv, .choices = ch, .numChoices = sizeof(ch) / sizeof(ch[0]) }
#define DEF_OPT_SUBMENU(lbl, nm) \
{ .type = OPT_SUBMENU, .label = lbl, .nextMenu = nm }
#define DEF_OPT_BIND(lbl, uv) \
{ .type = OPT_BIND, .label = lbl, .uval = uv }
#define DEF_OPT_BUTTON(lbl, act) \
{ .type = OPT_BUTTON, .label = lbl, .actionFn = act }
#define DEF_SUBMENU(lbl, opt) \
{ .label = lbl, .opts = opt, .numOpts = sizeof(opt) / sizeof(opt[0]) }
/* button action functions */
static void optmenu_act_exit(UNUSED struct Option *self, s32 arg) {
if (!arg) exit(0); // only exit on A press and not directions
}
/* submenu option lists */
#ifdef BETTERCAMERA
static struct Option optsCamera[] = {
DEF_OPT_TOGGLE( optsCameraStr[6], &configEnableCamera ),
DEF_OPT_TOGGLE( optsCameraStr[7], &configCameraMouse ),
DEF_OPT_TOGGLE( optsCameraStr[2], &configCameraInvertX ),
DEF_OPT_TOGGLE( optsCameraStr[3], &configCameraInvertY ),
DEF_OPT_SCROLL( optsCameraStr[0], &configCameraXSens, 10, 250, 1 ),
DEF_OPT_SCROLL( optsCameraStr[1], &configCameraYSens, 10, 250, 1 ),
DEF_OPT_SCROLL( optsCameraStr[4], &configCameraAggr, 0, 100, 1 ),
DEF_OPT_SCROLL( optsCameraStr[5], &configCameraPan, 0, 100, 1 ),
DEF_OPT_SCROLL( optsCameraStr[8], &configCameraDegrade, 0, 100, 1 ),
};
#endif
static struct Option optsControls[] = {
DEF_OPT_BIND( bindStr[ 2], configKeyA ),
DEF_OPT_BIND( bindStr[ 3], configKeyB ),
DEF_OPT_BIND( bindStr[ 4], configKeyStart ),
DEF_OPT_BIND( bindStr[ 5], configKeyL ),
DEF_OPT_BIND( bindStr[ 6], configKeyR ),
DEF_OPT_BIND( bindStr[ 7], configKeyZ ),
DEF_OPT_BIND( bindStr[ 8], configKeyCUp ),
DEF_OPT_BIND( bindStr[ 9], configKeyCDown ),
DEF_OPT_BIND( bindStr[10], configKeyCLeft ),
DEF_OPT_BIND( bindStr[11], configKeyCRight ),
DEF_OPT_BIND( bindStr[12], configKeyStickUp ),
DEF_OPT_BIND( bindStr[13], configKeyStickDown ),
DEF_OPT_BIND( bindStr[14], configKeyStickLeft ),
DEF_OPT_BIND( bindStr[15], configKeyStickRight ),
};
static struct Option optsVideo[] = {
DEF_OPT_TOGGLE( optsVideoStr[0], &configFullscreen ),
DEF_OPT_CHOICE( optsVideoStr[1], &configFiltering, filterChoices ),
};
static struct Option optsAudio[] = {
DEF_OPT_SCROLL( optsAudioStr[0], &configMasterVolume, 0, MAX_VOLUME, 1 ),
};
/* submenu definitions */
#ifdef BETTERCAMERA
static struct SubMenu menuCamera = DEF_SUBMENU( menuStr[4], optsCamera );
#endif
static struct SubMenu menuControls = DEF_SUBMENU( menuStr[5], optsControls );
static struct SubMenu menuVideo = DEF_SUBMENU( menuStr[6], optsVideo );
static struct SubMenu menuAudio = DEF_SUBMENU( menuStr[7], optsAudio );
/* main options menu definition */
static struct Option optsMain[] = {
#ifdef BETTERCAMERA
DEF_OPT_SUBMENU( menuStr[4], &menuCamera ),
#endif
DEF_OPT_SUBMENU( menuStr[5], &menuControls ),
DEF_OPT_SUBMENU( menuStr[6], &menuVideo ),
DEF_OPT_SUBMENU( menuStr[7], &menuAudio ),
DEF_OPT_BUTTON ( menuStr[8], optmenu_act_exit ),
};
static struct SubMenu menuMain = DEF_SUBMENU( menuStr[3], optsMain );
/* implementation */
static s32 optmenu_option_timer = 0;
static u8 optmenu_hold_count = 0;
static struct SubMenu *currentMenu = &menuMain;
static inline s32 wrap_add(s32 a, const s32 b, const s32 min, const s32 max) {
a += b;
if (a < min) a = max - (min - a) + 1;
else if (a > max) a = min + (a - max) - 1;
return a;
}
static void uint_to_hex(u32 num, u8 *dst) {
u8 places = 4;
while (places--) {
const u32 digit = num & 0xF;
dst[places] = digit;
num >>= 4;
}
dst[4] = DIALOG_CHAR_TERMINATOR;
}
//Displays a box.
static void optmenu_draw_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) {
gDPPipeSync(gDisplayListHead++);
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255));
gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1);
gDPPipeSync(gDisplayListHead++);
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
}
static void optmenu_draw_text(s16 x, s16 y, const u8 *str, u8 col) {
const u8 textX = get_str_x_pos_from_center(x, (u8*)str, 10.0f);
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
print_generic_string(textX+1, y-1, str);
if (col == 0) {
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
} else {
gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255);
}
print_generic_string(textX, y, str);
}
static void optmenu_draw_opt(const struct Option *opt, s16 x, s16 y, u8 sel) {
u8 buf[32] = { 0 };
if (opt->type == OPT_SUBMENU || opt->type == OPT_BUTTON)
y -= 6;
optmenu_draw_text(x, y, opt->label, sel);
switch (opt->type) {
case OPT_TOGGLE:
optmenu_draw_text(x, y-13, toggleStr[(int)*opt->bval], sel);
break;
case OPT_CHOICE:
optmenu_draw_text(x, y-13, opt->choices[*opt->uval], sel);
break;
case OPT_SCROLL:
int_to_str(*opt->uval, buf);
optmenu_draw_text(x, y-13, buf, sel);
break;
case OPT_BIND:
x = 112;
for (u8 i = 0; i < MAX_BINDS; ++i, x += 48) {
const u8 white = (sel && (optmenu_bind_idx == i));
// TODO: button names
if (opt->uval[i] == VK_INVALID) {
if (optmenu_binding && white)
optmenu_draw_text(x, y-13, bindStr[1], 1);
else
optmenu_draw_text(x, y-13, bindStr[0], white);
} else {
uint_to_hex(opt->uval[i], buf);
optmenu_draw_text(x, y-13, buf, white);
}
}
break;
default: break;
};
}
static void optmenu_opt_change(struct Option *opt, s32 val) {
switch (opt->type) {
case OPT_TOGGLE:
*opt->bval = !*opt->bval;
break;
case OPT_CHOICE:
*opt->uval = wrap_add(*opt->uval, val, 0, opt->numChoices - 1);
break;
case OPT_SCROLL:
*opt->uval = wrap_add(*opt->uval, opt->scrStep * val, opt->scrMin, opt->scrMax);
break;
case OPT_SUBMENU:
opt->nextMenu->prev = currentMenu;
currentMenu = opt->nextMenu;
break;
case OPT_BUTTON:
if (opt->actionFn)
opt->actionFn(opt, val);
break;
case OPT_BIND:
if (val == 0xFF) {
// clear the bind
opt->uval[optmenu_bind_idx] = VK_INVALID;
} else if (val == 0) {
opt->uval[optmenu_bind_idx] = VK_INVALID;
optmenu_binding = 1;
controller_get_raw_key(); // clear the last key, which is probably A
} else {
optmenu_bind_idx = wrap_add(optmenu_bind_idx, val, 0, MAX_BINDS - 1);
}
break;
default: break;
}
}
static inline s16 get_hudstr_centered_x(const s16 sx, const u8 *str) {
const u8 *chr = str;
s16 len = 0;
while (*chr != GLOBAR_CHAR_TERMINATOR) ++chr, ++len;
return sx - len * 6; // stride is 12
}
//Options menu
void optmenu_draw(void) {
s16 scroll;
s16 scrollpos;
const s16 labelX = get_hudstr_centered_x(160, currentMenu->label);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
print_hud_lut_string(HUD_LUT_GLOBAL, labelX, 40, currentMenu->label);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
if (currentMenu->numOpts > 4) {
optmenu_draw_box(272, 90, 280, 208, 0x80, 0x80, 0x80);
scrollpos = 54 * ((f32)currentMenu->scroll / (currentMenu->numOpts-4));
optmenu_draw_box(272, 90+scrollpos, 280, 154+scrollpos, 0xFF, 0xFF, 0xFF);
}
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT);
for (u8 i = 0; i < currentMenu->numOpts; i++) {
scroll = 140 - 32 * i + currentMenu->scroll * 32;
// FIXME: just start from the first visible option bruh
if (scroll <= 140 && scroll > 32)
optmenu_draw_opt(&currentMenu->opts[i], 160, scroll, (currentMenu->select == i));
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]);
print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
}
//This has been separated for interesting reasons. Don't question it.
void optmenu_draw_prompt(void) {
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
optmenu_draw_text(278, 212, menuStr[1 + optmenu_open], 0);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
void optmenu_toggle(void) {
if (optmenu_open == 0) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
currentMenu = &menuMain;
optmenu_open = 1;
} else {
#ifndef nosound
play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs);
#endif
optmenu_open = 0;
#ifdef BETTERCAMERA
newcam_init_settings(); // load bettercam settings from config vars
#endif
controller_reconfigure(); // rebind using new config values
configfile_save(CONFIG_FILE);
}
}
void optmenu_check_buttons(void) {
if (optmenu_binding) {
u32 key = controller_get_raw_key();
if (key != VK_INVALID) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
currentMenu->opts[currentMenu->select].uval[optmenu_bind_idx] = key;
optmenu_binding = 0;
optmenu_option_timer = 12;
}
return;
}
if (gPlayer1Controller->buttonPressed & R_TRIG)
optmenu_toggle();
if (!optmenu_open) return;
u8 allowInput = 0;
optmenu_option_timer--;
if (optmenu_option_timer <= 0) {
if (optmenu_hold_count == 0) {
optmenu_hold_count++;
optmenu_option_timer = 10;
} else {
optmenu_option_timer = 3;
}
allowInput = 1;
}
if (ABS(gPlayer1Controller->stickY) > 60) {
if (allowInput) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickY >= 60) {
currentMenu->select--;
if (currentMenu->select < 0)
currentMenu->select = currentMenu->numOpts-1;
} else {
currentMenu->select++;
if (currentMenu->select >= currentMenu->numOpts)
currentMenu->select = 0;
}
if (currentMenu->select < currentMenu->scroll)
currentMenu->scroll = currentMenu->select;
else if (currentMenu->select > currentMenu->scroll + 3)
currentMenu->scroll = currentMenu->select - 3;
}
} else if (ABS(gPlayer1Controller->stickX) > 60) {
if (allowInput) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickX >= 60)
optmenu_opt_change(&currentMenu->opts[currentMenu->select], 1);
else
optmenu_opt_change(&currentMenu->opts[currentMenu->select], -1);
}
} else if (gPlayer1Controller->buttonPressed & A_BUTTON) {
if (allowInput) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
optmenu_opt_change(&currentMenu->opts[currentMenu->select], 0);
}
} else if (gPlayer1Controller->buttonPressed & B_BUTTON) {
if (allowInput) {
if (currentMenu->prev) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
currentMenu = currentMenu->prev;
} else {
// can't go back, exit the menu altogether
optmenu_toggle();
}
}
} else if (gPlayer1Controller->buttonPressed & Z_TRIG) {
// HACK: clear binds with Z
if (allowInput && currentMenu->opts[currentMenu->select].type == OPT_BIND)
optmenu_opt_change(&currentMenu->opts[currentMenu->select], 0xFF);
} else if (gPlayer1Controller->buttonPressed & START_BUTTON) {
if (allowInput) optmenu_toggle();
} else {
optmenu_hold_count = 0;
optmenu_option_timer = 0;
}
}
#endif // EXT_OPTIONS_MENU

11
src/game/options_menu.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef OPTIONS_MENU_H
#define OPTIONS_MENU_H
void optmenu_toggle(void);
void optmenu_draw(void);
void optmenu_draw_prompt(void);
void optmenu_check_buttons(void);
extern u8 optmenu_open;
#endif // OPTIONS_MENU_H

View file

@ -796,6 +796,9 @@ static void geo_process_object(struct Object *node) {
if (node->header.gfx.throwMatrix != NULL) { if (node->header.gfx.throwMatrix != NULL) {
mtxf_mul(gMatStack[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix, mtxf_mul(gMatStack[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix,
gMatStack[gMatStackIndex]); gMatStack[gMatStackIndex]);
} else if (node->header.gfx.node.flags & GRAPH_RENDER_CYLBOARD) {
mtxf_cylboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
node->header.gfx.pos, gCurGraphNodeCamera->roll);
} else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) {
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
node->header.gfx.pos, gCurGraphNodeCamera->roll); node->header.gfx.pos, gCurGraphNodeCamera->roll);

View file

@ -22,6 +22,7 @@
#include "gd_math.h" #include "gd_math.h"
#include "shape_helper.h" #include "shape_helper.h"
#include "config.h"
#include "gfx_dimensions.h" #include "gfx_dimensions.h"
#define MAX_GD_DLS 1000 #define MAX_GD_DLS 1000
@ -2302,7 +2303,7 @@ void start_view_dl(struct ObjView *view) {
uly = lry - 1.0f; uly = lry - 1.0f;
} }
gDPSetScissor(next_gfx(), G_SC_NON_INTERLACE, ulx, uly, lrx, lry); // gDPSetScissor(next_gfx(), G_SC_NON_INTERLACE, ulx, uly, lrx, lry); // N64 only
gSPClearGeometryMode(next_gfx(), 0xFFFFFFFF); gSPClearGeometryMode(next_gfx(), 0xFFFFFFFF);
gSPSetGeometryMode(next_gfx(), G_LIGHTING | G_CULL_BACK | G_SHADING_SMOOTH | G_SHADE); gSPSetGeometryMode(next_gfx(), G_LIGHTING | G_CULL_BACK | G_SHADING_SMOOTH | G_SHADE);
if (view->flags & VIEW_ALLOC_ZBUF) { if (view->flags & VIEW_ALLOC_ZBUF) {
@ -2961,9 +2962,9 @@ void update_cursor(void) {
reset_dlnum_indices(sHandShape->gdDls[gGdFrameBuf]); reset_dlnum_indices(sHandShape->gdDls[gGdFrameBuf]);
if (gGdCtrl.btnApressed) { if (gGdCtrl.btnApressed) {
gd_put_sprite((u16 *) gd_texture_hand_closed, sHandView->upperLeft.x, sHandView->upperLeft.y, 0x20, 0x20); gd_put_sprite((u16 *) gd_texture_hand_closed, sHandView->upperLeft.x, sHandView->upperLeft.y, 32, 32);
} else { } else {
gd_put_sprite((u16 *) gd_texture_hand_open, sHandView->upperLeft.x, sHandView->upperLeft.y, 0x20, 0x20); gd_put_sprite((u16 *) gd_texture_hand_open, sHandView->upperLeft.x, sHandView->upperLeft.y, 32, 32);
} }
gd_enddlsplist_parent(); gd_enddlsplist_parent();
@ -3419,7 +3420,8 @@ void Unknown801A5FF8(struct ObjGroup *arg0) {
void gd_put_sprite(u16 *sprite, s32 x, s32 y, s32 wx, s32 wy) { void gd_put_sprite(u16 *sprite, s32 x, s32 y, s32 wx, s32 wy) {
s32 c; // 5c s32 c; // 5c
s32 r; // 58 s32 r; // 58
f32 aspect = GFX_DIMENSIONS_ASPECT_RATIO * 0.75; // Must be game screen aspect ratio, not GFX window aspect ratio
f32 aspect = ((float) SCREEN_WIDTH) / ((float) SCREEN_HEIGHT ) * 0.75;
x *= aspect; x *= aspect;
gSPDisplayList(next_gfx(), osVirtualToPhysical(gd_dl_sprite_start_tex_block)); gSPDisplayList(next_gfx(), osVirtualToPhysical(gd_dl_sprite_start_tex_block));

View file

@ -10,6 +10,7 @@ struct AudioAPI {
int (*buffered)(void); int (*buffered)(void);
int (*get_desired_buffered)(void); int (*get_desired_buffered)(void);
void (*play)(const uint8_t *buf, size_t len); void (*play)(const uint8_t *buf, size_t len);
void (*shutdown)(void);
}; };
#endif #endif

View file

@ -15,9 +15,13 @@ static int audio_null_get_desired_buffered(void) {
static void audio_null_play(const uint8_t *buf, size_t len) { static void audio_null_play(const uint8_t *buf, size_t len) {
} }
static void audio_null_shutdown(void) {
}
struct AudioAPI audio_null = { struct AudioAPI audio_null = {
audio_null_init, audio_null_init,
audio_null_buffered, audio_null_buffered,
audio_null_get_desired_buffered, audio_null_get_desired_buffered,
audio_null_play audio_null_play,
}; audio_null_shutdown
};

View file

@ -40,9 +40,21 @@ static void audio_sdl_play(const uint8_t *buf, size_t len) {
} }
} }
static void audio_sdl_shutdown(void)
{
if (SDL_WasInit(SDL_INIT_AUDIO)) {
if (dev != 0) {
SDL_CloseAudioDevice(dev);
dev = 0;
}
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
}
struct AudioAPI audio_sdl = { struct AudioAPI audio_sdl = {
audio_sdl_init, audio_sdl_init,
audio_sdl_buffered, audio_sdl_buffered,
audio_sdl_get_desired_buffered, audio_sdl_get_desired_buffered,
audio_sdl_play audio_sdl_play,
audio_sdl_shutdown
}; };

25
src/pc/cliopts.c Normal file
View file

@ -0,0 +1,25 @@
#include "cliopts.h"
#include <strings.h>
struct PCCLIOptions gCLIOpts;
void parse_cli_opts(int argc, char* argv[])
{
// Initialize options with false values.
gCLIOpts.SkipIntro = 0;
gCLIOpts.FullScreen = 0;
// Scan arguments for options
if (argc > 1)
{
int i;
for (i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--skip-intro") == 0) // Skip Peach Intro
gCLIOpts.SkipIntro = 1;
if (strcmp(argv[i], "--fullscreen") == 0) // Open game in fullscreen
gCLIOpts.FullScreen = 1;
}
}
}

11
src/pc/cliopts.h Normal file
View file

@ -0,0 +1,11 @@
#include "sm64.h"
struct PCCLIOptions
{
u8 SkipIntro;
u8 FullScreen;
};
extern struct PCCLIOptions gCLIOpts;
void parse_cli_opts(int argc, char* argv[]);

View file

@ -7,6 +7,7 @@
#include <ctype.h> #include <ctype.h>
#include "configfile.h" #include "configfile.h"
#include "controller/controller_api.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@ -14,6 +15,7 @@ enum ConfigOptionType {
CONFIG_TYPE_BOOL, CONFIG_TYPE_BOOL,
CONFIG_TYPE_UINT, CONFIG_TYPE_UINT,
CONFIG_TYPE_FLOAT, CONFIG_TYPE_FLOAT,
CONFIG_TYPE_BIND,
}; };
struct ConfigOption { struct ConfigOption {
@ -29,74 +31,60 @@ struct ConfigOption {
/* /*
*Config options and default values *Config options and default values
*/ */
bool configFullscreen = false;
// Keyboard mappings (scancode values) // Video/audio stuff
unsigned int configKeyA = 0x26; bool configFullscreen = false;
unsigned int configKeyB = 0x33; unsigned int configFiltering = 1; // 0=force nearest, 1=linear, (TODO) 2=three-point
unsigned int configKeyStart = 0x39; unsigned int configMasterVolume = MAX_VOLUME; // 0 - MAX_VOLUME
unsigned int configKeyL = 0x34;
unsigned int configKeyR = 0x36; // Keyboard mappings (VK_ values, by default keyboard/gamepad/mouse)
unsigned int configKeyZ = 0x25; unsigned int configKeyA[MAX_BINDS] = { 0x0026, 0x1000, 0x1103 };
unsigned int configKeyCUp = 0x148; unsigned int configKeyB[MAX_BINDS] = { 0x0033, 0x1002, 0x1101 };
unsigned int configKeyCDown = 0x150; unsigned int configKeyStart[MAX_BINDS] = { 0x0039, 0x1006, VK_INVALID };
unsigned int configKeyCLeft = 0x14B; unsigned int configKeyL[MAX_BINDS] = { 0x0034, 0x1007, 0x1104 };
unsigned int configKeyCRight = 0x14D; unsigned int configKeyR[MAX_BINDS] = { 0x0036, 0x100A, 0x1105 };
unsigned int configKeyStickUp = 0x11; unsigned int configKeyZ[MAX_BINDS] = { 0x0025, 0x1009, 0x1102 };
unsigned int configKeyStickDown = 0x1F; unsigned int configKeyCUp[MAX_BINDS] = { 0x0148, VK_INVALID, VK_INVALID };
unsigned int configKeyStickLeft = 0x1E; unsigned int configKeyCDown[MAX_BINDS] = { 0x0150, VK_INVALID, VK_INVALID };
unsigned int configKeyStickRight = 0x20; unsigned int configKeyCLeft[MAX_BINDS] = { 0x014B, VK_INVALID, VK_INVALID };
// Gamepad mappings (SDL_GameControllerButton values) unsigned int configKeyCRight[MAX_BINDS] = { 0x014D, VK_INVALID, VK_INVALID };
unsigned int configJoyA = 0; unsigned int configKeyStickUp[MAX_BINDS] = { 0x0011, VK_INVALID, VK_INVALID };
unsigned int configJoyB = 2; unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID };
unsigned int configJoyStart = 6; unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID };
unsigned int configJoyL = 7; unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID };
unsigned int configJoyR = 10;
unsigned int configJoyZ = 9;
// Mouse button mappings (0 for none, 1 for left, 2 for middle, 3 for right)
unsigned int configMouseA = 3;
unsigned int configMouseB = 1;
unsigned int configMouseL = 4;
unsigned int configMouseR = 5;
unsigned int configMouseZ = 2;
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
// BetterCamera settings // BetterCamera settings
unsigned int configCameraXSens = 50; unsigned int configCameraXSens = 50;
unsigned int configCameraYSens = 50; unsigned int configCameraYSens = 50;
unsigned int configCameraAggr = 0; unsigned int configCameraAggr = 0;
unsigned int configCameraPan = 0; unsigned int configCameraPan = 0;
unsigned int configCameraDegrade = 10; // 0 - 100%
bool configCameraInvertX = false; bool configCameraInvertX = false;
bool configCameraInvertY = false; bool configCameraInvertY = false;
bool configEnableCamera = false; bool configEnableCamera = false;
bool configCameraMouse = false; bool configCameraMouse = false;
#endif #endif
unsigned int configSkipIntro = 0;
static const struct ConfigOption options[] = { static const struct ConfigOption options[] = {
{.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen}, {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen},
{.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA}, {.name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configFiltering},
{.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB}, {.name = "master_volume", .type = CONFIG_TYPE_UINT, .uintValue = &configMasterVolume},
{.name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart}, {.name = "key_a", .type = CONFIG_TYPE_BIND, .uintValue = configKeyA},
{.name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL}, {.name = "key_b", .type = CONFIG_TYPE_BIND, .uintValue = configKeyB},
{.name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR}, {.name = "key_start", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStart},
{.name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ}, {.name = "key_l", .type = CONFIG_TYPE_BIND, .uintValue = configKeyL},
{.name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp}, {.name = "key_r", .type = CONFIG_TYPE_BIND, .uintValue = configKeyR},
{.name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown}, {.name = "key_z", .type = CONFIG_TYPE_BIND, .uintValue = configKeyZ},
{.name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft}, {.name = "key_cup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCUp},
{.name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight}, {.name = "key_cdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCDown},
{.name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp}, {.name = "key_cleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCLeft},
{.name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown}, {.name = "key_cright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCRight},
{.name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft}, {.name = "key_stickup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickUp},
{.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight}, {.name = "key_stickdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickDown},
{.name = "joy_a", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyA}, {.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft},
{.name = "joy_b", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyB}, {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight},
{.name = "joy_start", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyStart},
{.name = "joy_l", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyL},
{.name = "joy_r", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyR},
{.name = "joy_z", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyZ},
{.name = "mouse_a", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseA},
{.name = "mouse_b", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseB},
{.name = "mouse_l", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseL},
{.name = "mouse_r", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseR},
{.name = "mouse_z", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseZ},
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
{.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera}, {.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera},
{.name = "bettercam_mouse_look", .type = CONFIG_TYPE_BOOL, .boolValue = &configCameraMouse}, {.name = "bettercam_mouse_look", .type = CONFIG_TYPE_BOOL, .boolValue = &configCameraMouse},
@ -106,7 +94,9 @@ static const struct ConfigOption options[] = {
{.name = "bettercam_ysens", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraYSens}, {.name = "bettercam_ysens", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraYSens},
{.name = "bettercam_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr}, {.name = "bettercam_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr},
{.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan}, {.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan},
{.name = "bettercam_degrade", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraDegrade},
#endif #endif
{.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, // Add this back!
}; };
// Reads an entire line from a file (excluding the newline character) and returns an allocated string // Reads an entire line from a file (excluding the newline character) and returns an allocated string
@ -207,9 +197,13 @@ void configfile_load(const char *filename) {
while (isspace(*p)) while (isspace(*p))
p++; p++;
if (!*p || *p == '#') // comment or empty line
continue;
numTokens = tokenize_string(p, 2, tokens); numTokens = tokenize_string(p, 2, tokens);
if (numTokens != 0) { if (numTokens != 0) {
if (numTokens == 2) { if (numTokens >= 2) {
const struct ConfigOption *option = NULL; const struct ConfigOption *option = NULL;
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
@ -231,6 +225,10 @@ void configfile_load(const char *filename) {
case CONFIG_TYPE_UINT: case CONFIG_TYPE_UINT:
sscanf(tokens[1], "%u", option->uintValue); sscanf(tokens[1], "%u", option->uintValue);
break; break;
case CONFIG_TYPE_BIND:
for (int i = 0; i < MAX_BINDS && i < numTokens - 1; ++i)
sscanf(tokens[i + 1], "%x", option->uintValue + i);
break;
case CONFIG_TYPE_FLOAT: case CONFIG_TYPE_FLOAT:
sscanf(tokens[1], "%f", option->floatValue); sscanf(tokens[1], "%f", option->floatValue);
break; break;
@ -273,6 +271,12 @@ void configfile_save(const char *filename) {
case CONFIG_TYPE_FLOAT: case CONFIG_TYPE_FLOAT:
fprintf(file, "%s %f\n", option->name, *option->floatValue); fprintf(file, "%s %f\n", option->name, *option->floatValue);
break; break;
case CONFIG_TYPE_BIND:
fprintf(file, "%s ", option->name);
for (int i = 0; i < MAX_BINDS; ++i)
fprintf(file, "%04x ", option->uintValue[i]);
fprintf(file, "\n");
break;
default: default:
assert(0); // unknown type assert(0); // unknown type
} }

View file

@ -5,38 +5,33 @@
#define CONFIG_FILE "sm64config.txt" #define CONFIG_FILE "sm64config.txt"
#define MAX_BINDS 3
#define MAX_VOLUME 127
#define VOLUME_SHIFT 7
extern bool configFullscreen; extern bool configFullscreen;
extern unsigned int configKeyA; extern unsigned int configFiltering;
extern unsigned int configKeyB; extern unsigned int configMasterVolume;
extern unsigned int configKeyStart; extern unsigned int configKeyA[];
extern unsigned int configKeyL; extern unsigned int configKeyB[];
extern unsigned int configKeyR; extern unsigned int configKeyStart[];
extern unsigned int configKeyZ; extern unsigned int configKeyL[];
extern unsigned int configKeyCUp; extern unsigned int configKeyR[];
extern unsigned int configKeyCDown; extern unsigned int configKeyZ[];
extern unsigned int configKeyCLeft; extern unsigned int configKeyCUp[];
extern unsigned int configKeyCRight; extern unsigned int configKeyCDown[];
extern unsigned int configKeyStickUp; extern unsigned int configKeyCLeft[];
extern unsigned int configKeyStickDown; extern unsigned int configKeyCRight[];
extern unsigned int configKeyStickLeft; extern unsigned int configKeyStickUp[];
extern unsigned int configKeyStickRight; extern unsigned int configKeyStickDown[];
extern unsigned int configJoyA; extern unsigned int configKeyStickLeft[];
extern unsigned int configJoyB; extern unsigned int configKeyStickRight[];
extern unsigned int configJoyStart;
extern unsigned int configJoyL;
extern unsigned int configJoyR;
extern unsigned int configJoyZ;
extern unsigned int configMouseA;
extern unsigned int configMouseB;
extern unsigned int configMouseStart;
extern unsigned int configMouseL;
extern unsigned int configMouseR;
extern unsigned int configMouseZ;
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
extern unsigned int configCameraXSens; extern unsigned int configCameraXSens;
extern unsigned int configCameraYSens; extern unsigned int configCameraYSens;
extern unsigned int configCameraAggr; extern unsigned int configCameraAggr;
extern unsigned int configCameraPan; extern unsigned int configCameraPan;
extern unsigned int configCameraDegrade;
extern bool configCameraInvertX; extern bool configCameraInvertX;
extern bool configCameraInvertY; extern bool configCameraInvertY;
extern bool configEnableCamera; extern bool configEnableCamera;

View file

@ -2,15 +2,25 @@
#define CONTROLLER_API #define CONTROLLER_API
#define DEADZONE 4960 #define DEADZONE 4960
#define VK_INVALID 0xFFFF
// Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors #define VK_SIZE 0x1000
// Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera
#include <ultra64.h> #include <ultra64.h>
struct ControllerAPI { struct ControllerAPI {
void (*init)(void); const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000)
void (*read)(OSContPad *pad); void (*init)(void); // call once, also calls reconfig()
void (*read)(OSContPad *pad); // read controller and update N64 pad values
u32 (*rawkey)(void); // returns last pressed virtual key or VK_INVALID if none
void (*reconfig)(void); // (optional) call when bindings have changed
void (*shutdown)(void); // (optional) call in osContReset
}; };
// used for binding keys
u32 controller_get_raw_key(void);
void controller_reconfigure(void);
// calls the shutdown() function of all controller subsystems
void controller_shutdown(void);
#endif #endif

View file

@ -56,3 +56,25 @@ void osContGetReadData(OSContPad *pad) {
controller_implementations[i]->read(pad); controller_implementations[i]->read(pad);
} }
} }
u32 controller_get_raw_key(void) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
u32 vk = controller_implementations[i]->rawkey();
if (vk != VK_INVALID) return vk + controller_implementations[i]->vkbase;
}
return VK_INVALID;
}
void controller_shutdown(void) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
if (controller_implementations[i]->shutdown)
controller_implementations[i]->shutdown();
}
}
void controller_reconfigure(void) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
if (controller_implementations[i]->reconfig)
controller_implementations[i]->reconfig();
}
}

View file

@ -8,14 +8,19 @@
#endif #endif
#include "../configfile.h" #include "../configfile.h"
#include "controller_keyboard.h"
static int keyboard_buttons_down; static int keyboard_buttons_down;
static int keyboard_mapping[14][2]; #define MAX_KEYBINDS 64
static int keyboard_mapping[MAX_KEYBINDS][2];
static int num_keybinds = 0;
static u32 keyboard_lastkey = VK_INVALID;
static int keyboard_map_scancode(int scancode) { static int keyboard_map_scancode(int scancode) {
int ret = 0; int ret = 0;
for (size_t i = 0; i < sizeof(keyboard_mapping) / sizeof(keyboard_mapping[0]); i++) { for (int i = 0; i < num_keybinds; i++) {
if (keyboard_mapping[i][0] == scancode) { if (keyboard_mapping[i][0] == scancode) {
ret |= keyboard_mapping[i][1]; ret |= keyboard_mapping[i][1];
} }
@ -26,12 +31,15 @@ static int keyboard_map_scancode(int scancode) {
bool keyboard_on_key_down(int scancode) { bool keyboard_on_key_down(int scancode) {
int mapped = keyboard_map_scancode(scancode); int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= mapped; keyboard_buttons_down |= mapped;
keyboard_lastkey = scancode;
return mapped != 0; return mapped != 0;
} }
bool keyboard_on_key_up(int scancode) { bool keyboard_on_key_up(int scancode) {
int mapped = keyboard_map_scancode(scancode); int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~mapped; keyboard_buttons_down &= ~mapped;
if (keyboard_lastkey == (u32) scancode)
keyboard_lastkey = VK_INVALID;
return mapped != 0; return mapped != 0;
} }
@ -39,28 +47,38 @@ void keyboard_on_all_keys_up(void) {
keyboard_buttons_down = 0; keyboard_buttons_down = 0;
} }
static void set_keyboard_mapping(int index, int mask, int scancode) { static void keyboard_add_binds(int mask, unsigned int *scancode) {
keyboard_mapping[index][0] = scancode; for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) {
keyboard_mapping[index][1] = mask; if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) {
keyboard_mapping[num_keybinds][0] = scancode[i];
keyboard_mapping[num_keybinds][1] = mask;
num_keybinds++;
}
}
}
static void keyboard_bindkeys(void) {
bzero(keyboard_mapping, sizeof(keyboard_mapping));
num_keybinds = 0;
keyboard_add_binds(0x80000, configKeyStickUp);
keyboard_add_binds(0x10000, configKeyStickLeft);
keyboard_add_binds(0x40000, configKeyStickDown);
keyboard_add_binds(0x20000, configKeyStickRight);
keyboard_add_binds(A_BUTTON, configKeyA);
keyboard_add_binds(B_BUTTON, configKeyB);
keyboard_add_binds(Z_TRIG, configKeyZ);
keyboard_add_binds(U_CBUTTONS, configKeyCUp);
keyboard_add_binds(L_CBUTTONS, configKeyCLeft);
keyboard_add_binds(D_CBUTTONS, configKeyCDown);
keyboard_add_binds(R_CBUTTONS, configKeyCRight);
keyboard_add_binds(L_TRIG, configKeyL);
keyboard_add_binds(R_TRIG, configKeyR);
keyboard_add_binds(START_BUTTON, configKeyStart);
} }
static void keyboard_init(void) { static void keyboard_init(void) {
int i = 0; keyboard_bindkeys();
set_keyboard_mapping(i++, 0x80000, configKeyStickUp);
set_keyboard_mapping(i++, 0x10000, configKeyStickLeft);
set_keyboard_mapping(i++, 0x40000, configKeyStickDown);
set_keyboard_mapping(i++, 0x20000, configKeyStickRight);
set_keyboard_mapping(i++, A_BUTTON, configKeyA);
set_keyboard_mapping(i++, B_BUTTON, configKeyB);
set_keyboard_mapping(i++, Z_TRIG, configKeyZ);
set_keyboard_mapping(i++, U_CBUTTONS, configKeyCUp);
set_keyboard_mapping(i++, L_CBUTTONS, configKeyCLeft);
set_keyboard_mapping(i++, D_CBUTTONS, configKeyCDown);
set_keyboard_mapping(i++, R_CBUTTONS, configKeyCRight);
set_keyboard_mapping(i++, L_TRIG, configKeyL);
set_keyboard_mapping(i++, R_TRIG, configKeyR);
set_keyboard_mapping(i++, START_BUTTON, configKeyStart);
#ifdef TARGET_WEB #ifdef TARGET_WEB
controller_emscripten_keyboard_init(); controller_emscripten_keyboard_init();
@ -83,7 +101,20 @@ static void keyboard_read(OSContPad *pad) {
} }
} }
static u32 keyboard_rawkey(void) {
const u32 ret = keyboard_lastkey;
keyboard_lastkey = VK_INVALID;
return ret;
}
static void keyboard_shutdown(void) {
}
struct ControllerAPI controller_keyboard = { struct ControllerAPI controller_keyboard = {
VK_BASE_KEYBOARD,
keyboard_init, keyboard_init,
keyboard_read keyboard_read,
keyboard_rawkey,
keyboard_bindkeys,
keyboard_shutdown
}; };

View file

@ -4,6 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
#include "controller_api.h" #include "controller_api.h"
# define VK_BASE_KEYBOARD 0x0000
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View file

@ -23,7 +23,22 @@ static void tas_read(OSContPad *pad) {
} }
} }
static void tas_shutdown(void) {
if (fp != NULL) {
fclose(fp);
fp = NULL;
}
}
static u32 tas_rawkey(void) {
return VK_INVALID;
}
struct ControllerAPI controller_recorded_tas = { struct ControllerAPI controller_recorded_tas = {
VK_INVALID,
tas_init, tas_init,
tas_read tas_read,
tas_rawkey,
NULL, // no rebinding
tas_shutdown
}; };

View file

@ -11,9 +11,15 @@
#include <ultra64.h> #include <ultra64.h>
#include "controller_api.h" #include "controller_api.h"
#include "controller_sdl.h"
#include "../configfile.h" #include "../configfile.h"
// mouse buttons are also in the controller namespace (why), just offset 0x100
#define VK_OFS_SDL_MOUSE 0x0100
#define VK_BASE_SDL_MOUSE (VK_BASE_SDL_GAMEPAD + VK_OFS_SDL_MOUSE)
#define MAX_JOYBINDS 32
#define MAX_MOUSEBUTTONS 8 // arbitrary
extern int16_t rightx; extern int16_t rightx;
extern int16_t righty; extern int16_t righty;
@ -27,6 +33,51 @@ extern u8 newcam_mouse;
static bool init_ok; static bool init_ok;
static SDL_GameController *sdl_cntrl; static SDL_GameController *sdl_cntrl;
static u32 num_joy_binds = 0;
static u32 num_mouse_binds = 0;
static u32 joy_binds[MAX_JOYBINDS][2];
static u32 mouse_binds[MAX_JOYBINDS][2];
static bool joy_buttons[SDL_CONTROLLER_BUTTON_MAX ] = { false };
static u32 mouse_buttons = 0;
static u32 last_mouse = VK_INVALID;
static u32 last_joybutton = VK_INVALID;
static inline void controller_add_binds(const u32 mask, const u32 *btns) {
for (u32 i = 0; i < MAX_BINDS; ++i) {
if (btns[i] >= VK_BASE_SDL_GAMEPAD && btns[i] <= VK_BASE_SDL_GAMEPAD + VK_SIZE) {
if (btns[i] >= VK_BASE_SDL_MOUSE && num_joy_binds < MAX_JOYBINDS) {
mouse_binds[num_mouse_binds][0] = btns[i] - VK_BASE_SDL_MOUSE;
mouse_binds[num_mouse_binds][1] = mask;
++num_mouse_binds;
} else if (num_mouse_binds < MAX_JOYBINDS) {
joy_binds[num_joy_binds][0] = btns[i] - VK_BASE_SDL_GAMEPAD;
joy_binds[num_joy_binds][1] = mask;
++num_joy_binds;
}
}
}
}
static void controller_sdl_bind(void) {
bzero(joy_binds, sizeof(joy_binds));
bzero(mouse_binds, sizeof(mouse_binds));
num_joy_binds = 0;
num_mouse_binds = 0;
controller_add_binds(A_BUTTON, configKeyA);
controller_add_binds(B_BUTTON, configKeyB);
controller_add_binds(Z_TRIG, configKeyZ);
controller_add_binds(U_CBUTTONS, configKeyCUp);
controller_add_binds(L_CBUTTONS, configKeyCLeft);
controller_add_binds(D_CBUTTONS, configKeyCDown);
controller_add_binds(R_CBUTTONS, configKeyCRight);
controller_add_binds(L_TRIG, configKeyL);
controller_add_binds(R_TRIG, configKeyR);
controller_add_binds(START_BUTTON, configKeyStart);
}
static void controller_sdl_init(void) { static void controller_sdl_init(void) {
if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) {
fprintf(stderr, "SDL init error: %s\n", SDL_GetError()); fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
@ -39,6 +90,8 @@ static void controller_sdl_init(void) {
SDL_GetRelativeMouseState(&mouse_x, &mouse_y); SDL_GetRelativeMouseState(&mouse_x, &mouse_y);
#endif #endif
controller_sdl_bind();
init_ok = true; init_ok = true;
} }
@ -53,13 +106,16 @@ static void controller_sdl_read(OSContPad *pad) {
else else
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
const u32 mbuttons = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); u32 mouse = SDL_GetRelativeMouseState(&mouse_x, &mouse_y);
for (u32 i = 0; i < num_mouse_binds; ++i)
if (mouse & SDL_BUTTON(mouse_binds[i][0]))
pad->button |= mouse_binds[i][1];
// remember buttons that changed from 0 to 1
last_mouse = (mouse_buttons ^ mouse) & mouse;
mouse_buttons = mouse;
if (configMouseA && (mbuttons & SDL_BUTTON(configMouseA))) pad->button |= A_BUTTON;
if (configMouseB && (mbuttons & SDL_BUTTON(configMouseB))) pad->button |= B_BUTTON;
if (configMouseL && (mbuttons & SDL_BUTTON(configMouseL))) pad->button |= L_TRIG;
if (configMouseR && (mbuttons & SDL_BUTTON(configMouseR))) pad->button |= R_TRIG;
if (configMouseZ && (mbuttons & SDL_BUTTON(configMouseZ))) pad->button |= Z_TRIG;
#endif #endif
SDL_GameControllerUpdate(); SDL_GameControllerUpdate();
@ -82,12 +138,16 @@ static void controller_sdl_read(OSContPad *pad) {
} }
} }
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyStart)) pad->button |= START_BUTTON; for (u32 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyZ)) pad->button |= Z_TRIG; const bool new = SDL_GameControllerGetButton(sdl_cntrl, i);
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyL)) pad->button |= L_TRIG; const bool pressed = !joy_buttons[i] && new;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyR)) pad->button |= R_TRIG; joy_buttons[i] = new;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyA)) pad->button |= A_BUTTON; if (pressed) last_joybutton = i;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyB)) pad->button |= B_BUTTON; }
for (u32 i = 0; i < num_joy_binds; ++i)
if (joy_buttons[joy_binds[i][0]])
pad->button |= joy_binds[i][1];
int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX); int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX);
int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY); int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY);
@ -127,7 +187,39 @@ static void controller_sdl_read(OSContPad *pad) {
} }
} }
static u32 controller_sdl_rawkey(void) {
if (last_joybutton != VK_INVALID) {
const u32 ret = last_joybutton;
last_joybutton = VK_INVALID;
return ret;
}
for (int i = 0; i < MAX_MOUSEBUTTONS; ++i) {
if (last_mouse & SDL_BUTTON(i)) {
const u32 ret = VK_OFS_SDL_MOUSE + i;
last_mouse = 0;
return ret;
}
}
return VK_INVALID;
}
static void controller_sdl_shutdown(void) {
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
if (sdl_cntrl) {
SDL_GameControllerClose(sdl_cntrl);
sdl_cntrl = NULL;
}
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER);
}
init_ok = false;
}
struct ControllerAPI controller_sdl = { struct ControllerAPI controller_sdl = {
VK_BASE_SDL_GAMEPAD,
controller_sdl_init, controller_sdl_init,
controller_sdl_read controller_sdl_read,
controller_sdl_rawkey,
controller_sdl_bind,
controller_sdl_shutdown
}; };

View file

@ -3,6 +3,8 @@
#include "controller_api.h" #include "controller_api.h"
#define VK_BASE_SDL_GAMEPAD 0x1000
extern struct ControllerAPI controller_sdl; extern struct ControllerAPI controller_sdl;
#endif #endif

View file

@ -408,9 +408,10 @@ static uint32_t gfx_cm_to_opengl(uint32_t val) {
} }
static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) {
const GLenum filter = linear_filter ? GL_LINEAR : GL_NEAREST;
glActiveTexture(GL_TEXTURE0 + tile); glActiveTexture(GL_TEXTURE0 + tile);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt));
} }
@ -480,6 +481,9 @@ static void gfx_opengl_start_frame(void) {
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
} }
static void gfx_opengl_shutdown(void) {
}
struct GfxRenderingAPI gfx_opengl_api = { struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_z_is_from_0_to_1, gfx_opengl_z_is_from_0_to_1,
gfx_opengl_unload_shader, gfx_opengl_unload_shader,
@ -499,5 +503,6 @@ struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_set_use_alpha, gfx_opengl_set_use_alpha,
gfx_opengl_draw_triangles, gfx_opengl_draw_triangles,
gfx_opengl_init, gfx_opengl_init,
gfx_opengl_start_frame gfx_opengl_start_frame,
gfx_opengl_shutdown
}; };

View file

@ -18,6 +18,8 @@
#include "gfx_rendering_api.h" #include "gfx_rendering_api.h"
#include "gfx_screen_config.h" #include "gfx_screen_config.h"
#include "../configfile.h"
#define SUPPORT_CHECK(x) assert(x) #define SUPPORT_CHECK(x) assert(x)
// SCALE_M_N: upscale/downscale M-bit integer to N-bit // SCALE_M_N: upscale/downscale M-bit integer to N-bit
@ -569,10 +571,16 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti
calculate_normal_dir(&lookat_y, rsp.current_lookat_coeffs[1]); calculate_normal_dir(&lookat_y, rsp.current_lookat_coeffs[1]);
rsp.lights_changed = false; rsp.lights_changed = false;
} }
int r = rsp.current_lights[rsp.current_num_lights - 1].col[0]; // Inspired by:
int g = rsp.current_lights[rsp.current_num_lights - 1].col[1]; // https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL484
int b = rsp.current_lights[rsp.current_num_lights - 1].col[2]; const bool useFirstColor = (dest_index & 1) == 0;
const unsigned char* col = useFirstColor
? rsp.current_lights[rsp.current_num_lights - 1].col
: rsp.current_lights[rsp.current_num_lights - 1].colc;
int r = col[0];
int g = col[1];
int b = col[2];
for (int i = 0; i < rsp.current_num_lights - 1; i++) { for (int i = 0; i < rsp.current_num_lights - 1; i++) {
float intensity = 0; float intensity = 0;
@ -581,9 +589,14 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti
intensity += vn->n[2] * rsp.current_lights_coeffs[i][2]; intensity += vn->n[2] * rsp.current_lights_coeffs[i][2];
intensity /= 127.0f; intensity /= 127.0f;
if (intensity > 0.0f) { if (intensity > 0.0f) {
r += intensity * rsp.current_lights[i].col[0]; // Inspired by:
g += intensity * rsp.current_lights[i].col[1]; // https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL492
b += intensity * rsp.current_lights[i].col[2]; col = useFirstColor
? rsp.current_lights[i].col
: rsp.current_lights[i].colc;
r += intensity * col[0];
g += intensity * col[1];
b += intensity * col[2];
} }
} }
@ -763,7 +776,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) {
import_texture(i); import_texture(i);
rdp.textures_changed[i] = false; rdp.textures_changed[i] = false;
} }
bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT; bool linear_filter = configFiltering && ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT);
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) { 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_flush();
gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt); gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt);
@ -1545,3 +1558,14 @@ void gfx_end_frame(void) {
gfx_wapi->swap_buffers_end(); gfx_wapi->swap_buffers_end();
} }
} }
void gfx_shutdown(void) {
if (gfx_rapi) {
if (gfx_rapi->shutdown) gfx_rapi->shutdown();
gfx_rapi = NULL;
}
if (gfx_wapi) {
if (gfx_wapi->shutdown) gfx_wapi->shutdown();
gfx_wapi = NULL;
}
}

View file

@ -15,5 +15,6 @@ void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi);
void gfx_start_frame(void); void gfx_start_frame(void);
void gfx_run(Gfx *commands); void gfx_run(Gfx *commands);
void gfx_end_frame(void); void gfx_end_frame(void);
void gfx_shutdown(void);
#endif #endif

View file

@ -27,6 +27,7 @@ struct GfxRenderingAPI {
void (*draw_triangles)(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris); void (*draw_triangles)(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris);
void (*init)(void); void (*init)(void);
void (*start_frame)(void); void (*start_frame)(void);
void (*shutdown)(void);
}; };
#endif #endif

View file

@ -19,13 +19,16 @@
#include "gfx_window_manager_api.h" #include "gfx_window_manager_api.h"
#include "gfx_screen_config.h" #include "gfx_screen_config.h"
#include "../configfile.h" #include "../configfile.h"
#include "../cliopts.h"
#include "src/pc/controller/controller_keyboard.h" #include "src/pc/controller/controller_keyboard.h"
static SDL_Window *wnd; static SDL_Window *wnd;
static SDL_GLContext ctx = NULL;
static int inverted_scancode_table[512]; static int inverted_scancode_table[512];
extern bool configFullscreen; static bool cur_fullscreen;
static uint32_t cur_width, cur_height;
const SDL_Scancode windows_scancode_table[] = const SDL_Scancode windows_scancode_table[] =
{ {
@ -78,20 +81,18 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = {
{SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN} {SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN}
}; };
static void gfx_sdl_set_fullscreen(bool fullscreen) static void gfx_sdl_set_fullscreen(bool fullscreen) {
{ if (fullscreen == cur_fullscreen) return;
if (fullscreen)
{ if (fullscreen) {
SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
} } else {
else
{
SDL_SetWindowFullscreen(wnd, 0); SDL_SetWindowFullscreen(wnd, 0);
SDL_ShowCursor(SDL_ENABLE); SDL_ShowCursor(SDL_ENABLE);
} }
configFullscreen = fullscreen; cur_fullscreen = fullscreen;
} }
static void gfx_sdl_init(void) { static void gfx_sdl_init(void) {
@ -113,6 +114,10 @@ static void gfx_sdl_init(void) {
window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
if (gCLIOpts.FullScreen) {
configFullscreen = true;
}
if (configFullscreen) { if (configFullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} }
@ -185,13 +190,9 @@ static void gfx_sdl_onkeydown(int scancode) {
const Uint8 *state = SDL_GetKeyboardState(NULL); const Uint8 *state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN]) if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN])
{ configFullscreen = !configFullscreen;
gfx_sdl_set_fullscreen(!configFullscreen);
}
else if (state[SDL_SCANCODE_ESCAPE] && configFullscreen) else if (state[SDL_SCANCODE_ESCAPE] && configFullscreen)
{ configFullscreen = false;
gfx_sdl_set_fullscreen(false);
}
} }
static void gfx_sdl_onkeyup(int scancode) { static void gfx_sdl_onkeyup(int scancode) {
@ -215,6 +216,9 @@ static void gfx_sdl_handle_events(void) {
exit(0); exit(0);
} }
} }
// just check if the fullscreen value has changed and toggle fullscreen if it has
if (configFullscreen != cur_fullscreen)
gfx_sdl_set_fullscreen(configFullscreen);
} }
static bool gfx_sdl_start_frame(void) { static bool gfx_sdl_start_frame(void) {
@ -232,6 +236,15 @@ static double gfx_sdl_get_time(void) {
return 0.0; return 0.0;
} }
static void gfx_sdl_shutdown(void) {
if (SDL_WasInit(0)) {
if (ctx) { SDL_GL_DeleteContext(ctx); ctx = NULL; }
if (wnd) { SDL_DestroyWindow(wnd); wnd = NULL; }
SDL_Quit();
}
}
struct GfxWindowManagerAPI gfx_sdl = { struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_init, gfx_sdl_init,
gfx_sdl_main_loop, gfx_sdl_main_loop,
@ -240,5 +253,6 @@ struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_start_frame, gfx_sdl_start_frame,
gfx_sdl_swap_buffers_begin, gfx_sdl_swap_buffers_begin,
gfx_sdl_swap_buffers_end, gfx_sdl_swap_buffers_end,
gfx_sdl_get_time gfx_sdl_get_time,
gfx_sdl_shutdown
}; };

View file

@ -13,6 +13,7 @@ struct GfxWindowManagerAPI {
void (*swap_buffers_begin)(void); void (*swap_buffers_begin)(void);
void (*swap_buffers_end)(void); void (*swap_buffers_end)(void);
double (*get_time)(void); // For debug double (*get_time)(void); // For debug
void (*shutdown)(void);
}; };
#endif #endif

View file

@ -18,6 +18,7 @@
#include "audio/audio_sdl.h" #include "audio/audio_sdl.h"
#include "audio/audio_null.h" #include "audio/audio_null.h"
#include "cliopts.h"
#include "configfile.h" #include "configfile.h"
OSMesg D_80339BEC; OSMesg D_80339BEC;
@ -72,11 +73,31 @@ void produce_one_frame(void) {
create_next_audio_buffer(audio_buffer + i * (num_audio_samples * 2), num_audio_samples); create_next_audio_buffer(audio_buffer + i * (num_audio_samples * 2), num_audio_samples);
} }
//printf("Audio samples before submitting: %d\n", audio_api->buffered()); //printf("Audio samples before submitting: %d\n", audio_api->buffered());
audio_api->play(audio_buffer, 2 * num_audio_samples * 4);
// scale by master volume (0-127)
const s32 mod = (s32)configMasterVolume;
for (u32 i = 0; i < num_audio_samples * 4; ++i)
audio_buffer[i] = ((s32)audio_buffer[i] * mod) >> VOLUME_SHIFT;
audio_api->play((u8*)audio_buffer, 2 * num_audio_samples * 4);
gfx_end_frame(); gfx_end_frame();
} }
void audio_shutdown(void) {
if (audio_api) {
if (audio_api->shutdown) audio_api->shutdown();
audio_api = NULL;
}
}
void game_shutdown(void) {
configfile_save(CONFIG_FILE);
controller_shutdown();
audio_shutdown();
gfx_shutdown();
}
#ifdef TARGET_WEB #ifdef TARGET_WEB
static void em_main_loop(void) { static void em_main_loop(void) {
} }
@ -110,17 +131,13 @@ static void on_anim_frame(double time) {
} }
#endif #endif
static void save_config(void) {
configfile_save(CONFIG_FILE);
}
void main_func(void) { void main_func(void) {
static u64 pool[0x165000/8 / 4 * sizeof(void *)]; static u64 pool[0x165000/8 / 4 * sizeof(void *)];
main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0])); main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0]));
gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT); gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
configfile_load(CONFIG_FILE); configfile_load(CONFIG_FILE);
atexit(save_config); atexit(game_shutdown);
#ifdef TARGET_WEB #ifdef TARGET_WEB
emscripten_set_main_loop(em_main_loop, 0, 0); emscripten_set_main_loop(em_main_loop, 0, 0);
@ -155,6 +172,7 @@ void main_func(void) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
parse_cli_opts(argc, argv);
main_func(); main_func();
return 0; return 0;
} }