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
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
TARGET_WEB ?= 0
@ -42,8 +48,6 @@ else
endif
# 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
GRUCODE := f3dex2e
@ -420,9 +424,14 @@ else
LD := $(CC)
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
OBJCOPY := $(CROSS)objcopy
PYTHON := python3
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`
endif
# Check for better camera option
# Check for enhancement options
# Check for Puppycam option
ifeq ($(BETTERCAMERA),1)
CC_CHECK += -DBETTERCAMERA
CFLAGS += -DBETTERCAMERA
CC_CHECK += -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
ASFLAGS := -I include -I $(BUILD_DIR) $(VERSION_ASFLAGS)
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']"
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 -no-pie -static
ifeq ($(WINDOWS_CONSOLE),1)
LDFLAGS += -mconsole
endif
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
ifneq ($(CROSS),i686-w64-mingw32.static-)
ifneq ($(CROSS),x86_64-w64-mingw32.static-)
LDFLAGS += -no-pie
endif
endif
ifeq ($(WINDOWS_CONSOLE),1)
LDFLAGS += -mconsole
endif
else ifeq ($(TARGET_RPI),1)
# Linux / Other builds below
ifeq ($(TARGET_RPI),1)
LDFLAGS := $(OPT_FLAGS) -lm -lGLESv2 `$(SDLCONFIG) --libs` -no-pie
else
LDFLAGS := $(BITS) -march=$(TARGET_ARCH) -lm -lGL `$(SDLCONFIG) --libs` -no-pie -lpthread
endif
endif
endif #Added for Pi ifeq
# 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/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/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
else
$(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/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h
endif
################################################################

View file

@ -2,7 +2,9 @@
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**.
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
@ -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.
* 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`.)
* 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
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
// as a transparent black burn smoke. Probably meant to show up as white-ish
// 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
ALIGNED8 static const u8 burn_smoke_seg4_texture_04021800[] = {
#include "actors/burn_smoke/burn_smoke.ia16.inc.c"
@ -44,7 +47,11 @@ const Gfx burn_smoke_seg4_dl_04022048[] = {
// 0x04022070 - 0x040220C8
const Gfx burn_smoke_seg4_dl_04022070[] = {
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),
#endif
gsSPDisplayList(burn_smoke_seg4_dl_04022028),
gsSPDisplayList(burn_smoke_seg4_dl_04022048),
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
// the rest of its body. This is evident because once the mistake is corrected
// 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
ALIGNED8 static const u8 koopa_seg6_texture_06002648[] = {
#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),
gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 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 + 0x18, 2), // koopa appearing to wear pink shorts.
#endif
gsSPVertex(koopa_seg6_vertex_0600B5F0, 15, 0),
gsSP2Triangles( 0, 1, 2, 0x0, 3, 4, 5, 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

@ -219,6 +219,9 @@
#define BILLBOARD() \
BC_B(0x21)
#define CYLBOARD() \
BC_B(0x38)
// Hides the current object.
#define HIDE() \
BC_B(0x22)
@ -3180,7 +3183,7 @@ const BehaviorScript bhvFloorTrapInCastle[] = {
const BehaviorScript bhvTree[] = {
BEGIN(OBJ_LIST_POLELIKE),
BILLBOARD(),
CYLBOARD(),
OR_INT(oFlags, OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE),
SET_INT(oInteractType, INTERACT_POLE),
SET_HITBOX(/*Radius*/ 80, /*Height*/ 500),
@ -6105,5 +6108,3 @@ const BehaviorScript bhvIntroScene[] = {
CALL_NATIVE(bhv_intro_scene_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"
#define NC_CAMX _("Camera X Sensitivity")
#define NC_CAMY _("Camera Y Sensitivity")
#define NC_INVERTX _("Invert X Axis")
#define NC_INVERTY _("Invert Y Axis")
#define NC_CAMC _("Camera Centre Aggression")
#define NC_CAMP _("Camera Pan Level")
#define NC_ENABLED _("Enabled")
#define NC_DISABLED _("Disabled")
#define NC_BUTTON _("[R]: Options")
#define NC_BUTTON2 _("[R]: Return")
#define NC_OPTION _("OPTIONS")
#define NC_HIGHLIGHT _("O")
#define NC_ANALOGUE _("Analogue Camera")
#define NC_MOUSE _("Mouse Look")
#define TEXT_OPT_CAMX _("Camera X Sensitivity")
#define TEXT_OPT_CAMY _("Camera Y Sensitivity")
#define TEXT_OPT_INVERTX _("Invert X Axis")
#define TEXT_OPT_INVERTY _("Invert Y Axis")
#define TEXT_OPT_CAMC _("Camera Centre Aggression")
#define TEXT_OPT_CAMP _("Camera Pan Level")
#define TEXT_OPT_CAMD _("Camera Deceleration")
#define TEXT_OPT_ENABLED _("Enabled")
#define TEXT_OPT_DISABLED _("Disabled")
#define TEXT_OPT_BUTTON1 _("[R]: Options")
#define TEXT_OPT_BUTTON2 _("[R]: Return")
#define TEXT_OPT_OPTIONS _("OPTIONS")
#define TEXT_OPT_CAMERA _("CAMERA")
#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

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
# 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
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"
@ -65,7 +69,8 @@ clear
echo "Super Mario 64 RPi Initial Setup"
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)
echo "Checking for pre-enabled VC4 acceleration (inxi -Gx)"
@ -119,7 +124,7 @@ fixmem=$(cat /boot/cmdline.txt | grep cma=128M)
else
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 "gpu_mem=48M (config.txt) | cma=128M (cmdline.txt) will be written to /boot "
echo ""
@ -149,11 +154,12 @@ read -p "Reboot to enable changes? (Y/N): " fixstart
if [[ $fixstart =~ "Y" ]]
then
echo ""
echo "Rebooting RasPi!"
echo "Rebooting RasPi in 4 seconds! Press Control-C to cancel."
sleep 4
sudo reboot
fi
fi
fi #This should never run on a Pi 4
fi
fi # "Should never run on a Pi 4" part ends here
#--------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
@ -182,7 +188,7 @@ fi
#-------------------------------------------------------------------------------------
clear
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 "Warning: Compile could take up to an hour on older Raspberry Pi models"
read -p "Proceed? (Y/N): " sdlcomp
@ -293,7 +299,12 @@ echo ""
echo "Step 3. Compiling Super Mario 64 for the Raspberry Pi"
echo ""
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 "
fi
if [[ $curdir == 1 ]]
then
@ -312,12 +323,15 @@ else
echo ""
echo "Please satisfy this requirement before continuing."
echo "Exiting Super Mario 64 RasPi setup and compilation script."
echo ""
echo "Note: Re-run script once baserom(s) are inserted into"
if [[ $curdir == 1 ]]
then
echo $PWD
echo ""
else
echo ""
echo $HOME/src/sm64pi/sm64pc/
fi

View file

@ -138,6 +138,14 @@ static s32 bhv_cmd_billboard(void) {
return BHV_PROC_CONTINUE;
}
// Command 0x
static s32 bhv_cmd_cylboard(void) {
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD;
gCurBhvCommand++;
return BHV_PROC_CONTINUE;
}
// Command 0x1B: Sets the current model ID of the object.
// Usage: SET_MODEL(modelID)
static s32 bhv_cmd_set_model(void) {
@ -842,62 +850,63 @@ void stub_behavior_script_2(void) {
typedef s32 (*BhvCommandProc)(void);
static BhvCommandProc BehaviorCmdTable[] = {
bhv_cmd_begin,
bhv_cmd_delay,
bhv_cmd_call,
bhv_cmd_return,
bhv_cmd_goto,
bhv_cmd_begin_repeat,
bhv_cmd_end_repeat,
bhv_cmd_end_repeat_continue,
bhv_cmd_begin_loop,
bhv_cmd_end_loop,
bhv_cmd_break,
bhv_cmd_break_unused,
bhv_cmd_call_native,
bhv_cmd_add_float,
bhv_cmd_set_float,
bhv_cmd_add_int,
bhv_cmd_set_int,
bhv_cmd_or_int,
bhv_cmd_bit_clear,
bhv_cmd_set_int_rand_rshift,
bhv_cmd_set_random_float,
bhv_cmd_set_random_int,
bhv_cmd_add_random_float,
bhv_cmd_add_int_rand_rshift,
bhv_cmd_nop_1,
bhv_cmd_nop_2,
bhv_cmd_nop_3,
bhv_cmd_set_model,
bhv_cmd_spawn_child,
bhv_cmd_deactivate,
bhv_cmd_drop_to_floor,
bhv_cmd_sum_float,
bhv_cmd_sum_int,
bhv_cmd_billboard,
bhv_cmd_hide,
bhv_cmd_set_hitbox,
bhv_cmd_nop_4,
bhv_cmd_delay_var,
bhv_cmd_begin_repeat_unused,
bhv_cmd_load_animations,
bhv_cmd_animate,
bhv_cmd_spawn_child_with_param,
bhv_cmd_load_collision_data,
bhv_cmd_set_hitbox_with_offset,
bhv_cmd_spawn_obj,
bhv_cmd_set_home,
bhv_cmd_set_hurtbox,
bhv_cmd_set_interact_type,
bhv_cmd_set_obj_physics,
bhv_cmd_set_interact_subtype,
bhv_cmd_scale,
bhv_cmd_parent_bit_clear,
bhv_cmd_animate_texture,
bhv_cmd_disable_rendering,
bhv_cmd_set_int_unused,
bhv_cmd_spawn_water_droplet,
bhv_cmd_begin, //00
bhv_cmd_delay, //01
bhv_cmd_call, //02
bhv_cmd_return, //03
bhv_cmd_goto, //04
bhv_cmd_begin_repeat, //05
bhv_cmd_end_repeat, //06
bhv_cmd_end_repeat_continue, //07
bhv_cmd_begin_loop, //08
bhv_cmd_end_loop, //09
bhv_cmd_break, //0A
bhv_cmd_break_unused, //0B
bhv_cmd_call_native, //0C
bhv_cmd_add_float, //0D
bhv_cmd_set_float, //0E
bhv_cmd_add_int, //0F
bhv_cmd_set_int, //10
bhv_cmd_or_int, //11
bhv_cmd_bit_clear, //12
bhv_cmd_set_int_rand_rshift, //13
bhv_cmd_set_random_float, //14
bhv_cmd_set_random_int, //15
bhv_cmd_add_random_float, //16
bhv_cmd_add_int_rand_rshift, //17
bhv_cmd_nop_1, //18
bhv_cmd_nop_2, //19
bhv_cmd_nop_3, //1A
bhv_cmd_set_model, //1B
bhv_cmd_spawn_child, //1C
bhv_cmd_deactivate, //1D
bhv_cmd_drop_to_floor, //1E
bhv_cmd_sum_float, //1F
bhv_cmd_sum_int, //20
bhv_cmd_billboard, //21
bhv_cmd_hide, //22
bhv_cmd_set_hitbox, //23
bhv_cmd_nop_4, //24
bhv_cmd_delay_var, //25
bhv_cmd_begin_repeat_unused, //26
bhv_cmd_load_animations, //27
bhv_cmd_animate, //28
bhv_cmd_spawn_child_with_param, //29
bhv_cmd_load_collision_data, //2A
bhv_cmd_set_hitbox_with_offset, //2B
bhv_cmd_spawn_obj, //2C
bhv_cmd_set_home, //2D
bhv_cmd_set_hurtbox, //2E
bhv_cmd_set_interact_type, //2F
bhv_cmd_set_obj_physics, //30
bhv_cmd_set_interact_subtype, //31
bhv_cmd_scale, //32
bhv_cmd_parent_bit_clear, //33
bhv_cmd_animate_texture, //34
bhv_cmd_disable_rendering, //35
bhv_cmd_set_int_unused, //36
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.
@ -987,11 +996,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;

View file

@ -30,6 +30,7 @@ extern Vec3s gVec3sOne;
#define GRAPH_RENDER_Z_BUFFER (1 << 3)
#define GRAPH_RENDER_INVISIBLE (1 << 4)
#define GRAPH_RENDER_HAS_ANIMATION (1 << 5)
#define GRAPH_RENDER_CYLBOARD (1 << 6)
// Whether the node type has a function pointer of type GraphNodeFunc
#define GRAPH_NODE_TYPE_FUNCTIONAL 0x100

View file

@ -377,6 +377,31 @@ void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) {
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
* 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_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_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_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]);

View file

@ -786,9 +786,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
}

View file

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

View file

@ -17,9 +17,11 @@ void opened_cannon_act_0(void) {
cur_obj_enable_rendering();
cur_obj_become_tangible();
}
if (o->oDistanceToMario < 500.0f) {
cur_obj_become_tangible();
cur_obj_enable_rendering();
if (o->oDistanceToMario < 500.0f) {
//cur_obj_become_tangible();
//cur_obj_enable_rendering();
if (o->oInteractStatus & INT_STATUS_INTERACTED
&& (!(o->oInteractStatus
& 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
o->oInteractStatus = 0;
} else {
cur_obj_become_intangible();
cur_obj_disable_rendering();
//cur_obj_become_intangible();
//cur_obj_disable_rendering();
o->oCannonUnk10C = 0;
}
}

View file

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

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.
*/
static void cloud_act_fwoosh_hidden(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario < 2000.0f) {
#endif
cur_obj_unhide();
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.
*/
static void cloud_fwoosh_update(void) {
#ifndef NODRAWINGDISTANCE
if (o->oDistanceToMario > 2500.0f) {
o->oAction = CLOUD_ACT_UNLOAD;
} else {
#endif
if (o->oCloudBlowing) {
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]);
#ifndef NODRAWINGDISTANCE
}
#endif
}
/**

View file

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

View file

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

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
// 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
}
}

View file

@ -72,8 +72,12 @@ 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) {
#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_unhide();
o->oAction = 1;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -97,7 +97,9 @@ void bhv_sl_walking_penguin_loop(void) {
}
cur_obj_move_standard(-78);
#ifndef NODRAWINGDISTANCE
if (!cur_obj_hide_if_mario_far_away_y(1000.0f))
#endif
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.

View file

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

View file

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

View file

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

View file

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

@ -246,10 +246,14 @@ void bhv_whomp_loop(void) {
cur_obj_call_action_function(sWhompActions);
cur_obj_move_standard(-20);
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)
cur_obj_hide_if_mario_far_away_y(2000.0f);
else
cur_obj_hide_if_mario_far_away_y(1000.0f);
#endif
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_save_settings(void);
extern void newcam_render_option_text(void);
extern void newcam_diagnostics(void);
extern u8 newcam_option_open;
extern u8 newcam_sensitivityX; //How quick the camera works.
extern u8 newcam_sensitivityY;

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 nosound //If for some reason you hate the concept of audio, you can disable it.
//#define noaccel //Disables smooth movement of the camera with the C buttons.
#define DEGRADE 0.1f //What percent of the remaining camera movement is degraded. Default is 10%
//!Hardcoded camera angle stuff. They're essentially area boxes that when Mario is inside, will trigger some view changes.
///Don't touch this btw, unless you know what you're doing, this has to be above for religious reasons.
@ -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_pan_x;
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_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_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_y;
@ -166,18 +154,7 @@ void newcam_init_settings(void)
newcam_invertY = (u8)configCameraInvertY;
newcam_mouse = (u8)configCameraMouse;
newcam_analogue = (u8)configEnableCamera;
}
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;
newcam_degrade = (f32)configCameraDegrade / 100.0f;
}
/** Mathematic calculations. This stuffs so basic even *I* understand it lol
@ -291,7 +268,7 @@ static void newcam_rotate_button(void)
#ifdef noaccel
newcam_yaw_acc = 0;
#else
newcam_yaw_acc -= (newcam_yaw_acc*(DEGRADE));
newcam_yaw_acc -= (newcam_yaw_acc*newcam_degrade);
#endif
}
@ -303,7 +280,7 @@ static void newcam_rotate_button(void)
#ifdef noaccel
newcam_tilt_acc = 0;
#else
newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE));
newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade);
#endif
newcam_framessincec[0] += 1;
@ -369,13 +346,13 @@ static void newcam_rotate_button(void)
else
{
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)
newcam_tilt_acc = newcam_adjust_value(newcam_tilt_acc,(-gPlayer2Controller->stickY/4));
else
newcam_tilt_acc -= (newcam_tilt_acc*(DEGRADE));
newcam_tilt_acc -= (newcam_tilt_acc*newcam_degrade);
}
if (newcam_mouse == 1)
@ -401,8 +378,8 @@ static void newcam_zoom_button(void)
newcam_distance = newcam_distance_target;
}
//When you press L and R together, set the flag for centering the camera. Afterwards, start setting the yaw to the Player's yaw at the time.
if (gPlayer1Controller->buttonDown & L_TRIG && gPlayer1Controller->buttonDown & R_TRIG && newcam_modeflags & NC_FLAG_ZOOM)
//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 && newcam_modeflags & NC_FLAG_ZOOM)
{
newcam_yaw_target = -gMarioState->faceAngle[1]-0x4000;
newcam_centering = 1;
@ -677,227 +654,3 @@ void newcam_loop(struct Camera *c)
newcam_diagnostics();
#endif // NEWCAM_DEBUG
}
//Displays a box.
void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b)
{
gDPPipeSync(gDisplayListHead++);
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255));
gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1);
gDPPipeSync(gDisplayListHead++);
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
}
//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :(
void newcam_change_setting(u8 toggle)
{
switch (newcam_option_selection)
{
case 0:
newcam_analogue ^= 1;
break;
case 1:
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
#include "bettercamera.h"
#endif
#ifdef EXT_OPTIONS_MENU
#include "options_menu.h"
#endif
extern Gfx *gDisplayListHead;
extern s16 gCurrCourseNum;
@ -2371,12 +2374,6 @@ void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) {
{ TEXT_EXIT_COURSE_DE }
};
u8 textExitGame[][22] ={
{ TEXT_EXIT_GAME },
{ TEXT_EXIT_GAME_FR },
{ TEXT_EXIT_GAME_DE }
};
u8 textCameraAngleR[][24] = {
{ TEXT_CAMERA_ANGLE_R },
{ 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 textExitCourse textExitCourse[gInGameLanguage]
#define textExitGame textExitGame[gInGameLanguage]
#define textCameraAngleR textCameraAngleR[gInGameLanguage]
#else
u8 textContinue[] = { TEXT_CONTINUE };
u8 textExitCourse[] = { TEXT_EXIT_COURSE };
u8 textExitGame[] = { TEXT_EXIT_GAME };
u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R };
#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);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha);
print_generic_string(x + 10, y - 2, textContinue);
print_generic_string(x + 10, y - 17, textExitCourse);
print_generic_string(x + 10, y - 33, textExitGame);
if (index[0] != 4) {
print_generic_string(x + 10, y - 48, textCameraAngleR);
if (index[0] != 3) {
print_generic_string(x + 10, y - 33, textCameraAngleR);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
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);
gSPDisplayList(gDisplayListHead++, dl_draw_triangle);
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
}
if (index[0] == 4) {
render_pause_camera_options(x - 42, y - 57, &gDialogCameraAngleIndex, 110);
} else {
render_pause_camera_options(x - 42, y - 42, &gDialogCameraAngleIndex, 110);
}
}
@ -2612,9 +2603,8 @@ s16 render_pause_courses_and_castle(void) {
#ifdef VERSION_EU
gInGameLanguage = eu_get_language();
#endif
#ifdef BETTERCAMERA
if (newcam_option_open == 0)
{
#ifdef EXT_OPTIONS_MENU
if (optmenu_open == 0) {
#endif
switch (gDialogBoxState) {
case DIALOG_STATE_OPENING:
@ -2656,7 +2646,7 @@ s16 render_pause_courses_and_castle(void) {
gDialogBoxState = DIALOG_STATE_OPENING;
gMenuMode = -1;
if (gDialogLineNum == 2 || gDialogLineNum == 3) {
if (gDialogLineNum == 2) {
num = gDialogLineNum;
} else {
num = 1;
@ -2691,15 +2681,13 @@ s16 render_pause_courses_and_castle(void) {
if (gDialogTextAlpha < 250) {
gDialogTextAlpha += 25;
}
#ifdef BETTERCAMERA
}
else
{
#ifdef EXT_OPTIONS_MENU
} else {
shade_screen();
newcam_display_options();
optmenu_draw();
}
newcam_check_pause_buttons();
newcam_render_option_text();
optmenu_check_buttons();
optmenu_draw_prompt();
#endif
return 0;

View file

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

View file

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

View file

@ -1595,6 +1595,10 @@ void obj_set_billboard(struct Object *obj) {
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) {
o->hitboxRadius = radius;
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);
// extern ? set_mario_interact_hoot_if_in_range(?);
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_hurtbox_radius_and_height(f32,f32);
// 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) {
mtxf_mul(gMatStack[gMatStackIndex + 1], (void *) node->header.gfx.throwMatrix,
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) {
mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex],
node->header.gfx.pos, gCurGraphNodeCamera->roll);

View file

@ -22,6 +22,7 @@
#include "gd_math.h"
#include "shape_helper.h"
#include "config.h"
#include "gfx_dimensions.h"
#define MAX_GD_DLS 1000
@ -2302,7 +2303,7 @@ void start_view_dl(struct ObjView *view) {
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);
gSPSetGeometryMode(next_gfx(), G_LIGHTING | G_CULL_BACK | G_SHADING_SMOOTH | G_SHADE);
if (view->flags & VIEW_ALLOC_ZBUF) {
@ -2961,9 +2962,9 @@ void update_cursor(void) {
reset_dlnum_indices(sHandShape->gdDls[gGdFrameBuf]);
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 {
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();
@ -3419,7 +3420,8 @@ void Unknown801A5FF8(struct ObjGroup *arg0) {
void gd_put_sprite(u16 *sprite, s32 x, s32 y, s32 wx, s32 wy) {
s32 c; // 5c
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;
gSPDisplayList(next_gfx(), osVirtualToPhysical(gd_dl_sprite_start_tex_block));

View file

@ -10,6 +10,7 @@ struct AudioAPI {
int (*buffered)(void);
int (*get_desired_buffered)(void);
void (*play)(const uint8_t *buf, size_t len);
void (*shutdown)(void);
};
#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_shutdown(void) {
}
struct AudioAPI audio_null = {
audio_null_init,
audio_null_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 = {
audio_sdl_init,
audio_sdl_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 "configfile.h"
#include "controller/controller_api.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@ -14,6 +15,7 @@ enum ConfigOptionType {
CONFIG_TYPE_BOOL,
CONFIG_TYPE_UINT,
CONFIG_TYPE_FLOAT,
CONFIG_TYPE_BIND,
};
struct ConfigOption {
@ -29,74 +31,60 @@ struct ConfigOption {
/*
*Config options and default values
*/
// Video/audio stuff
bool configFullscreen = false;
// Keyboard mappings (scancode values)
unsigned int configKeyA = 0x26;
unsigned int configKeyB = 0x33;
unsigned int configKeyStart = 0x39;
unsigned int configKeyL = 0x34;
unsigned int configKeyR = 0x36;
unsigned int configKeyZ = 0x25;
unsigned int configKeyCUp = 0x148;
unsigned int configKeyCDown = 0x150;
unsigned int configKeyCLeft = 0x14B;
unsigned int configKeyCRight = 0x14D;
unsigned int configKeyStickUp = 0x11;
unsigned int configKeyStickDown = 0x1F;
unsigned int configKeyStickLeft = 0x1E;
unsigned int configKeyStickRight = 0x20;
// Gamepad mappings (SDL_GameControllerButton values)
unsigned int configJoyA = 0;
unsigned int configJoyB = 2;
unsigned int configJoyStart = 6;
unsigned int configJoyL = 7;
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;
unsigned int configFiltering = 1; // 0=force nearest, 1=linear, (TODO) 2=three-point
unsigned int configMasterVolume = MAX_VOLUME; // 0 - MAX_VOLUME
// Keyboard mappings (VK_ values, by default keyboard/gamepad/mouse)
unsigned int configKeyA[MAX_BINDS] = { 0x0026, 0x1000, 0x1103 };
unsigned int configKeyB[MAX_BINDS] = { 0x0033, 0x1002, 0x1101 };
unsigned int configKeyStart[MAX_BINDS] = { 0x0039, 0x1006, VK_INVALID };
unsigned int configKeyL[MAX_BINDS] = { 0x0034, 0x1007, 0x1104 };
unsigned int configKeyR[MAX_BINDS] = { 0x0036, 0x100A, 0x1105 };
unsigned int configKeyZ[MAX_BINDS] = { 0x0025, 0x1009, 0x1102 };
unsigned int configKeyCUp[MAX_BINDS] = { 0x0148, VK_INVALID, VK_INVALID };
unsigned int configKeyCDown[MAX_BINDS] = { 0x0150, VK_INVALID, VK_INVALID };
unsigned int configKeyCLeft[MAX_BINDS] = { 0x014B, VK_INVALID, VK_INVALID };
unsigned int configKeyCRight[MAX_BINDS] = { 0x014D, VK_INVALID, VK_INVALID };
unsigned int configKeyStickUp[MAX_BINDS] = { 0x0011, VK_INVALID, VK_INVALID };
unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID };
unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID };
unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID };
#ifdef BETTERCAMERA
// BetterCamera settings
unsigned int configCameraXSens = 50;
unsigned int configCameraYSens = 50;
unsigned int configCameraAggr = 0;
unsigned int configCameraPan = 0;
unsigned int configCameraDegrade = 10; // 0 - 100%
bool configCameraInvertX = false;
bool configCameraInvertY = false;
bool configEnableCamera = false;
bool configCameraMouse = false;
#endif
unsigned int configSkipIntro = 0;
static const struct ConfigOption options[] = {
{.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen},
{.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA},
{.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB},
{.name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart},
{.name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL},
{.name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR},
{.name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ},
{.name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp},
{.name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown},
{.name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft},
{.name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight},
{.name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp},
{.name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown},
{.name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft},
{.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight},
{.name = "joy_a", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyA},
{.name = "joy_b", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyB},
{.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},
{.name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configFiltering},
{.name = "master_volume", .type = CONFIG_TYPE_UINT, .uintValue = &configMasterVolume},
{.name = "key_a", .type = CONFIG_TYPE_BIND, .uintValue = configKeyA},
{.name = "key_b", .type = CONFIG_TYPE_BIND, .uintValue = configKeyB},
{.name = "key_start", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStart},
{.name = "key_l", .type = CONFIG_TYPE_BIND, .uintValue = configKeyL},
{.name = "key_r", .type = CONFIG_TYPE_BIND, .uintValue = configKeyR},
{.name = "key_z", .type = CONFIG_TYPE_BIND, .uintValue = configKeyZ},
{.name = "key_cup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCUp},
{.name = "key_cdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCDown},
{.name = "key_cleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCLeft},
{.name = "key_cright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCRight},
{.name = "key_stickup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickUp},
{.name = "key_stickdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickDown},
{.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft},
{.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight},
#ifdef BETTERCAMERA
{.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera},
{.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_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr},
{.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan},
{.name = "bettercam_degrade", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraDegrade},
#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
@ -207,9 +197,13 @@ void configfile_load(const char *filename) {
while (isspace(*p))
p++;
if (!*p || *p == '#') // comment or empty line
continue;
numTokens = tokenize_string(p, 2, tokens);
if (numTokens != 0) {
if (numTokens == 2) {
if (numTokens >= 2) {
const struct ConfigOption *option = NULL;
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
@ -231,6 +225,10 @@ void configfile_load(const char *filename) {
case CONFIG_TYPE_UINT:
sscanf(tokens[1], "%u", option->uintValue);
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:
sscanf(tokens[1], "%f", option->floatValue);
break;
@ -273,6 +271,12 @@ void configfile_save(const char *filename) {
case CONFIG_TYPE_FLOAT:
fprintf(file, "%s %f\n", option->name, *option->floatValue);
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:
assert(0); // unknown type
}

View file

@ -5,38 +5,33 @@
#define CONFIG_FILE "sm64config.txt"
#define MAX_BINDS 3
#define MAX_VOLUME 127
#define VOLUME_SHIFT 7
extern bool configFullscreen;
extern unsigned int configKeyA;
extern unsigned int configKeyB;
extern unsigned int configKeyStart;
extern unsigned int configKeyL;
extern unsigned int configKeyR;
extern unsigned int configKeyZ;
extern unsigned int configKeyCUp;
extern unsigned int configKeyCDown;
extern unsigned int configKeyCLeft;
extern unsigned int configKeyCRight;
extern unsigned int configKeyStickUp;
extern unsigned int configKeyStickDown;
extern unsigned int configKeyStickLeft;
extern unsigned int configKeyStickRight;
extern unsigned int configJoyA;
extern unsigned int configJoyB;
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;
extern unsigned int configFiltering;
extern unsigned int configMasterVolume;
extern unsigned int configKeyA[];
extern unsigned int configKeyB[];
extern unsigned int configKeyStart[];
extern unsigned int configKeyL[];
extern unsigned int configKeyR[];
extern unsigned int configKeyZ[];
extern unsigned int configKeyCUp[];
extern unsigned int configKeyCDown[];
extern unsigned int configKeyCLeft[];
extern unsigned int configKeyCRight[];
extern unsigned int configKeyStickUp[];
extern unsigned int configKeyStickDown[];
extern unsigned int configKeyStickLeft[];
extern unsigned int configKeyStickRight[];
#ifdef BETTERCAMERA
extern unsigned int configCameraXSens;
extern unsigned int configCameraYSens;
extern unsigned int configCameraAggr;
extern unsigned int configCameraPan;
extern unsigned int configCameraDegrade;
extern bool configCameraInvertX;
extern bool configCameraInvertY;
extern bool configEnableCamera;

View file

@ -2,15 +2,25 @@
#define CONTROLLER_API
#define DEADZONE 4960
// Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors
// Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera
#define VK_INVALID 0xFFFF
#define VK_SIZE 0x1000
#include <ultra64.h>
struct ControllerAPI {
void (*init)(void);
void (*read)(OSContPad *pad);
const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000)
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

View file

@ -56,3 +56,25 @@ void osContGetReadData(OSContPad *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
#include "../configfile.h"
#include "controller_keyboard.h"
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) {
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) {
ret |= keyboard_mapping[i][1];
}
@ -26,12 +31,15 @@ static int keyboard_map_scancode(int scancode) {
bool keyboard_on_key_down(int scancode) {
int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= mapped;
keyboard_lastkey = scancode;
return mapped != 0;
}
bool keyboard_on_key_up(int scancode) {
int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~mapped;
if (keyboard_lastkey == (u32) scancode)
keyboard_lastkey = VK_INVALID;
return mapped != 0;
}
@ -39,28 +47,38 @@ void keyboard_on_all_keys_up(void) {
keyboard_buttons_down = 0;
}
static void set_keyboard_mapping(int index, int mask, int scancode) {
keyboard_mapping[index][0] = scancode;
keyboard_mapping[index][1] = mask;
static void keyboard_add_binds(int mask, unsigned int *scancode) {
for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) {
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) {
int i = 0;
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);
keyboard_bindkeys();
#ifdef TARGET_WEB
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 = {
VK_BASE_KEYBOARD,
keyboard_init,
keyboard_read
keyboard_read,
keyboard_rawkey,
keyboard_bindkeys,
keyboard_shutdown
};

View file

@ -4,6 +4,8 @@
#include <stdbool.h>
#include "controller_api.h"
# define VK_BASE_KEYBOARD 0x0000
#ifdef __cplusplus
extern "C" {
#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 = {
VK_INVALID,
tas_init,
tas_read
tas_read,
tas_rawkey,
NULL, // no rebinding
tas_shutdown
};

View file

@ -11,9 +11,15 @@
#include <ultra64.h>
#include "controller_api.h"
#include "controller_sdl.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 righty;
@ -27,6 +33,51 @@ extern u8 newcam_mouse;
static bool init_ok;
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) {
if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) {
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);
#endif
controller_sdl_bind();
init_ok = true;
}
@ -53,13 +106,16 @@ static void controller_sdl_read(OSContPad *pad) {
else
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
SDL_GameControllerUpdate();
@ -82,12 +138,16 @@ static void controller_sdl_read(OSContPad *pad) {
}
}
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyStart)) pad->button |= START_BUTTON;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyZ)) pad->button |= Z_TRIG;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyL)) pad->button |= L_TRIG;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyR)) pad->button |= R_TRIG;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyA)) pad->button |= A_BUTTON;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyB)) pad->button |= B_BUTTON;
for (u32 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
const bool new = SDL_GameControllerGetButton(sdl_cntrl, i);
const bool pressed = !joy_buttons[i] && new;
joy_buttons[i] = new;
if (pressed) last_joybutton = i;
}
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 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 = {
VK_BASE_SDL_GAMEPAD,
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"
#define VK_BASE_SDL_GAMEPAD 0x1000
extern struct ControllerAPI controller_sdl;
#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) {
const GLenum filter = linear_filter ? GL_LINEAR : GL_NEAREST;
glActiveTexture(GL_TEXTURE0 + tile);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
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_T, gfx_cm_to_opengl(cmt));
}
@ -480,6 +481,9 @@ static void gfx_opengl_start_frame(void) {
glEnable(GL_SCISSOR_TEST);
}
static void gfx_opengl_shutdown(void) {
}
struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_z_is_from_0_to_1,
gfx_opengl_unload_shader,
@ -499,5 +503,6 @@ struct GfxRenderingAPI gfx_opengl_api = {
gfx_opengl_set_use_alpha,
gfx_opengl_draw_triangles,
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_screen_config.h"
#include "../configfile.h"
#define SUPPORT_CHECK(x) assert(x)
// SCALE_M_N: upscale/downscale M-bit integer to N-bit
@ -570,9 +572,15 @@ static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *verti
rsp.lights_changed = false;
}
int r = rsp.current_lights[rsp.current_num_lights - 1].col[0];
int g = rsp.current_lights[rsp.current_num_lights - 1].col[1];
int b = rsp.current_lights[rsp.current_num_lights - 1].col[2];
// Inspired by:
// https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL484
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++) {
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 /= 127.0f;
if (intensity > 0.0f) {
r += intensity * rsp.current_lights[i].col[0];
g += intensity * rsp.current_lights[i].col[1];
b += intensity * rsp.current_lights[i].col[2];
// Inspired by:
// https://github.com/gonetz/GLideN64/commit/c8cbafff71a81bee5112aaafe6e21d6648ff8125#diff-69d8715ec7f9fd627ec4f5516edd003dL492
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);
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) {
gfx_flush();
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();
}
}
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_run(Gfx *commands);
void gfx_end_frame(void);
void gfx_shutdown(void);
#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 (*init)(void);
void (*start_frame)(void);
void (*shutdown)(void);
};
#endif

View file

@ -19,13 +19,16 @@
#include "gfx_window_manager_api.h"
#include "gfx_screen_config.h"
#include "../configfile.h"
#include "../cliopts.h"
#include "src/pc/controller/controller_keyboard.h"
static SDL_Window *wnd;
static SDL_GLContext ctx = NULL;
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[] =
{
@ -78,20 +81,18 @@ const SDL_Scancode scancode_rmapping_nonextended[][2] = {
{SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN}
};
static void gfx_sdl_set_fullscreen(bool fullscreen)
{
if (fullscreen)
{
static void gfx_sdl_set_fullscreen(bool fullscreen) {
if (fullscreen == cur_fullscreen) return;
if (fullscreen) {
SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_ShowCursor(SDL_DISABLE);
}
else
{
} else {
SDL_SetWindowFullscreen(wnd, 0);
SDL_ShowCursor(SDL_ENABLE);
}
configFullscreen = fullscreen;
cur_fullscreen = fullscreen;
}
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;
if (gCLIOpts.FullScreen) {
configFullscreen = true;
}
if (configFullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
@ -185,13 +190,9 @@ static void gfx_sdl_onkeydown(int scancode) {
const Uint8 *state = SDL_GetKeyboardState(NULL);
if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN])
{
gfx_sdl_set_fullscreen(!configFullscreen);
}
configFullscreen = !configFullscreen;
else if (state[SDL_SCANCODE_ESCAPE] && configFullscreen)
{
gfx_sdl_set_fullscreen(false);
}
configFullscreen = false;
}
static void gfx_sdl_onkeyup(int scancode) {
@ -215,6 +216,9 @@ static void gfx_sdl_handle_events(void) {
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) {
@ -232,6 +236,15 @@ static double gfx_sdl_get_time(void) {
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 = {
gfx_sdl_init,
gfx_sdl_main_loop,
@ -240,5 +253,6 @@ struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_start_frame,
gfx_sdl_swap_buffers_begin,
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_end)(void);
double (*get_time)(void); // For debug
void (*shutdown)(void);
};
#endif

View file

@ -18,6 +18,7 @@
#include "audio/audio_sdl.h"
#include "audio/audio_null.h"
#include "cliopts.h"
#include "configfile.h"
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);
}
//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();
}
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
static void em_main_loop(void) {
}
@ -110,17 +131,13 @@ static void on_anim_frame(double time) {
}
#endif
static void save_config(void) {
configfile_save(CONFIG_FILE);
}
void main_func(void) {
static u64 pool[0x165000/8 / 4 * sizeof(void *)];
main_pool_init(pool, pool + sizeof(pool) / sizeof(pool[0]));
gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
configfile_load(CONFIG_FILE);
atexit(save_config);
atexit(game_shutdown);
#ifdef TARGET_WEB
emscripten_set_main_loop(em_main_loop, 0, 0);
@ -155,6 +172,7 @@ void main_func(void) {
}
int main(int argc, char *argv[]) {
parse_cli_opts(argc, argv);
main_func();
return 0;
}