Merge remote-tracking branch 'origin/dev'

This commit is contained in:
EmeraldLockdown 2026-03-14 19:34:27 -05:00
commit 2f72b24033
887 changed files with 37119 additions and 4821 deletions

View file

@ -26,7 +26,7 @@ jobs:
cd tools
g++ -std=c++17 -o hash_file hash_file.cpp
echo "::notice ::$(./hash_file ../build/us_pc/sm64coopdx)"
- name: Zip the game
run: |
cd ./build/us_pc
@ -37,7 +37,7 @@ jobs:
with:
name: sm64coopdx-linux
path: ./build/us_pc/sm64coopdx_Linux.zip
build-steamos:
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[build]') }}
runs-on: ubuntu-22.04
@ -58,7 +58,7 @@ jobs:
cd tools
g++ -std=c++17 -o hash_file hash_file.cpp
echo "::notice ::$(./hash_file ../build/us_pc/sm64coopdx)"
- name: Zip the game
run: |
cd ./build/us_pc
@ -108,7 +108,7 @@ jobs:
cd tools
g++ -std=c++17 -o hash_file.exe hash_file.cpp
echo "::notice ::$(./hash_file.exe ../build/us_pc/sm64coopdx.exe)"
- name: Zip the game
run: |
cd ./build/us_pc
@ -158,7 +158,7 @@ jobs:
cd tools
g++ -std=c++17 -o hash_file.exe hash_file.cpp
echo "::notice ::$(./hash_file.exe ../build/us_pc/sm64coopdx.exe)"
- name: Zip the game
run: |
cd ./build/us_pc
@ -172,17 +172,36 @@ jobs:
build-macos-arm:
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[build]') }}
runs-on: macos-latest
runs-on: macos-26
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
brew install make mingw-w64 sdl2 pkg-config glew glfw3 coreutils
brew install make mingw-w64 pkg-config glfw3 coreutils
- name: Compile glew
run: |
export MACOSX_DEPLOYMENT_TARGET=11
curl -L -o glew.tar.gz https://github.com/nigels-com/glew/releases/download/glew-2.2.0/glew-2.2.0.tgz
tar -xzf glew.tar.gz
cd glew-2.2.0
make SYSTEM=darwin CC="clang -arch arm64 -mmacosx-version-min=11" GLEW_PREFIX=/opt/homebrew GLEW_DEST=/opt/homebrew
make install GLEW_PREFIX=/opt/homebrew GLEW_DEST=/opt/homebrew
- name: Compile SDL2
run: |
curl -L -o SDL2.tar.gz https://github.com/libsdl-org/SDL/releases/download/release-2.30.9/SDL2-2.30.9.tar.gz
tar -xzf SDL2.tar.gz
cd SDL2-2.30.9
./configure --prefix=/opt/homebrew CC="clang -arch arm64 -mmacosx-version-min=11"
make -j$(sysctl -n hw.ncpu)
make install
- name: Build the game
run: |
export PKG_CONFIG_PATH=/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH
gmake OSX_BUILD=1 -j$(sysctl -n hw.ncpu)
- name: Code sign the app (Ad-Hoc)
@ -199,7 +218,7 @@ jobs:
run: |
cd ./build/us_pc
zip -r sm64coopdx_macOS_ARM.zip sm64coopdx.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
@ -215,7 +234,25 @@ jobs:
- name: Install dependencies
run: |
brew install make mingw-w64 gcc@9 sdl2 pkg-config glew glfw3 coreutils
brew install make mingw-w64 gcc@9 pkg-config glfw3 coreutils
- name: Compile glew
run: |
export MACOSX_DEPLOYMENT_TARGET=10.15
curl -L -o glew.tar.gz https://github.com/nigels-com/glew/releases/download/glew-2.2.0/glew-2.2.0.tgz
tar -xzf glew.tar.gz
cd glew-2.2.0
make SYSTEM=darwin CC="clang -arch x86_64 -mmacosx-version-min=10.15"
make install
- name: Compile SDL2
run: |
curl -L -o SDL2.tar.gz https://github.com/libsdl-org/SDL/releases/download/release-2.30.9/SDL2-2.30.9.tar.gz
tar -xzf SDL2.tar.gz
cd SDL2-2.30.9
./configure --prefix=/usr/local CC="clang -arch x86_64 -mmacosx-version-min=10.15"
make -j$(sysctl -n hw.ncpu)
make install
- name: Build the game
run: |
@ -235,7 +272,7 @@ jobs:
run: |
cd ./build/us_pc
zip -r sm64coopdx_macOS_Intel.zip sm64coopdx.app
- name: Upload artifact
uses: actions/upload-artifact@v4
with:

View file

@ -64,12 +64,7 @@ ICON ?= 1
# Use .app (for macOS)
USE_APP ?= 1
# Minimum macOS Version
# If our arch is arm, set to macOS 14
ifeq ($(shell arch),arm64)
MIN_MACOS_VERSION ?= 14
else
MIN_MACOS_VERSION ?= 10.15
endif
MIN_MACOS_VERSION ?= 11
# Make some small adjustments for handheld devices
HANDHELD ?= 0
@ -1575,15 +1570,15 @@ endif
# with no prerequisites, .SECONDARY causes no intermediate target to be removed
.SECONDARY:
# Handle end of macOS compilation
APP_DIR = ./sm64coopdx.app
APP_CONTENTS_DIR = $(APP_DIR)/Contents
APP_MACOS_DIR = $(APP_CONTENTS_DIR)/MacOS
APP_RESOURCES_DIR = $(APP_CONTENTS_DIR)/Resources
ifeq ($(OSX_BUILD),1)
GLEW_LIB := $(shell find $(BREW_PREFIX)/Cellar/glew | grep libGLEW.2.2.0 | sort -n | uniq)
SDL2_LIB := $(shell find $(BREW_PREFIX)/Cellar/sdl2 | grep libSDL2- | sort -n | uniq)
GLEW_LIB := $(shell find $(BREW_PREFIX)/lib/ | grep libGLEW.2.2.0 | sort -n | uniq)
SDL2_LIB := $(shell find $(BREW_PREFIX)/lib/ | grep libSDL2- | sort -n | uniq)
endif
all:
@ -1605,14 +1600,20 @@ all:
cp build/us_pc/libcoopnet.dylib $(APP_MACOS_DIR); \
cp build/us_pc/libjuice.1.6.2.dylib $(APP_MACOS_DIR); \
cp $(SDL2_LIB) $(APP_MACOS_DIR)/libSDL2.dylib; \
install_name_tool -change $(BREW_PREFIX)/opt/sdl2/lib/libSDL2-2.0.0.dylib @executable_path/libSDL2.dylib $(APP_MACOS_DIR)/sm64coopdx; > /dev/null 2>&1 \
install_name_tool -id @executable_path/libSDL2.dylib $(APP_MACOS_DIR)/libSDL2.dylib; > /dev/null 2>&1 \
install_name_tool -change $(BREW_PREFIX)/lib/libSDL2-2.0.0.dylib @executable_path/libSDL2.dylib $(APP_MACOS_DIR)/sm64coopdx > /dev/null 2>&1; \
install_name_tool -change $(BREW_PREFIX)/opt/sdl2/lib/libSDL2-2.0.0.dylib @executable_path/libSDL2.dylib $(APP_MACOS_DIR)/sm64coopdx > /dev/null 2>&1; \
install_name_tool -id @executable_path/libSDL2.dylib $(APP_MACOS_DIR)/libSDL2.dylib > /dev/null 2>&1; \
codesign --force --deep --sign - $(APP_MACOS_DIR)/libSDL2.dylib; \
cp $(GLEW_LIB) $(APP_MACOS_DIR)/libGLEW.dylib; \
install_name_tool -change $(BREW_PREFIX)/opt/glew/lib/libGLEW.2.2.dylib @executable_path/libGLEW.dylib $(APP_MACOS_DIR)/sm64coopdx; > /dev/null 2>&1 \
install_name_tool -id @executable_path/libGLEW.dylib $(APP_MACOS_DIR)/libGLEW.dylib; > /dev/null 2>&1 \
install_name_tool -change $(BREW_PREFIX)/lib/libGLEW.2.2.0.dylib @executable_path/libGLEW.dylib $(APP_MACOS_DIR)/sm64coopdx > /dev/null 2>&1; \
install_name_tool -change $(BREW_PREFIX)/opt/glew/lib/libGLEW.2.2.0.dylib @executable_path/libGLEW.dylib $(APP_MACOS_DIR)/sm64coopdx > /dev/null 2>&1; \
install_name_tool -id @executable_path/libGLEW.dylib $(APP_MACOS_DIR)/libGLEW.dylib > /dev/null 2>&1; \
codesign --force --deep --sign - $(APP_MACOS_DIR)/libGLEW.dylib; \
cp res/icon.icns $(APP_RESOURCES_DIR)/icon.icns; \
mkdir res/build; \
xcrun actool res/icon.icon --compile res/build --app-icon icon --output-partial-info-plist res/build/Info.plist --minimum-deployment-target $(MIN_MACOS_VERSION) --platform macosx > /dev/null 2>&1; \
mv res/build/Assets.car $(APP_RESOURCES_DIR)/; \
cp res/icon.icns $(APP_RESOURCES_DIR)/; \
rm -rf res/build; \
echo "APPL????" > $(APP_CONTENTS_DIR)/PkgInfo; \
echo '<?xml version="1.0" encoding="UTF-8"?>' > $(APP_CONTENTS_DIR)/Info.plist; \
echo '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' >> $(APP_CONTENTS_DIR)/Info.plist; \
@ -1623,7 +1624,7 @@ all:
echo ' <key>CFBundleIconFile</key>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <string>icon</string>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <key>CFBundleIconName</key>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <string>AppIcon</string>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <string>icon</string>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <key>CFBundleDisplayName</key>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <string>sm64coopdx</string>' >> $(APP_CONTENTS_DIR)/Info.plist; \
echo ' <!-- Add other keys and values here -->' >> $(APP_CONTENTS_DIR)/Info.plist; \

View file

@ -535,6 +535,8 @@ def def_constant(fname, processed_constant, skip_constant):
continue
if '"' in c[1]:
s += '\n--- @type string\n'
elif "." in c[1]:
s += '\n--- @type number\n'
else:
s += '\n--- @type integer\n'
s += '%s = %s\n' % (c[0], c[1])

View file

@ -80,6 +80,7 @@ in_files = [
"src/audio/seqplayer.h",
"src/engine/lighting_engine.h",
"src/pc/network/sync_object.h",
"src/audio/load.h",
]
override_allowed_functions = {
@ -99,6 +100,7 @@ override_allowed_functions = {
"src/game/ingame_menu.h": [ "set_min_dialog_width", "set_dialog_override_pos", "reset_dialog_override_pos", "set_dialog_override_color", "reset_dialog_override_color", "set_menu_mode", "create_dialog_box", "create_dialog_box_with_var", "create_dialog_inverted_box", "create_dialog_box_with_response", "reset_dialog_render_state", "set_dialog_box_state", "handle_special_dialog_text" ],
"src/audio/seqplayer.h": [ "sequence_player_set_tempo", "sequence_player_set_tempo_acc", "sequence_player_set_transposition", "sequence_player_get_tempo", "sequence_player_get_tempo_acc", "sequence_player_get_transposition", "sequence_player_get_volume", "sequence_player_get_fade_volume", "sequence_player_get_mute_volume_scale" ],
"src/pc/network/sync_object.h": [ "sync_object_is_initialized", "sync_object_is_owned_locally", "sync_object_get_object" ],
"src/audio/load.h": [ "set_sound_bank_override" ],
}
override_disallowed_functions = {
@ -120,14 +122,14 @@ override_disallowed_functions = {
"src/game/sound_init.h": [ "_loop_", "thread4_", "set_sound_mode" ],
"src/pc/network/network_utils.h": [ "network_get_player_text_color[^_]" ],
"src/pc/network/network_player.h": [ "_init", "_connected[^_]", "_shutdown", "_disconnected", "_update", "construct_player_popup", "network_player_name_valid" ],
"src/game/object_helpers.c": [ "spawn_obj", "^bhv_", "abs[fi]", "^bit_shift", "_debug$", "^stub_", "_set_model", "cur_obj_set_direction_table", "cur_obj_progress_direction_table" ],
"src/game/obj_behaviors.c": [ "debug_", "turn_obj_away_from_surface" ],
"src/game/object_helpers.c": [ "spawn_obj", "^bhv_", "geo_", "abs[fi]", "^bit_shift", "_debug$", "^stub_", "_set_model", "cur_obj_set_direction_table", "cur_obj_progress_direction_table" ],
"src/game/obj_behaviors.c": [ "debug_", "geo_", "turn_obj_away_from_surface"],
"src/game/obj_behaviors_2.c": [ "wiggler_jumped_on_attack_handler", "huge_goomba_weakly_attacked" ],
"src/game/spawn_sound.h": [ "exec_anim_sound_state" ],
"src/game/level_info.h": [ "_name_table", "convert_string_" ],
"src/pc/lua/utils/smlua_obj_utils.h": [ "spawn_object_remember_field" ],
"src/game/camera.h": [ "update_camera", "init_camera", "stub_camera", "^reset_camera", "move_point_along_spline", "romhack_camera_init_settings", "romhack_camera_reset_settings" ],
"src/game/behavior_actions.h": [ "bhv_dust_smoke_loop", "bhv_init_room" ],
"src/game/camera.h": [ "geo_", "update_camera", "init_camera", "stub_camera", "^reset_camera", "move_point_along_spline", "romhack_camera_init_settings", "romhack_camera_reset_settings" ],
"src/game/behavior_actions.h": [ "bhv_dust_smoke_loop", "bhv_init_room", "geo_" ],
"src/pc/lua/utils/smlua_audio_utils.h": [ "smlua_audio_utils_override", "audio_custom_shutdown", "smlua_audio_custom_deinit", "audio_sample_destroy_pending_copies", "audio_custom_update_volume" ],
"src/pc/lua/utils/smlua_level_utils.h": [ "smlua_level_util_reset" ],
"src/pc/lua/utils/smlua_text_utils.h": [ "smlua_text_utils_init", "smlua_text_utils_shutdown", "smlua_text_utils_dialog_get_unmodified"],
@ -825,7 +827,7 @@ def build_param(fid, param, i):
lot = translate_type_to_lot(ptype)
s = ' %s %s = (%s)smlua_to_cobject(L, %d, %s);' % (ptype, pid, ptype, i, lot)
if '???' in lot or "GRAPHNODE" in lot:
if '???' in lot:
s = '//' + s + ' <--- UNIMPLEMENTED'
else:
s = ' ' + s
@ -949,7 +951,7 @@ def build_function(function, do_extern):
sparam = build_param(fid, param, i)
param_var, param_value = sparam.split('=')
param_type = param_var.replace(pid, '').strip()
s += ' %s = (%s) NULL;\n' % (param_var.strip(), param_type)
s += ' %s = (%s) %s;\n' % (param_var.strip(), param_type, "NULL" if '*' in param_type else "0")
s += ' if (top >= %d) {\n' % (i)
s += ' %s = %s\n' % (pid, param_value.strip())
s += ' if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %%u for function \'%%s\'", %d, "%s"); return 0; }\n' % (i, fid)
@ -1340,7 +1342,7 @@ def doc_function(fname, function):
s += '- None\n'
s += '\n### Returns\n'
if rtype != None:
if len(rvalues) > 0:
for _, ptype, plink in rvalues:
if plink:
s += '- [%s](%s)\n' % (ptype, plink)

View file

@ -30,7 +30,7 @@ SMLUA_CALL_EVENT_HOOKS_SET_HOOK_RESULT = """
SMLUA_CALL_EVENT_HOOKS_CALLBACK = """
// call the callback
if (0 != smlua_call_hook(L, {n_inputs}, {n_outputs}, 0, hook->mod[i], hook->modFile[i])) {{
LOG_LUA("Failed to call the callback for hook %s", sLuaHookedEventTypeName[{hook_type}]);
LOG_LUA("Failed to call the callback for hook %s - '%s/%s'", sLuaHookedEventTypeName[{hook_type}], hook->mod[i]->relativePath, hook->modFile[i]->relativePath);
continue;
}}{set_hook_result}
"""

View file

@ -306,6 +306,49 @@ IN_OUT_BOUNCE = function (x) return x < 0.5 and (1 - OUT_BOUNCE(1 - 2 * x)) / 2
---@return number
OUT_IN_BOUNCE = function (x) return x < 0.5 and 0.5 * OUT_BOUNCE(x * 2) or 0.5 + 0.5 * IN_BOUNCE(2 * x - 1) end
--- @alias EasingFunction
--- | `IN_SINE`
--- | `OUT_SINE`
--- | `IN_OUT_SINE`
--- | `OUT_IN_SINE`
--- | `IN_QUAD`
--- | `OUT_QUAD`
--- | `IN_OUT_QUAD`
--- | `OUT_IN_QUAD`
--- | `IN_CUBIC`
--- | `OUT_CUBIC`
--- | `IN_OUT_CUBIC`
--- | `OUT_IN_CUBIC`
--- | `IN_QUART`
--- | `OUT_QUART`
--- | `IN_OUT_QUART`
--- | `OUT_IN_QUART`
--- | `IN_QUINT`
--- | `OUT_QUINT`
--- | `IN_OUT_QUINT`
--- | `OUT_IN_QUINT`
--- | `IN_EXPO`
--- | `OUT_EXPO`
--- | `IN_OUT_EXPO`
--- | `OUT_IN_EXPO`
--- | `IN_CIRC`
--- | `OUT_CIRC`
--- | `IN_OUT_CIRC`
--- | `OUT_IN_CIRC`
--- | `IN_BACK`
--- | `OUT_BACK`
--- | `IN_OUT_BACK`
--- | `OUT_IN_BACK`
--- | `IN_ELASTIC`
--- | `OUT_ELASTIC`
--- | `IN_OUT_ELASTIC`
--- | `OUT_IN_ELASTIC`
--- | `IN_BOUNCE`
--- | `OUT_BOUNCE`
--- | `IN_OUT_BOUNCE`
--- | `OUT_IN_BOUNCE`
--- | fun(x: number): number
--------------------
-- math functions --
--------------------
@ -385,7 +428,7 @@ function math.round(x)
return x > 0 and __math_floor(x + 0.5) or __math_ceil(x - 0.5)
end
--- @param t function | number
--- @param t EasingFunction | number
--- @param a number
--- @param b number
--- @param x number

View file

@ -308,6 +308,49 @@ IN_OUT_BOUNCE = function (x) return x < 0.5 and (1 - OUT_BOUNCE(1 - 2 * x)) / 2
---@return number
OUT_IN_BOUNCE = function (x) return x < 0.5 and 0.5 * OUT_BOUNCE(x * 2) or 0.5 + 0.5 * IN_BOUNCE(2 * x - 1) end
--- @alias EasingFunction
--- | `IN_SINE`
--- | `OUT_SINE`
--- | `IN_OUT_SINE`
--- | `OUT_IN_SINE`
--- | `IN_QUAD`
--- | `OUT_QUAD`
--- | `IN_OUT_QUAD`
--- | `OUT_IN_QUAD`
--- | `IN_CUBIC`
--- | `OUT_CUBIC`
--- | `IN_OUT_CUBIC`
--- | `OUT_IN_CUBIC`
--- | `IN_QUART`
--- | `OUT_QUART`
--- | `IN_OUT_QUART`
--- | `OUT_IN_QUART`
--- | `IN_QUINT`
--- | `OUT_QUINT`
--- | `IN_OUT_QUINT`
--- | `OUT_IN_QUINT`
--- | `IN_EXPO`
--- | `OUT_EXPO`
--- | `IN_OUT_EXPO`
--- | `OUT_IN_EXPO`
--- | `IN_CIRC`
--- | `OUT_CIRC`
--- | `IN_OUT_CIRC`
--- | `OUT_IN_CIRC`
--- | `IN_BACK`
--- | `OUT_BACK`
--- | `IN_OUT_BACK`
--- | `OUT_IN_BACK`
--- | `IN_ELASTIC`
--- | `OUT_ELASTIC`
--- | `IN_OUT_ELASTIC`
--- | `OUT_IN_ELASTIC`
--- | `IN_BOUNCE`
--- | `OUT_BOUNCE`
--- | `IN_OUT_BOUNCE`
--- | `OUT_IN_BOUNCE`
--- | fun(x: number): number
--------------------
-- math functions --
--------------------
@ -387,7 +430,7 @@ function math.round(x)
return x > 0 and __math_floor(x + 0.5) or __math_ceil(x - 0.5)
end
--- @param t function | number
--- @param t EasingFunction | number
--- @param a number
--- @param b number
--- @param x number
@ -2717,6 +2760,42 @@ CONSOLE_MESSAGE_ERROR = 2 --- @type ConsoleMessageLevel
--- | `CONSOLE_MESSAGE_WARNING`
--- | `CONSOLE_MESSAGE_ERROR`
--- @type number
ROTATION_PIVOT_X_LEFT = 0.0
--- @type number
ROTATION_PIVOT_X_CENTER = 0.5
--- @type number
ROTATION_PIVOT_X_RIGHT = 1.0
--- @type number
ROTATION_PIVOT_Y_TOP = 0.0
--- @type number
ROTATION_PIVOT_Y_CENTER = 0.5
--- @type number
ROTATION_PIVOT_Y_BOTTOM = 1.0
--- @type number
TEXT_HALIGN_LEFT = 0.0
--- @type number
TEXT_HALIGN_CENTER = 0.5
--- @type number
TEXT_HALIGN_RIGHT = 1.0
--- @type number
TEXT_VALIGN_TOP = 0.0
--- @type number
TEXT_VALIGN_CENTER = 0.5
--- @type number
TEXT_VALIGN_BOTTOM = 1.0
RESOLUTION_DJUI = 0 --- @type HudUtilsResolution
RESOLUTION_N64 = 1 --- @type HudUtilsResolution
RESOLUTION_COUNT = 2 --- @type HudUtilsResolution
@ -2735,16 +2814,18 @@ FILTER_COUNT = 2 --- @type HudUtilsFilter
--- | `FILTER_LINEAR`
--- | `FILTER_COUNT`
FONT_NORMAL = 0 --- @type DjuiFontType
FONT_MENU = 1 --- @type DjuiFontType
FONT_HUD = 2 --- @type DjuiFontType
FONT_ALIASED = 3 --- @type DjuiFontType
FONT_CUSTOM_HUD = 4 --- @type DjuiFontType
FONT_RECOLOR_HUD = 5 --- @type DjuiFontType
FONT_SPECIAL = 6 --- @type DjuiFontType
FONT_COUNT = 7 --- @type DjuiFontType
FONT_LEGACY = -1 --- @type DjuiFontType
FONT_NORMAL = 0 --- @type DjuiFontType
FONT_MENU = 1 --- @type DjuiFontType
FONT_HUD = 2 --- @type DjuiFontType
FONT_ALIASED = 3 --- @type DjuiFontType
FONT_CUSTOM_HUD = 4 --- @type DjuiFontType
FONT_RECOLOR_HUD = 5 --- @type DjuiFontType
FONT_SPECIAL = 6 --- @type DjuiFontType
FONT_COUNT = 7 --- @type DjuiFontType
--- @alias DjuiFontType
--- | `FONT_LEGACY`
--- | `FONT_NORMAL`
--- | `FONT_MENU`
--- | `FONT_HUD`
@ -3276,7 +3357,7 @@ PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT = 10
PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE = -5
--- @type integer
PVP_ATTACK_OVERRIDE_VANILLA_INVINCIBILITY = 0x0000FFFF
PVP_ATTACK_KNOCKBACK_ACTION_ARG = 0x10000
--- @type integer
INT_STATUS_ATTACK_MASK = 0x000000FF
@ -3635,7 +3716,7 @@ HUD_DISPLAY_DEFAULT = HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_CO
--- | `HUD_DISPLAY_DEFAULT`
--- @type integer
LE_MAX_LIGHTS = 512
LE_MAX_LIGHTS = 1024
LE_MODE_AFFECT_ALL_SHADED_AND_COLORED = 0 --- @type LEMode
LE_MODE_AFFECT_ALL_SHADED = 1 --- @type LEMode
@ -8191,6 +8272,9 @@ HOOK_MAX = 60 --- @type LuaHookedEventType
--- | `HOOK_ON_PACKET_BYTESTRING_RECEIVE`
--- | `HOOK_MAX`
--- @type integer
MAX_HOOKED_BEHAVIORS = 1024
HUD_DISPLAY_LIVES = 0 --- @type HudDisplayValue
HUD_DISPLAY_COINS = 1 --- @type HudDisplayValue
HUD_DISPLAY_STARS = 2 --- @type HudDisplayValue

View file

@ -3030,7 +3030,7 @@ end
--- @param name string
--- @return BehaviorId
--- gets a behavior ID from a behavior name
--- Gets a behavior ID from a behavior name
function get_id_from_behavior_name(name)
-- ...
end
@ -3856,7 +3856,9 @@ function djui_hud_reset_color()
-- ...
end
--- @return HudUtilsRotation
--- @return integer rotation
--- @return number pivotX
--- @return number pivotY
--- Gets the current DJUI HUD rotation
function djui_hud_get_rotation()
-- ...
@ -3881,6 +3883,29 @@ function djui_hud_set_rotation_interpolated(prevRotation, prevPivotX, prevPivotY
-- ...
end
--- @return number textHAlign
--- @return number textVAlign
--- Gets the current DJUI HUD text alignment
function djui_hud_get_text_alignment()
-- ...
end
--- @param textHAlign number
--- @param textVAlign number
--- Sets the current DJUI HUD text alignment
function djui_hud_set_text_alignment(textHAlign, textVAlign)
-- ...
end
--- @param prevTextHAlign number
--- @param prevTextVAlign number
--- @param textHAlign number
--- @param textVAlign number
--- Sets the current DJUI HUD text alignment interpolated
function djui_hud_set_text_alignment_interpolated(prevTextHAlign, prevTextVAlign, textHAlign, textVAlign)
-- ...
end
--- @return integer
--- Gets the screen width in the current DJUI HUD resolution
function djui_hud_get_screen_width()
@ -5103,6 +5128,12 @@ function le_set_ambient_color(r, g, b)
-- ...
end
--- @param count integer
--- Sets the max amount of lights that can affect a vertex
function le_set_max_lights_per_vertex(count)
-- ...
end
--- @param pos Vec3f
--- @param out Color
--- @param lightIntensityScalar number
@ -5234,6 +5265,12 @@ function le_set_light_use_surface_normals(id, useSurfaceNormals)
-- ...
end
--- @param bank integer
--- Overrides the soundbank, set to -1 to reset
function set_sound_bank_override(bank)
-- ...
end
--- @param m MarioState
--- @return integer
--- Checks if Mario's current animation has reached its final frame (i.e., the last valid frame in the animation). Useful for deciding when to transition out of an animation-driven action
@ -11516,6 +11553,12 @@ function get_dialog_response()
-- ...
end
--- @return integer
--- Gets the active time stop flags, used to freeze specific objects during cutscenes
function get_time_stop_flags()
-- ...
end
--- @return string
--- Gets the local discord ID if it isn't disabled, otherwise "0" is returned
function get_local_discord_id()
@ -11622,35 +11665,42 @@ function get_os_name()
end
--- @return GraphNodeRoot
--- Gets the current GraphNodeRoot
--- Gets the current root node being processed
function geo_get_current_root()
-- ...
end
--- @return GraphNodeMasterList
--- Gets the current GraphNodeMasterList
--- Gets the current master list node being processed
function geo_get_current_master_list()
-- ...
end
--- @return GraphNodePerspective
--- Gets the current GraphNodePerspective
--- Gets the current perspective node being processed
function geo_get_current_perspective()
-- ...
end
--- @return GraphNodeCamera
--- Gets the current GraphNodeCamera
--- Gets the current camera node being processed
function geo_get_current_camera()
-- ...
end
--- @return GraphNodeHeldObject
--- Gets the current GraphNodeHeldObject
--- Gets the current held object node being processed
function geo_get_current_held_object()
-- ...
end
--- @param node GraphNode
--- @param obj GraphNodeObject
--- Skips graph node interpolation for a frame
function geo_skip_interpolation(node, obj)
-- ...
end
--- @param tex Pointer_Texture
--- @return table
--- Converts a texture's pixels to a Lua table. Returns nil if failed. Otherwise, returns a 1-indexed table of RGBA pixels
@ -12062,6 +12112,18 @@ function set_whirlpools(x, y, z, strength, area, index)
-- ...
end
--- @param o Object
--- Skips object interpolation for a frame
function obj_skip_interpolation(o)
-- ...
end
--- @param o Object
--- Skips animation interpolation for a frame
function obj_anim_skip_interpolation(o)
-- ...
end
--- Resets every modified dialog back to vanilla
function smlua_text_utils_reset_all()
-- ...

View file

@ -97,11 +97,6 @@ gServerSettings = {}
--- Struct containing the settings for Nametags
gNametagsSettings = {}
--- @type Camera
--- Struct contaning camera fields
--- - This camera is the same as `gMarioStates[i].area.camera` or `gCurrentArea.camera`
gCamera = {}
-----------
-- hooks --
-----------

View file

@ -955,14 +955,6 @@
--- @field public translation Vec3s
--- @field public rotation Vec3s
--- @class HudUtilsRotation
--- @field public rotation number
--- @field public rotationDiff number
--- @field public prevPivotX number
--- @field public prevPivotY number
--- @field public pivotX number
--- @field public pivotY number
--- @class InstantWarp
--- @field public id integer
--- @field public area integer

View file

@ -597,5 +597,11 @@ enum BehaviorId get_id_from_behavior_name(const char* name) {
return i;
}
}
for (int i = 0; i < gHookedBehaviorsCount; i++) {
struct LuaHookedBehavior *hooked = &gHookedBehaviors[i];
if (hooked->bhvName && !strcmp(name, hooked->bhvName)) {
return hooked->overrideId;
}
}
return id_bhv_max_count;
}

View file

@ -2449,6 +2449,48 @@ static void ParseBehaviorScriptSymbol(GfxData *aGfxData, DataNode<BehaviorScript
PrintDataError(" ERROR: Unknown behavior symbol: %s", _Symbol.begin());
}
static bool DynOS_Bhv_CheckCommands(const BehaviorScript *aBhv, const Array<BehaviorScript> &aCommands) {
u8 bhvCommand = (*aBhv >> 24) & 0xFF;
for (const auto &commandToCheck : aCommands) {
if (bhvCommand == ((commandToCheck >> 24) & 0xFF)) {
return true;
}
}
return false;
}
static bool DynOS_Bhv_Validate(GfxData *aGfxData, const DataNode<BehaviorScript> *aNode) {
// 1st command must be BEGIN
if (!DynOS_Bhv_CheckCommands(aNode->mData + 0, { BEGIN(0) })) {
PrintDataError(" ERROR: Validation failed for behavior %s: First command of the script must be BEGIN.", aNode->mName.begin());
return false;
}
// 2nd command must be ID
if (!DynOS_Bhv_CheckCommands(aNode->mData + 1, { ID(0) })) {
PrintDataError(" ERROR: Validation failed for behavior %s: Second command of the script must be ID.", aNode->mName.begin());
return false;
}
// Last command must be a terminating command
if (!DynOS_Bhv_CheckCommands(aNode->mData + aNode->mSize - 1, {
CALL(0),
RETURN(),
GOTO(0),
END_LOOP(),
BREAK(),
DEACTIVATE(),
CALL_EXT(0),
GOTO_EXT(0),
})) {
PrintDataError(" ERROR: Validation failed for behavior %s: Last command of the script must be one of:\n CALL, RETURN, GOTO, END_LOOP, BREAK, DEACTIVATE", aNode->mName.begin());
return false;
}
return true;
}
DataNode<BehaviorScript> *DynOS_Bhv_Parse(GfxData *aGfxData, DataNode<BehaviorScript> *aNode, bool aDisplayPercent) {
if (aNode->mData) return aNode;
@ -2460,9 +2502,13 @@ DataNode<BehaviorScript> *DynOS_Bhv_Parse(GfxData *aGfxData, DataNode<BehaviorSc
ParseBehaviorScriptSymbol(aGfxData, aNode, _Head, _TokenIndex, _SwitchNodes);
if (aDisplayPercent && aGfxData->mErrorCount == 0) { PrintNoNewLine("%3d%%\b\b\b\b", (s32) (_TokenIndex * 100) / aNode->mTokens.Count()); }
}
if (aDisplayPercent && aGfxData->mErrorCount == 0) { Print("100%%"); }
aNode->mSize = (u32)(_Head - aNode->mData);
aNode->mLoadIndex = aGfxData->mLoadIndex++;
// Validate behavior script
DynOS_Bhv_Validate(aGfxData, aNode);
if (aDisplayPercent && aGfxData->mErrorCount == 0) { Print("100%%"); }
return aNode;
}
@ -2592,6 +2638,12 @@ static DataNode<BehaviorScript> *DynOS_Bhv_Load(BinFile *aFile, GfxData *aGfxDat
}
}
// Validate it
if (!DynOS_Bhv_Validate(aGfxData, _Node)) {
Delete(_Node);
return NULL;
}
// Add it
if (aGfxData != NULL) {
aGfxData->mBehaviorScripts.Add(_Node);

View file

@ -1149,6 +1149,18 @@
<br />
## [djui_hud_utils.h](#djui_hud_utils.h)
- ROTATION_PIVOT_X_LEFT
- ROTATION_PIVOT_X_CENTER
- ROTATION_PIVOT_X_RIGHT
- ROTATION_PIVOT_Y_TOP
- ROTATION_PIVOT_Y_CENTER
- ROTATION_PIVOT_Y_BOTTOM
- TEXT_HALIGN_LEFT
- TEXT_HALIGN_CENTER
- TEXT_HALIGN_RIGHT
- TEXT_VALIGN_TOP
- TEXT_VALIGN_CENTER
- TEXT_VALIGN_BOTTOM
### [enum HudUtilsResolution](#HudUtilsResolution)
| Identifier | Value |
@ -1167,6 +1179,7 @@
### [enum DjuiFontType](#DjuiFontType)
| Identifier | Value |
| :--------- | :---- |
| FONT_LEGACY | -1 |
| FONT_NORMAL | 0 |
| FONT_MENU | 1 |
| FONT_HUD | 2 |
@ -1459,7 +1472,7 @@
- ATTACK_FROM_BELOW
- PVP_ATTACK_KNOCKBACK_TIMER_DEFAULT
- PVP_ATTACK_KNOCKBACK_TIMER_OVERRIDE
- PVP_ATTACK_OVERRIDE_VANILLA_INVINCIBILITY
- PVP_ATTACK_KNOCKBACK_ACTION_ARG
- INT_STATUS_ATTACK_MASK
- INT_STATUS_HOOT_GRABBED_BY_MARIO
- INT_STATUS_MARIO_UNK1
@ -3525,6 +3538,7 @@
| HOOK_ON_CLEAR_AREAS | 58 |
| HOOK_ON_PACKET_BYTESTRING_RECEIVE | 59 |
| HOOK_MAX | 60 |
- MAX_HOOKED_BEHAVIORS
[:arrow_up_small:](#)

View file

@ -261,7 +261,7 @@ Gets a behavior name from a behavior ID (bhvMyGreatMODCustom004)
## [get_id_from_behavior_name](#get_id_from_behavior_name)
### Description
gets a behavior ID from a behavior name
Gets a behavior ID from a behavior name
### Lua Example
`local enumValue = get_id_from_behavior_name(name)`
@ -1312,7 +1312,8 @@ Calculates and returns the pitch and yaw angles from one 3D position (`from`) to
| to | [Vec3f](structs.md#Vec3f) |
### Returns
- None
- `integer`
- `integer`
### C Prototype
`void calculate_angles(Vec3f from, Vec3f to, RET s16 *pitch, RET s16 *yaw);`
@ -1585,7 +1586,7 @@ Applies a roll-based shake effect to the camera. Simulates rotational disturbanc
| roll | `integer` |
### Returns
- None
- `integer`
### C Prototype
`void shake_camera_roll(INOUT s16 *roll);`
@ -2825,7 +2826,7 @@ Gets the current DJUI HUD font
- `integer`
### C Prototype
`u8 djui_hud_get_font(void);`
`s8 djui_hud_get_font(void);`
[:arrow_up_small:](#)
@ -2928,16 +2929,18 @@ Resets the current DJUI HUD color
Gets the current DJUI HUD rotation
### Lua Example
`local hudUtilsRotationValue = djui_hud_get_rotation()`
`local rotation, pivotX, pivotY = djui_hud_get_rotation()`
### Parameters
- None
### Returns
- [HudUtilsRotation](structs.md#HudUtilsRotation)
- `integer`
- `number`
- `number`
### C Prototype
`struct HudUtilsRotation* djui_hud_get_rotation(void);`
`void djui_hud_get_rotation(RET s16 *rotation, RET f32 *pivotX, RET f32 *pivotY);`
[:arrow_up_small:](#)
@ -2990,7 +2993,79 @@ Sets the current DJUI HUD rotation interpolated
- None
### C Prototype
`void djui_hud_set_rotation_interpolated(s32 prevRotation, f32 prevPivotX, f32 prevPivotY, s32 rotation, f32 pivotX, f32 pivotY);`
`void djui_hud_set_rotation_interpolated(s16 prevRotation, f32 prevPivotX, f32 prevPivotY, s16 rotation, f32 pivotX, f32 pivotY);`
[:arrow_up_small:](#)
<br />
## [djui_hud_get_text_alignment](#djui_hud_get_text_alignment)
### Description
Gets the current DJUI HUD text alignment
### Lua Example
`local textHAlign, textVAlign = djui_hud_get_text_alignment()`
### Parameters
- None
### Returns
- `number`
- `number`
### C Prototype
`void djui_hud_get_text_alignment(RET f32 *textHAlign, RET f32 *textVAlign);`
[:arrow_up_small:](#)
<br />
## [djui_hud_set_text_alignment](#djui_hud_set_text_alignment)
### Description
Sets the current DJUI HUD text alignment
### Lua Example
`djui_hud_set_text_alignment(textHAlign, textVAlign)`
### Parameters
| Field | Type |
| ----- | ---- |
| textHAlign | `number` |
| textVAlign | `number` |
### Returns
- None
### C Prototype
`void djui_hud_set_text_alignment(f32 textHAlign, f32 textVAlign);`
[:arrow_up_small:](#)
<br />
## [djui_hud_set_text_alignment_interpolated](#djui_hud_set_text_alignment_interpolated)
### Description
Sets the current DJUI HUD text alignment interpolated
### Lua Example
`djui_hud_set_text_alignment_interpolated(prevTextHAlign, prevTextVAlign, textHAlign, textVAlign)`
### Parameters
| Field | Type |
| ----- | ---- |
| prevTextHAlign | `number` |
| prevTextVAlign | `number` |
| textHAlign | `number` |
| textVAlign | `number` |
### Returns
- None
### C Prototype
`void djui_hud_set_text_alignment_interpolated(f32 prevTextHAlign, f32 prevTextVAlign, f32 textHAlign, f32 textVAlign);`
[:arrow_up_small:](#)

View file

@ -147,6 +147,29 @@ Sets the lighting engine ambient color
<br />
## [le_set_max_lights_per_vertex](#le_set_max_lights_per_vertex)
### Description
Sets the max amount of lights that can affect a vertex
### Lua Example
`le_set_max_lights_per_vertex(count)`
### Parameters
| Field | Type |
| ----- | ---- |
| count | `integer` |
### Returns
- None
### C Prototype
`void le_set_max_lights_per_vertex(u8 count);`
[:arrow_up_small:](#)
<br />
## [le_calculate_lighting_color](#le_calculate_lighting_color)
### Description
@ -560,6 +583,35 @@ Sets whether a lighting engine point light will use a surface's normals to deter
<br />
---
# functions from load.h
<br />
## [set_sound_bank_override](#set_sound_bank_override)
### Description
Overrides the soundbank, set to -1 to reset
### Lua Example
`set_sound_bank_override(bank)`
### Parameters
| Field | Type |
| ----- | ---- |
| bank | `integer` |
### Returns
- None
### C Prototype
`void set_sound_bank_override(s32 bank);`
[:arrow_up_small:](#)
<br />
---
# functions from mario.h
@ -5082,7 +5134,9 @@ Calculates the distance between two points in 3D space (`from` and `to`), as wel
| to | [Vec3f](structs.md#Vec3f) |
### Returns
- None
- `number`
- `integer`
- `integer`
### C Prototype
`void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, RET f32 *dist, RET s16 *pitch, RET s16 *yaw);`

View file

@ -2945,7 +2945,7 @@ Determines an object's forward speed multiplier.
| floor_nY | `number` |
### Returns
- None
- `number`
### C Prototype
`void calc_obj_friction(RET f32 *objFriction, f32 floor_nY);`
@ -4419,7 +4419,7 @@ Begin by increasing the current object's scale by `scaleVel`, and slowly decreas
| blinkLength | `integer` |
### Returns
- None
- `integer`
### C Prototype
`void obj_update_blinking(INOUT s32 *blinkTimer, s16 baseCycleLength, s16 cycleLengthRange, s16 blinkLength);`
@ -4743,7 +4743,8 @@ Treats far home as Mario. Returns the distance and angle to the nearest player
| threshold | `number` |
### Returns
- None
- `integer`
- `integer`
### C Prototype
`void treat_far_home_as_mario(f32 threshold, RET s32* distanceToPlayer, RET s32* angleToPlayer);`

View file

@ -1963,7 +1963,7 @@ Marks an object to be unloaded at the end of the frame
| dragStrength | `number` |
### Returns
- None
- `number`
### C Prototype
`void apply_drag_to_value(INOUT f32 *value, f32 dragStrength);`

View file

@ -1775,6 +1775,27 @@ Gets the choice selected inside of a dialog box (0-1)
<br />
## [get_time_stop_flags](#get_time_stop_flags)
### Description
Gets the active time stop flags, used to freeze specific objects during cutscenes
### Lua Example
`local integerValue = get_time_stop_flags()`
### Parameters
- None
### Returns
- `integer`
### C Prototype
`u32 get_time_stop_flags(void);`
[:arrow_up_small:](#)
<br />
## [get_local_discord_id](#get_local_discord_id)
### Description
@ -2154,7 +2175,7 @@ Gets the name of the operating system the game is running on
## [geo_get_current_root](#geo_get_current_root)
### Description
Gets the current GraphNodeRoot
Gets the current root node being processed
### Lua Example
`local graphNodeRootValue = geo_get_current_root()`
@ -2175,7 +2196,7 @@ Gets the current GraphNodeRoot
## [geo_get_current_master_list](#geo_get_current_master_list)
### Description
Gets the current GraphNodeMasterList
Gets the current master list node being processed
### Lua Example
`local graphNodeMasterListValue = geo_get_current_master_list()`
@ -2196,7 +2217,7 @@ Gets the current GraphNodeMasterList
## [geo_get_current_perspective](#geo_get_current_perspective)
### Description
Gets the current GraphNodePerspective
Gets the current perspective node being processed
### Lua Example
`local graphNodePerspectiveValue = geo_get_current_perspective()`
@ -2217,7 +2238,7 @@ Gets the current GraphNodePerspective
## [geo_get_current_camera](#geo_get_current_camera)
### Description
Gets the current GraphNodeCamera
Gets the current camera node being processed
### Lua Example
`local graphNodeCameraValue = geo_get_current_camera()`
@ -2238,7 +2259,7 @@ Gets the current GraphNodeCamera
## [geo_get_current_held_object](#geo_get_current_held_object)
### Description
Gets the current GraphNodeHeldObject
Gets the current held object node being processed
### Lua Example
`local graphNodeHeldObjectValue = geo_get_current_held_object()`
@ -2256,6 +2277,30 @@ Gets the current GraphNodeHeldObject
<br />
## [geo_skip_interpolation](#geo_skip_interpolation)
### Description
Skips graph node interpolation for a frame
### Lua Example
`geo_skip_interpolation(node, obj)`
### Parameters
| Field | Type |
| ----- | ---- |
| node | [GraphNode](structs.md#GraphNode) |
| obj | [GraphNodeObject](structs.md#GraphNodeObject) |
### Returns
- None
### C Prototype
`void geo_skip_interpolation(struct GraphNode *node, struct GraphNodeObject *obj);`
[:arrow_up_small:](#)
<br />
## [texture_to_lua_table](#texture_to_lua_table)
### Description
@ -3531,6 +3576,52 @@ Sets the parameters of one of the two whirlpools (0-indexed) in an area
<br />
## [obj_skip_interpolation](#obj_skip_interpolation)
### Description
Skips object interpolation for a frame
### Lua Example
`obj_skip_interpolation(o)`
### Parameters
| Field | Type |
| ----- | ---- |
| o | [Object](structs.md#Object) |
### Returns
- None
### C Prototype
`void obj_skip_interpolation(struct Object *o);`
[:arrow_up_small:](#)
<br />
## [obj_anim_skip_interpolation](#obj_anim_skip_interpolation)
### Description
Skips animation interpolation for a frame
### Lua Example
`obj_anim_skip_interpolation(o)`
### Parameters
| Field | Type |
| ----- | ---- |
| o | [Object](structs.md#Object) |
### Returns
- None
### C Prototype
`void obj_anim_skip_interpolation(struct Object *o);`
[:arrow_up_small:](#)
<br />
---
# functions from smlua_text_utils.h

View file

@ -763,6 +763,9 @@
- [djui_hud_get_rotation](functions-3.md#djui_hud_get_rotation)
- [djui_hud_set_rotation](functions-3.md#djui_hud_set_rotation)
- [djui_hud_set_rotation_interpolated](functions-3.md#djui_hud_set_rotation_interpolated)
- [djui_hud_get_text_alignment](functions-3.md#djui_hud_get_text_alignment)
- [djui_hud_set_text_alignment](functions-3.md#djui_hud_set_text_alignment)
- [djui_hud_set_text_alignment_interpolated](functions-3.md#djui_hud_set_text_alignment_interpolated)
- [djui_hud_get_screen_width](functions-3.md#djui_hud_get_screen_width)
- [djui_hud_get_screen_height](functions-3.md#djui_hud_get_screen_height)
- [djui_hud_get_mouse_x](functions-3.md#djui_hud_get_mouse_x)
@ -976,6 +979,7 @@
- [le_set_tone_mapping](functions-4.md#le_set_tone_mapping)
- [le_get_ambient_color](functions-4.md#le_get_ambient_color)
- [le_set_ambient_color](functions-4.md#le_set_ambient_color)
- [le_set_max_lights_per_vertex](functions-4.md#le_set_max_lights_per_vertex)
- [le_calculate_lighting_color](functions-4.md#le_calculate_lighting_color)
- [le_calculate_lighting_color_with_normal](functions-4.md#le_calculate_lighting_color_with_normal)
- [le_calculate_lighting_dir](functions-4.md#le_calculate_lighting_dir)
@ -996,6 +1000,11 @@
<br />
- load.h
- [set_sound_bank_override](functions-4.md#set_sound_bank_override)
<br />
- mario.h
- [is_anim_at_end](functions-4.md#is_anim_at_end)
- [is_anim_past_end](functions-4.md#is_anim_past_end)
@ -2047,6 +2056,7 @@
- [set_override_envfx](functions-7.md#set_override_envfx)
- [get_global_timer](functions-7.md#get_global_timer)
- [get_dialog_response](functions-7.md#get_dialog_response)
- [get_time_stop_flags](functions-7.md#get_time_stop_flags)
- [get_local_discord_id](functions-7.md#get_local_discord_id)
- [get_coopnet_id](functions-7.md#get_coopnet_id)
- [get_volume_master](functions-7.md#get_volume_master)
@ -2069,6 +2079,7 @@
- [geo_get_current_perspective](functions-7.md#geo_get_current_perspective)
- [geo_get_current_camera](functions-7.md#geo_get_current_camera)
- [geo_get_current_held_object](functions-7.md#geo_get_current_held_object)
- [geo_skip_interpolation](functions-7.md#geo_skip_interpolation)
- [texture_to_lua_table](functions-7.md#texture_to_lua_table)
- [get_texture_name](functions-7.md#get_texture_name)
@ -2130,6 +2141,8 @@
- [obj_set_vel](functions-7.md#obj_set_vel)
- [obj_move_xyz](functions-7.md#obj_move_xyz)
- [set_whirlpools](functions-7.md#set_whirlpools)
- [obj_skip_interpolation](functions-7.md#obj_skip_interpolation)
- [obj_anim_skip_interpolation](functions-7.md#obj_anim_skip_interpolation)
<br />

View file

@ -53,7 +53,6 @@
- [GraphNodeSwitchCase](#GraphNodeSwitchCase)
- [GraphNodeTranslation](#GraphNodeTranslation)
- [GraphNodeTranslationRotation](#GraphNodeTranslationRotation)
- [HudUtilsRotation](#HudUtilsRotation)
- [InstantWarp](#InstantWarp)
- [LakituState](#LakituState)
- [LevelValues](#LevelValues)
@ -1427,21 +1426,6 @@
<br />
## [HudUtilsRotation](#HudUtilsRotation)
| Field | Type | Access |
| ----- | ---- | ------ |
| rotation | `number` | |
| rotationDiff | `number` | |
| prevPivotX | `number` | |
| prevPivotY | `number` | |
| pivotX | `number` | |
| pivotY | `number` | |
[:arrow_up_small:](#)
<br />
## [InstantWarp](#InstantWarp)
| Field | Type | Access |

View file

@ -557,7 +557,7 @@ enum BehaviorId get_id_from_vanilla_behavior(const BehaviorScript* behavior);
const BehaviorScript* get_behavior_from_id(enum BehaviorId id);
/* |description|Gets a behavior name from a behavior ID (bhvMyGreatMODCustom004)|descriptionEnd| */
const char* get_behavior_name_from_id(enum BehaviorId id);
/* |description|gets a behavior ID from a behavior name|descriptionEnd| */
/* |description|Gets a behavior ID from a behavior name|descriptionEnd| */
enum BehaviorId get_id_from_behavior_name(const char* name);
#endif

Binary file not shown.

View file

@ -0,0 +1,93 @@
--- Don't add any functional code to this file ---
--- @meta
--- @class LuigiState
--- @class ToadState
--- @class WarioState
--- @class WaluigiState
--- @class ToadetteState
--- @field public averageForwardVel number
--- @class PeachState
--- @class DaisyState
--- @class YoshiState
--- @class BirdoState
--- @field public spitTimer integer
--- @field public framesSinceShoot integer
--- @field public flameCharge integer
--- @class SpikeState
--- @class PaulineState
--- @class RosalinaState
--- @field public canSpin boolean
--- @field public orbitObjActive boolean
--- @field public orbitObjDist number
--- @field public orbitObjAngle integer
--- @class WapeachState
--- @class DonkeyKongState
--- @class SonicState
--- @field public spinCharge integer
--- @field public groundYVel integer
--- @field public prevForwardVel integer
--- @field public peakHeight integer
--- @field public actionADone boolean
--- @field public actionBDone boolean
--- @field public bounced boolean
--- @field public spindashState integer
--- @field public instashieldTimer integer
--- @field public oxygen integer
--- @field public prevVelY number
--- @field public prevHeight number
--- @field public physTimer integer
--- @field public lastforwardPos Vec3f
--- @field public realFVel number
--- @class CharacterState
--- @field public mario MarioState
--- @field public luigi LuigiState
--- @field public toad ToadState
--- @field public wario WarioState
--- @field public waluigi WaluigiState
--- @field public toadette ToadetteState
--- @field public peach PeachState
--- @field public daisy DaisyState
--- @field public yoshi YoshiState
--- @field public birdo BirdoState
--- @field public spike SpikeState
--- @field public pauline PaulineState
--- @field public rosalina RosalinaState
--- @field public wapeach WapeachState
--- @field public donkeyKong DonkeyKongState
--- @field public sonic SonicState
--- @alias SonicMouthGSCId
--- | `SONIC_MOUTH_NORMAL`
--- | `SONIC_MOUTH_FROWN`
--- | `SONIC_MOUTH_GRIMACING`
--- | `SONIC_MOUTH_HAPPY`
--- | `SONIC_MOUTH_GRIN`
--- | `SONIC_MOUTH_ATTACKED`
--- | `SONIC_MOUTH_SHOCKED`
--- | `SONIC_MOUTH_SURPRISED`
--- | `SONIC_MOUTH_NEUTRAL`
--- @alias SonicMouthSideGSCId
--- | `SONIC_MOUTH_LEFT`
--- | `SONIC_MOUTH_RIGHT`
--- @alias HandParam
--- | `SONIC_HAND_RIGHT`
--- | `SONIC_HAND_LEFT`
--- | `WAPEACH_HAND_AXE`

View file

@ -0,0 +1,10 @@
-- Environment inclusions --
--[[
We only need to include character select rn
]]
charSelect = charSelect
if not charSelect then return end
_ENV = setmetatable(_G, { __index = charSelect })

View file

@ -0,0 +1,31 @@
--- Misc Functions ---
--- @param m MarioState
--- @param name string
--- @param accel? number
--- Plays a custom animation for MarioState `m`
function play_custom_anim(m, name, accel)
accel = accel or 0x10000
m.marioObj.header.gfx.animInfo.animAccel = accel
if (smlua_anim_util_get_current_animation_name(m.marioObj) ~= name or m.marioObj.header.gfx.animInfo.animID ~= -1) then
m.marioObj.header.gfx.animInfo.animID = -1
set_anim_to_frame(m, 0)
end
smlua_anim_util_set_animation(m.marioObj, name)
end
--- @param str string
--- @param splitAt? string
function string.split(str, splitAt)
if splitAt == nil then
splitAt = " "
end
local result = {}
for match in str:gmatch(string.format("[^%s]+", splitAt)) do
table.insert(result, match)
end
return result
end

View file

@ -0,0 +1,148 @@
local colObjLists = { OBJ_LIST_GENACTOR, OBJ_LIST_PUSHABLE, OBJ_LIST_SURFACE, OBJ_LIST_DESTRUCTIVE }
local bhvBlacklist = {
[id_bhvBowser] = true,
[id_bhvDoor] = true,
[id_bhvDoorWarp] = true,
[id_bhvStarDoor] = true,
[id_bhvUnlockDoorStar] = true,
[id_bhvToadMessage] = true,
[id_bhvFireSpitter] = true,
[id_bhvExplosion] = true
}
---@param o Object
---@param o2 Object
local function attack_bounce(o, o2)
o2.oVelY = 15.0
play_sound(SOUND_ACTION_BONK, o2.header.gfx.cameraToObject)
end
---@param o Object
---@param o2 Object
local function attack_bully(o, o2)
o2.oBullyLastNetworkPlayerIndex = o.globalPlayerIndex
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oForwardVel = 30.0
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
end
---@param o Object
---@param o2 Object
local function attack_bully_strong(o, o2)
o2.oBullyLastNetworkPlayerIndex = o.globalPlayerIndex
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oForwardVel = 50.0
o2.oVelY = 30.0
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED | INT_STATUS_INTERACTED
end
---@param o Object
---@param o2 Object
local function attack_mrblizzard(o, o2)
if o2.prevObj then
o2.prevObj.oAction = 2
o2.prevObj = nil
o2.oMrBlizzardHeldObj = nil
end
o2.oAction = MR_BLIZZARD_ACT_DEATH
end
---@param o Object
---@param o2 Object
local function attack_bullet_bill(o, o2)
spawn_mist_particles_with_sound(SOUND_GENERAL2_BOBOMB_EXPLOSION)
o2.oAction = 4
o2.oTimer = 0
end
---@param o Object
---@param o2 Object
local function attack_chuckya(o, o2)
o2.oAction = 2
o2.oVelY = 30
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oForwardVel = 25
end
---@param o Object
---@param o2 Object
local function attack_whomp(o, o2)
o2.oAction = 8
end
---@param o Object
---@param o2 Object
local function attack_kingbobomb(o, o2)
if o2.oFlags & OBJ_FLAG_HOLDABLE ~= 0 and o2.oAction ~= 8 then
o2.oVelY = 30
o2.oForwardVel = 30
o2.oMoveAngleYaw = o.oMoveAngleYaw
o2.oMoveFlags = 0
o2.oAction = 4
end
end
---@param o Object
---@param o2 Object
local function attack_wooden_post(o, o2)
o2.oWoodenPostMarioPounding = 1
o2.oWoodenPostSpeedY = -100.0
cur_obj_play_sound_2(SOUND_GENERAL_POUND_WOOD_POST)
end
-- lists for edge case interactions
bhvWapeachAxeAttacks = {
[id_bhvSmallBully] = attack_bully_strong,
[id_bhvBigBully] = attack_bully_strong,
[id_bhvBigBullyWithMinions] = attack_bully_strong,
[id_bhvSmallChillBully] = attack_bully_strong,
[id_bhvBigChillBully] = attack_bully_strong,
[id_bhvMrBlizzard] = attack_mrblizzard,
[id_bhvBulletBill] = attack_bullet_bill,
[id_bhvSmallWhomp] = attack_whomp,
[id_bhvChuckya] = attack_chuckya,
[id_bhvWoodenPost] = attack_wooden_post,
}
---@param o Object
---@param spAttacksList table<BehaviorId,function>
---@param getTarget? boolean
function obj_process_attacks(o, spAttacksList, getTarget)
-- players
if o.oInteractType == 0 then
local m = nearest_mario_state_to_object(o)
if m and m.playerIndex == 0 and m.marioObj.globalPlayerIndex ~= o.globalPlayerIndex
and m.action & (ACT_FLAG_INVULNERABLE | ACT_FLAG_INTANGIBLE) == 0 and m.invincTimer == 0
and obj_check_hitbox_overlap(m.marioObj, o) then
if spAttacksList[id_bhvMario] then
spAttacksList[id_bhvMario](o, m)
else
take_damage_and_knock_back(m, o)
end
if getTarget then return m.marioObj end
end
end
-- other objects
for i, list in ipairs(colObjLists) do
local o2 = obj_get_first(list)
while o2 do
if o ~= o2 and o2.oInteractStatus & INT_STATUS_INTERACTED == 0 and o2.oIntangibleTimer == 0 and obj_check_hitbox_overlap(o, o2) then
local bhv = get_id_from_behavior(o2.behavior)
if not bhvBlacklist[bhv] then
if spAttacksList[bhv] then
spAttacksList[bhv](o, o2)
else
o2.oInteractStatus = o2.oInteractStatus | ATTACK_FAST_ATTACK | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED
end
if getTarget then return o2 end
end
end
o2 = obj_get_next(o2)
end
end
end

View file

@ -0,0 +1,52 @@
--- Vars that all movesets use --
--- @type CharacterState[]
gCharacterStates = {}
for i = 0, (MAX_PLAYERS - 1) do
gCharacterStates[i] = {}
local m = gMarioStates[i]
local e = gCharacterStates[i]
e.mario = m
e.luigi = {}
e.toad = {}
e.wario = {}
e.waluigi = {}
e.toadette = {}
e.peach = {}
e.daisy = {}
e.yoshi = {}
e.birdo = {}
e.spike = {}
e.pauline = {}
e.rosalina = {}
e.wapeach = {}
e.donkeyKong = {}
e.sonic = {}
e.toadette.averageForwardVel = 0
e.birdo.spitTimer = 0
e.birdo.framesSinceShoot = 255
e.birdo.flameCharge = 0
e.rosalina.canSpin = true
e.rosalina.orbitObjActive = false
e.rosalina.orbitObjDist = 0
e.rosalina.orbitObjAngle = 0
e.sonic.spinCharge = 0
e.sonic.groundYVel = 0
e.sonic.prevForwardVel = 0
e.sonic.peakHeight = 0
e.sonic.actionADone = false
e.sonic.actionBDone = false
e.sonic.bounced = false
e.sonic.spindashState = 0
e.sonic.instashieldTimer = 0
e.sonic.oxygen = 900 -- 30 seconds
e.sonic.prevVelY = 0
e.sonic.prevHeight = 0
e.sonic.physTimer = 0
e.sonic.lastforwardPos = gVec3fZero()
e.sonic.realFVel = 0
end

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,843 @@
-------------------
-- Birdo Moveset --
-------------------
if not charSelect then return end
local SOUND_SPIT = audio_sample_load("z_sfx_birdo_spit.ogg") -- Load audio sample
---------------
-- Birdo Egg --
---------------
_G.ACT_BIRDO_HOLD_WALKING = allocate_mario_action(ACT_FLAG_MOVING | ACT_GROUP_OBJECT)
_G.ACT_SPIT_EGG = allocate_mario_action(ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT)
_G.ACT_SPIT_EGG_WALK = allocate_mario_action(ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON)
_G.ACT_SPIT_EGG_AIR = allocate_mario_action(ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT)
--- @param m MarioState
local function act_birdo_hold_walking(m)
local startYaw = m.faceAngle.y
if m.heldObj and m.heldObj.behavior == get_behavior_from_id(id_bhvJumpingBox) then
return set_mario_action(m, ACT_CRAZY_BOX_BOUNCE, 0)
end
if (m.marioObj.oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) ~= 0 then
return drop_and_set_mario_action(m, ACT_WALKING, 0)
end
if (should_begin_sliding(m)) ~= 0 then
return set_mario_action(m, ACT_HOLD_BEGIN_SLIDING, 0)
end
if (m.input & INPUT_B_PRESSED) ~= 0 then
return set_mario_action(m, ACT_THROWING, 0)
end
if (m.input & INPUT_A_PRESSED) ~= 0 then
return set_jumping_action(m, ACT_HOLD_JUMP, 0)
end
if (m.input & INPUT_ZERO_MOVEMENT) ~= 0 then
return set_mario_action(m, ACT_HOLD_DECELERATING, 0)
end
if (m.input & INPUT_Z_PRESSED) ~= 0 then
return drop_and_set_mario_action(m, ACT_CROUCH_SLIDE, 0)
end
update_walking_speed(m) -- normal walking speed
local result = perform_ground_step(m)
if result == GROUND_STEP_LEFT_GROUND then
set_mario_action(m, ACT_HOLD_FREEFALL, 0)
elseif result == GROUND_STEP_HIT_WALL then
if (m.forwardVel > 16) then
mario_set_forward_vel(m, 16)
end
end
-- for the animation, temporarily read birdo's speed as lower so it looks less goofy
local prevForwardVel = m.forwardVel
local prevMag = m.intendedMag
m.forwardVel = m.forwardVel * 0.6
m.intendedMag = m.intendedMag * 0.6
anim_and_audio_for_hold_walk(m)
m.forwardVel = prevForwardVel
m.intendedMag = prevMag
-- tilt body
local dYaw = m.faceAngle.y - startYaw
local val02 = -(dYaw * m.forwardVel / 12)
local val00 = (m.forwardVel * 170)
val02 = math.clamp(val02, -0x1555, 0x1555)
val00 = math.clamp(val00, 0x0, 0x1555)
m.marioBodyState.allowPartRotation = true
m.marioBodyState.torsoAngle.z = approach_s32(m.marioBodyState.torsoAngle.z, val02, 0x400, 0x400)
m.marioBodyState.torsoAngle.x = approach_s32(m.marioBodyState.torsoAngle.x, val00, 0x400, 0x400)
if (0.4 * m.intendedMag - m.forwardVel > 10) then
set_mario_particle_flags(m, PARTICLE_DUST, 0)
end
return 0
end
--- @param m MarioState
local function act_spit_egg(m)
local e = gCharacterStates[m.playerIndex]
if (m.quicksandDepth > 30) then
return set_mario_action(m, ACT_IN_QUICKSAND, 0)
end
if m.actionState == 0 then
play_custom_anim(m, "BIRDO_ANIM_IDLE_TO_AIM_IDLE")
if is_anim_past_end(m) ~= 0 then
m.actionState = 1
end
elseif e.birdo.flameCharge == 0 and e.birdo.framesSinceShoot > 10 then
play_custom_anim(m, "BIRDO_ANIM_AIM_IDLE_TO_IDLE")
if is_anim_past_end(m) ~= 0 then
return set_mario_action(m, ACT_IDLE, 0)
end
else
play_custom_anim(m, "BIRDO_ANIM_AIM_IDLE")
end
mario_drop_held_object(m)
m.actionTimer = m.actionTimer + 1
local oldActTimer = m.actionTimer
if (m.input & INPUT_NONZERO_ANALOG) ~= 0 then
mario_set_forward_vel(m, 0)
local result = set_mario_action(m, ACT_SPIT_EGG_WALK, 0)
m.actionTimer = oldActTimer
return result
elseif (check_common_idle_cancels(m) ~= 0) then
if m.action & ACT_FLAG_AIR ~= 0 then
mario_set_forward_vel(m, 0)
set_mario_action(m, ACT_SPIT_EGG_AIR, 1)
if m.vel.y <= 0 then
m.actionArg = 0
end
m.actionTimer = oldActTimer
end
return 1
end
mario_set_forward_vel(m, 0)
perform_ground_step(m)
return 0
end
--- @param m MarioState
local function act_spit_egg_walk(m)
local e = gCharacterStates[m.playerIndex]
local mBody = m.marioBodyState
mario_drop_held_object(m)
m.actionTimer = m.actionTimer + 1
if e.birdo.flameCharge == 0 and e.birdo.framesSinceShoot > 10 then
if m.forwardVel < 0 then
m.forwardVel = m.intendedMag
m.faceAngle.y = m.intendedYaw
return set_mario_action(m, ACT_FINISH_TURNING_AROUND, 0)
end
m.forwardVel = m.intendedMag
m.faceAngle.y = m.intendedYaw
return set_mario_action(m, ACT_WALKING, 0)
end
if mario_floor_is_slippery(m) ~= 0 then
return set_mario_action(m, ACT_WALKING, 0)
end
if (should_begin_sliding(m)) ~= 0 then
return set_mario_action(m, ACT_BEGIN_SLIDING, 0)
end
if (m.input & INPUT_FIRST_PERSON) ~= 0 then
m.intendedMag = 0
if m.slideVelX == 0 and m.slideVelZ == 0 then
return begin_braking_action(m)
end
end
if (m.input & INPUT_ZERO_MOVEMENT) ~= 0 and m.slideVelX == 0 and m.slideVelZ == 0 then
local oldActTimer = m.actionTimer
local result = set_mario_action(m, ACT_SPIT_EGG, 0)
m.actionTimer = oldActTimer
return result
end
if (m.input & INPUT_Z_PRESSED) ~= 0 then
return set_mario_action(m, ACT_CROUCH_SLIDE, 0)
end
-- strafe movement
local newVelX = sins(m.intendedYaw) * m.intendedMag
local newVelZ = coss(m.intendedYaw) * m.intendedMag
m.slideVelX = approach_f32(m.slideVelX, newVelX, 4, 4)
m.slideVelZ = approach_f32(m.slideVelZ, newVelZ, 4, 4)
m.vel.x, m.vel.z = m.slideVelX, m.slideVelZ
m.forwardVel = math.sqrt(m.vel.x ^ 2 + m.vel.z ^ 2)
if (m.input & INPUT_A_PRESSED) ~= 0 then
set_mario_y_vel_based_on_fspeed(m, 42, 0.25)
m.slideVelX = m.slideVelX * 0.8
m.slideVelZ = m.slideVelZ * 0.8
m.vel.x, m.vel.z = m.slideVelX, m.slideVelZ
m.forwardVel = m.forwardVel * 0.8
local oldActTimer = m.actionTimer
local result = set_mario_action(m, ACT_SPIT_EGG_AIR, 1)
m.actionTimer = oldActTimer
return result
end
local result = (perform_ground_step(m))
if result == GROUND_STEP_LEFT_GROUND then
m.vel.y = 0
local oldActTimer = m.actionTimer
set_mario_action(m, ACT_SPIT_EGG_AIR, 0)
m.actionTimer = oldActTimer
--set_character_animation(m, CHAR_ANIM_GENERAL_FALL)
elseif result == GROUND_STEP_NONE then
--anim_and_audio_for_walk(m)
play_step_sound(m, 10, 49)
local dYaw = math.s16(m.faceAngle.y - m.intendedYaw)
play_custom_anim(m, "BIRDO_ANIM_AIM_WALK", m.forwardVel / 4 * 0x10000)
mBody.allowPartRotation = true
m.marioObj.header.gfx.angle.y = m.intendedYaw
local marioAnimInfo = m.marioObj.header.gfx.animInfo
if math.abs(dYaw) > 0x4000 then
m.marioObj.header.gfx.angle.y = m.intendedYaw - 0x8000
marioAnimInfo.animAccel = -math.abs(marioAnimInfo.animAccel)
else
marioAnimInfo.animAccel = math.abs(marioAnimInfo.animAccel)
end
-- Handle manually the loop points of the animation if moving backwards
if marioAnimInfo.animAccel < 0 and marioAnimInfo.animFrame <= marioAnimInfo.curAnim.loopStart then
marioAnimInfo.animFrame = marioAnimInfo.curAnim.loopEnd
marioAnimInfo.animFrameAccelAssist = marioAnimInfo.animFrame << 16
end
mBody.torsoAngle.y = math.s16(m.faceAngle.y - m.marioObj.header.gfx.angle.y) * 0.4
mBody.headAngle.y = m.faceAngle.y - m.marioObj.header.gfx.angle.y - mBody.torsoAngle.y
if m.intendedMag - m.forwardVel > 16 then
set_mario_particle_flags(m, PARTICLE_DUST, 0)
end
end
check_ledge_climb_down(m)
return 0
end
---@param m MarioState
local function act_spit_egg_air(m)
local e = gCharacterStates[m.playerIndex]
play_custom_anim(m, "BIRDO_ANIM_AIM_JUMP")
if m.actionArg ~= 1 then
set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.curAnim.loopEnd)
else
play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0)
end
m.actionTimer = m.actionTimer + 1
if (m.input & INPUT_Z_PRESSED) ~= 0 then
return set_mario_action(m, ACT_GROUND_POUND, 0)
end
-- air strafe
local newVelX = sins(m.intendedYaw) * m.intendedMag
local newVelZ = coss(m.intendedYaw) * m.intendedMag
m.slideVelX = approach_f32(m.slideVelX, newVelX, 1, 1)
m.slideVelZ = approach_f32(m.slideVelZ, newVelZ, 1, 1)
m.vel.x, m.vel.z = m.slideVelX, m.slideVelZ
m.forwardVel = m.slideVelX * sins(m.faceAngle.y) + m.slideVelZ * coss(m.faceAngle.y)
--local absSpeed = math.max(math.abs(m.slideVelX), math.abs(m.slideVelZ))
local result = (perform_air_step(m, 0))
if result == AIR_STEP_LANDED then
if check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB) ~= 0 then
return 1
elseif e.birdo.flameCharge == 0 and e.birdo.framesSinceShoot > 10 then
set_mario_action(m, ACT_FREEFALL_LAND, 0)
else
local oldActTimer = m.actionTimer
set_mario_action(m, ACT_SPIT_EGG_WALK, 0)
m.actionTimer = oldActTimer
end
return 1
elseif result == AIR_STEP_HIT_WALL then
mario_set_forward_vel(m, 0)
elseif result == AIR_STEP_HIT_LAVA_WALL then
lava_boost_on_wall(m)
end
return 0
end
-- Egg
local eggIntObjLists = {
OBJ_LIST_GENACTOR,
OBJ_LIST_PUSHABLE,
OBJ_LIST_SURFACE,
OBJ_LIST_PLAYER,
}
E_MODEL_EGG = smlua_model_util_get_id("egg_geo")
---@param o Object
function bhv_birdo_egg_init(o)
o.oFlags = (OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE | OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW | OBJ_FLAG_HOLDABLE | OBJ_FLAG_COMPUTE_DIST_TO_MARIO)
o.oFaceAngleRoll = 0
o.oMoveAngleRoll = 0
o.oGravity = 0
o.oBounciness = 0
o.oFriction = 1
o.oDragStrength = 0
o.oBuoyancy = 0
o.oWallHitboxRadius = 60
o.oVelY = 0
o.collisionData = smlua_collision_util_get("egg_collision")
local hitbox = get_temp_object_hitbox()
hitbox.interactType = INTERACT_DAMAGE
hitbox.hurtboxRadius = 60
hitbox.hurtboxHeight = 80
hitbox.downOffset = 80
hitbox.radius = 60
hitbox.height = 80
hitbox.damageOrCoinValue = 1
if o.oBehParams ~= 0 then
-- similar hitbox to fire spitter flames
hitbox.interactType = INTERACT_FLAME
hitbox.radius = 10
hitbox.height = 40
hitbox.hurtboxRadius = 10
hitbox.hurtboxHeight = 40
hitbox.downOffset = 30
obj_set_billboard(o)
o.header.gfx.scale.x = 3
o.header.gfx.scale.y = 3
o.header.gfx.scale.z = 3
end
obj_set_hitbox(o, hitbox)
o.oIntangibleTimer = 10
-- do manual shadow, otherwise the shadow renders on top of itself
o.header.gfx.disableAutomaticShadowPos = true
o.header.gfx.shadowPos.x = o.oPosX
o.header.gfx.shadowPos.y = o.oPosY - 50
o.header.gfx.shadowPos.z = o.oPosZ
network_init_object(o, true, { 'globalPlayerIndex' })
end
---@param o Object
function bhv_birdo_egg_loop(o)
if o.oBehParams ~= 0 then
o.oAnimState = o.oAnimState + 1
end
if o.oHeldState == HELD_FREE then
cur_obj_enable_rendering()
if o.oAction == 0 then
o.oGravity = 0
else
o.oMoveAnglePitch = 0
o.oFaceAnglePitch = 0
o.oGravity = -2
end
cur_obj_update_floor_and_walls()
local oldForwardVel = o.oForwardVel
if o.oAction == 0 then
obj_compute_vel_from_move_pitch(o.oForwardVel)
end
cur_obj_move_standard(60)
o.oForwardVel = oldForwardVel
local defaultVel = 20
if o.oBehParams ~= 0 then
defaultVel = 40
end
if o.oAction == 0 and o.oForwardVel > defaultVel then
o.oForwardVel = approach_f32(o.oForwardVel, defaultVel, 3, 3)
end
-- manual object collision
local dieFromCollision = false
o.numCollidedObjs = obj_attack_collided_from_other_object(o)
if o.numCollidedObjs ~= 0 and o.oBehParams == 0 then
dieFromCollision = true
end
if o.oDistanceToMario < 2000 then
for _, list in ipairs(eggIntObjLists) do
local o2 = obj_get_first(list)
while o2 and o.numCollidedObjs < 4 do
if o ~= o2 then
if list ~= OBJ_LIST_PLAYER and o2.oHeldState == HELD_FREE and detect_object_hitbox_overlap(o, o2) ~= 0 then
o2.numCollidedObjs = o2.numCollidedObjs - 1 -- prevent game crash
local doEggInteract = birdo_egg_interaction(o2, o)
if o.oBehParams == 0 or doEggInteract then
dieFromCollision = true
end
if doEggInteract or o2.oInteractType == INTERACT_BREAKABLE or obj_is_attackable(o2) then
if obj_has_behavior_id(o2, id_bhvBowser) == 0 then
o2.oInteractStatus = o2.oInteractStatus | ATTACK_PUNCH | INT_STATUS_WAS_ATTACKED |
INT_STATUS_INTERACTED | INT_STATUS_TOUCHED_BOB_OMB
end
end
elseif o.oBehParams ~= 0 and birdo_fire_is_targettable(o2, o) and dist_between_objects(o2, o) <= 700 then
local angleToObject = obj_angle_to_object(o, o2)
if abs_angle_diff(o.oMoveAngleYaw, angleToObject) <= 0x4000 then
cur_obj_rotate_yaw_toward(angleToObject, 0x200)
end
end
end
o2 = obj_get_next(o2)
end
if o.numCollidedObjs >= 4 then break end
end
end
-- surface collision
if o.oAction == 0 and o.oBehParams == 0 and o.oMoveFlags & OBJ_MOVE_MASK_IN_WATER == 0 then
local m0 = gMarioStates[0]
load_object_collision_model()
if cur_obj_is_mario_on_platform() ~= 0 then
if (m0.action == ACT_PUNCHING or m0.action == ACT_MOVE_PUNCHING) then
-- pick up egg
m0.heldObj = o
m0.marioBodyState.grabPos = GRAB_POS_LIGHT_OBJ
o.heldByPlayerIndex = 0
o.oHeldState = HELD_HELD
set_mario_action(m0, ACT_HOLD_FREEFALL, 0)
if (o.oSyncID ~= 0) then network_send_object(o, false) end
elseif (m0.prevAction & ACT_FLAG_AIR) ~= 0 then -- prevent falling off of egg easily
m0.pos.x = o.oPosX
m0.pos.z = o.oPosZ
end
end
end
if dieFromCollision or o.oMoveFlags & (OBJ_MOVE_HIT_WALL | OBJ_MOVE_UNDERWATER_ON_GROUND | OBJ_MOVE_MASK_ON_GROUND) ~= 0 or o.oTimer > 120 then
o.numCollidedObjs = 0
spawn_mist_particles()
obj_mark_for_deletion(o)
end
o.oInteractStatus = 0
o.numCollidedObjs = 0
elseif o.oHeldState == HELD_HELD then
o.oFaceAnglePitch = 0
o.oMoveAnglePitch = 0
o.oInteractType = INTERACT_GRABBABLE
cur_obj_disable_rendering_and_become_intangible(o)
elseif o.oHeldState == HELD_THROWN then
o.oFaceAnglePitch = 0
o.oMoveAnglePitch = 0
o.oInteractType = INTERACT_DAMAGE
cur_obj_enable_rendering_and_become_tangible(o)
cur_obj_change_action(1)
local m = gMarioStates[o.heldByPlayerIndex]
o.oForwardVel = math.max(m.forwardVel + 15, 40)
o.oVelY = 10
o.oTimer = 0
o.oHeldState = HELD_FREE
o.oIntangibleTimer = 10
elseif o.oHeldState == HELD_DROPPED then
spawn_mist_particles()
obj_mark_for_deletion(o)
end
-- do manual shadow, otherwise the shadow renders on top of itself
if o.activeFlags ~= ACTIVE_FLAG_DEACTIVATED then
o.header.gfx.disableAutomaticShadowPos = true
o.header.gfx.shadowPos.x = o.oPosX
o.header.gfx.shadowPos.y = o.oPosY - 50
o.header.gfx.shadowPos.z = o.oPosZ
else
o.header.gfx.disableAutomaticShadowPos = false
end
end
-- lua recreation
---@param a Object
---@param b Object
function detect_object_hitbox_overlap(a, b)
if not (a and b) then return 0 end
local sp3C = a.oPosY - a.hitboxDownOffset
local sp38 = b.oPosY - b.hitboxDownOffset
local dx = a.oPosX - b.oPosX
local dz = a.oPosZ - b.oPosZ
local collisionRadius = a.hitboxRadius + b.hitboxRadius
local distance = math.floor(math.sqrt(dx * dx + dz * dz))
-- do not check for player interactions here
if ((a.oInteractType & INTERACT_PLAYER) ~= 0 and (b.oInteractType & INTERACT_PLAYER) ~= 0) then
return 0
end
if (collisionRadius > distance) then
local sp20 = a.hitboxHeight + sp3C
local sp1C = b.hitboxHeight + sp38
if (sp3C > sp1C) then
return 0
end
if (sp20 < sp38) then
return 0
end
if (a.numCollidedObjs >= 4) then
return 0
end
if (b.numCollidedObjs >= 4) then
return 0
end
-- can't reference these fields in lua
--a.collidedObjs[a.numCollidedObjs] = b
--b.collidedObjs[b.numCollidedObjs] = a
a.collidedObjInteractTypes = a.collidedObjInteractTypes | b.oInteractType
b.collidedObjInteractTypes = b.collidedObjInteractTypes | a.oInteractType
a.numCollidedObjs = a.numCollidedObjs + 1
b.numCollidedObjs = b.numCollidedObjs + 1
return 1
end
return 0
end
id_bhvBirdoEgg = hook_behavior(nil, OBJ_LIST_SURFACE, true, bhv_birdo_egg_init, bhv_birdo_egg_loop, "bhvBirdoEgg")
---@param m MarioState
function birdo_update(m)
-- spit egg
local e = gCharacterStates[m.playerIndex]
local inSpitAction = (m.action == ACT_SPIT_EGG or m.action == ACT_SPIT_EGG_WALK or m.action == ACT_SPIT_EGG_AIR or m.action == ACT_FIRST_PERSON or m.action == ACT_WATER_PUNCH or m.action == ACT_FLYING)
local headRot = m.marioBodyState.headAngle
if m.controller.buttonPressed & B_BUTTON ~= 0 and inSpitAction then
-- when mashing B, stay in spit action
e.birdo.framesSinceShoot = 0
if e.birdo.spitTimer == 0 then
e.birdo.flameCharge = 0
end
else
-- handle shooting repeatedly/charging
if e.birdo.framesSinceShoot ~= 255 then
e.birdo.framesSinceShoot = e.birdo.framesSinceShoot + 1
end
if m.controller.buttonDown & B_BUTTON ~= 0 then
if inSpitAction then
e.birdo.flameCharge = e.birdo.flameCharge + 1
end
elseif e.birdo.spitTimer < 25 then
if e.birdo.flameCharge >= 30 then
e.birdo.framesSinceShoot = 0 -- shoot fireball
else
e.birdo.flameCharge = 0
end
end
end
if (e.birdo.framesSinceShoot <= 10 or e.birdo.flameCharge ~= 0) and not m.heldObj and inSpitAction then
local canShoot = true
local eggCount = 0
local gIndex = network_global_index_from_local(m.playerIndex)
local egg = obj_get_first_with_behavior_id(id_bhvBirdoEgg)
while egg do
if egg.oAction == 0 and egg.oHeldState == HELD_FREE and egg.globalPlayerIndex == gIndex then
eggCount = eggCount + 1
if eggCount >= 3 then -- max of 3 eggs/fireballs per player
canShoot = false
break
end
end
egg = obj_get_next_with_same_behavior_id(egg)
end
if e.birdo.spitTimer ~= 0 then
e.birdo.spitTimer = e.birdo.spitTimer - 1
m.marioBodyState.allowPartRotation = true
if e.birdo.spitTimer > 24 then
headRot.x = approach_f32(headRot.x, degrees_to_sm64(-30), degrees_to_sm64(10), degrees_to_sm64(10))
else
headRot.x = approach_f32(headRot.x, degrees_to_sm64(0), degrees_to_sm64(3.5), degrees_to_sm64(3.5))
end
end
if e.birdo.spitTimer == 0 and canShoot and e.birdo.framesSinceShoot <= 10 then
m.actionTimer = 0
m.actionArg = 0
end
local mouthPos = gVec3fZero()
local yaw = m.faceAngle.y
local pitch = 0
if canShoot then
-- when swimming, flying, or in first person, allow shooting in any direction
if m.action == ACT_FIRST_PERSON then
yaw = m.statusForCamera.headRotation.y + yaw
pitch = m.statusForCamera.headRotation.x
mouthPos.x = m.pos.x + sins(yaw) * 60 * coss(pitch)
mouthPos.y = m.pos.y + 120 - sins(pitch) * 120
mouthPos.z = m.pos.z + coss(yaw) * 60 * coss(pitch)
elseif m.action & ACT_FLAG_SWIMMING_OR_FLYING ~= 0 then
pitch = -m.faceAngle.x
if pitch < 0 then
mouthPos.x = m.pos.x + sins(yaw) * 80 * coss(pitch)
mouthPos.y = m.pos.y + 120
mouthPos.z = m.pos.z + coss(yaw) * 80 * coss(pitch)
else
mouthPos.x = m.pos.x + sins(yaw) * 80
mouthPos.y = m.pos.y + 120 - sins(pitch) * 150
mouthPos.z = m.pos.z + coss(yaw) * 80
end
else
mouthPos.x = m.marioBodyState.headPos.x + sins(yaw + m.marioBodyState.headAngle.y) * 60
mouthPos.y = m.marioBodyState.headPos.y + 20
mouthPos.z = m.marioBodyState.headPos.z + coss(yaw + m.marioBodyState.headAngle.y) * 60
end
end
if canShoot and e.birdo.spitTimer == 0 and e.birdo.flameCharge >= 30 and m.action & ACT_FLAG_SWIMMING == 0 then
spawn_non_sync_object(id_bhvKoopaShellFlame, E_MODEL_RED_FLAME,
mouthPos.x,
mouthPos.y,
mouthPos.z,
function(o)
o.oKoopaShellFlameUnkF8 = 2
o.oMoveAngleYaw = math.random(0, 0xFFFF)
o.oVelY = math.random(10)
o.oAnimState = math.random(10)
o.oGravity = -4.0
o.oTimer = 1
o.oForwardVel = math.random(10)
end)
play_sound(SOUND_AIR_BLOW_FIRE, m.marioObj.header.gfx.cameraToObject)
end
if canShoot and e.birdo.spitTimer == 0 and e.birdo.framesSinceShoot <= 10 then
e.birdo.spitTimer = 30
elseif e.birdo.spitTimer == 25 then
local model = E_MODEL_EGG
local isFireball = (e.birdo.flameCharge >= 30)
if isFireball then
model = E_MODEL_RED_FLAME
e.birdo.flameCharge = 0
end
if not isFireball then
audio_sample_play(SOUND_SPIT, m.pos, 1) -- Play audio sample
else
play_sound(SOUND_AIR_BOWSER_SPIT_FIRE, m.marioObj.header.gfx.cameraToObject)
end
if m.playerIndex == 0 then
local eggVel = m.forwardVel * 2 + 25
-- add double floor velocity to prevent being able to platform on eggs forever
if m.floor and m.floor.object and m.floor.object.oForwardVel ~= 0 then
eggVel = eggVel + m.floor.object.oForwardVel * 2
end
spawn_sync_object(id_bhvBirdoEgg, model, mouthPos.x + sins(yaw) * 40 * coss(pitch), mouthPos.y,
mouthPos.z + coss(yaw) * 40 * coss(pitch), function(o)
o.oForwardVel = math.max(eggVel, 40)
o.oMoveAngleYaw = yaw
o.oFaceAnglePitch = pitch
o.oMoveAnglePitch = pitch
o.oIntangibleTimer = 100
o.globalPlayerIndex = gIndex
o.oBehParams = (isFireball and 1) or 0
spawn_mist_particles_variable(20, 120, 5)
end)
end
end
elseif e.birdo.spitTimer ~= 0 then
e.birdo.spitTimer = e.birdo.spitTimer - 1
m.marioBodyState.allowPartRotation = true
if e.birdo.spitTimer > 24 then
headRot.x = approach_f32(headRot.x, degrees_to_sm64(-30), degrees_to_sm64(10), degrees_to_sm64(10))
else
headRot.x = approach_f32(headRot.x, degrees_to_sm64(0), degrees_to_sm64(3.5), degrees_to_sm64(3.5))
end
end
-- throw objects instantly
if m.action == ACT_THROWING then
if m.actionTimer < 6 then
m.actionTimer = 6
set_anim_to_frame(m, 6)
end
elseif m.action == ACT_AIR_THROW or m.action == ACT_AIR_THROW_LAND then
if m.actionTimer < 3 then
m.actionTimer = 3
set_anim_to_frame(m, 3)
end
end
end
function birdo_on_set_action(m)
if m.action ~= ACT_SPIT_EGG and m.action ~= ACT_SPIT_EGG_WALK and m.action ~= ACT_SPIT_EGG_AIR then
gCharacterStates[m.playerIndex].birdo.spitTimer = 0
end
if m.action == ACT_HOLD_WALKING then -- switch to custom hold action
set_mario_action(m, ACT_BIRDO_HOLD_WALKING, 0)
end
end
local shootActs = {
[ACT_PUNCHING] = ACT_SPIT_EGG,
[ACT_MOVE_PUNCHING] = ACT_SPIT_EGG_WALK,
[ACT_JUMP_KICK] = ACT_SPIT_EGG_AIR,
}
function birdo_before_action(m, action, actionArg)
if m.playerIndex ~= 0 then return end
if shootActs[action] and m.controller.buttonDown & A_BUTTON == 0 then
if action == ACT_PUNCHING and actionArg == 9 then return end
local e = gCharacterStates[m.playerIndex]
e.birdo.framesSinceShoot = 0
if e.birdo.spitTimer == 0 then
e.birdo.flameCharge = 0
end
local canShoot = true
local eggCount = 0
local gIndex = network_global_index_from_local(m.playerIndex)
local egg = obj_get_first_with_behavior_id(id_bhvBirdoEgg)
while egg do
if egg.oAction == 0 and egg.oHeldState == HELD_FREE and egg.globalPlayerIndex == gIndex then
eggCount = eggCount + 1
if eggCount >= 3 then -- max of 3 eggs/fireballs per player
canShoot = false
break
end
end
egg = obj_get_next_with_same_behavior_id(egg)
end
if m.action ~= ACT_SPIT_EGG or e.birdo.spitTimer == 0 or canShoot then
m.marioObj.header.gfx.animInfo.animFrame = 0
return shootActs[action]
end
end
end
function birdo_on_interact(m, o, intType)
local e = gCharacterStates[m.playerIndex]
if intType == INTERACT_GRABBABLE and e.birdo.framesSinceShoot == 0 and e.birdo.flameCharge == 0 and (m.action == ACT_SPIT_EGG or m.action == ACT_SPIT_EGG_WALK) and o.oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE == 0 then
set_mario_action(m, ACT_MOVE_PUNCHING, 1)
return
end
end
function birdo_before_phys_step(m)
local hScale = 1.0
local vScale = 1.0
-- faster ground movement and slower, floaty air movement
if (m.action & ACT_FLAG_MOVING) ~= 0 and m.action ~= ACT_BUBBLED then
hScale = hScale * 1.12 -- not as fast as toad
elseif m.action & ACT_FLAG_AIR ~= 0 then
hScale = hScale * 0.94
if m.vel.y < 0 then
vScale = vScale * 0.98
end
end
m.vel.x = m.vel.x * hScale
m.vel.y = m.vel.y * vScale
m.vel.z = m.vel.z * hScale
end
-- allow shooting in first person
function birdo_before_update(m)
if m.action == ACT_FIRST_PERSON and m.controller.buttonPressed & B_BUTTON ~= 0 then
local e = gCharacterStates[m.playerIndex]
e.birdo.framesSinceShoot = 0
if e.birdo.spitTimer == 0 then
e.birdo.flameCharge = 0
end
m.controller.buttonPressed = m.controller.buttonPressed & ~B_BUTTON
end
end
-- interactions for birdo's egg/fireball
function birdo_egg_interaction(o, egg)
if egg.oBehParams ~= 0 and obj_has_behavior_id(o, id_bhvMrBlizzard) ~= 0 then
o.oFaceAngleRoll = 0x3000
o.oMrBlizzardHeldObj = nil
o.prevObj = o.oMrBlizzardHeldObj
o.oAction = MR_BLIZZARD_ACT_DEATH
o.oMrBlizzardDizziness = 0
o.oMrBlizzardChangeInDizziness = 0
o.oTimer = 30
return true
end
if egg.oBehParams ~= 0 and obj_has_behavior_id(o, id_bhvBowser) ~= 0 then
if o.oAction ~= 4 and o.oAction ~= 5 and o.oAction ~= 6 and o.oAction ~= 12 and o.oAction ~= 19 and o.oAction ~= 20 and math.abs(o.oVelY) <= 2 then
o.oAction = 1
end
return true
end
if o.oInteractType == INTERACT_BULLY then
o.oBullyLastNetworkPlayerIndex = egg.globalPlayerIndex
o.oForwardVel = (egg.oBehParams ~= 0 and 50) or 25
o.oMoveAngleYaw = egg.oMoveAngleYaw
return true
end
end
-- prevent player interaction with Birdo's egg if player interaction is not pvp (owner still interacts)
---@param m MarioState
---@param o Object
---@param type integer
function player_egg_allow_interact(m, o, type)
if obj_has_behavior_id(o, id_bhvBirdoEgg) ~= 0 then
local m2 = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if m.playerIndex ~= m2.playerIndex and gServerSettings.playerInteractions ~= PLAYER_INTERACTIONS_PVP then
return false
end
end
end
hook_event(HOOK_ALLOW_INTERACT, player_egg_allow_interact)
-- returns true if this object can be hit by birdo's fireball
function birdo_fire_is_targettable(o, egg)
if o.oInteractType == INTERACT_PLAYER then
local m = gMarioStates[o.oBehParams - 1]
if (not m) or is_player_active(m) == 0 then return false end
local gIndex = network_global_index_from_local(m.playerIndex)
return (gServerSettings.playerInteractions == PLAYER_INTERACTIONS_PVP) and (egg.globalPlayerIndex ~= gIndex)
end
return (obj_has_behavior_id(o, id_bhvMrBlizzard) ~= 0 or obj_has_behavior_id(o, id_bhvBowser) ~= 0
or o.oInteractType == INTERACT_BULLY or o.oInteractType == INTERACT_BREAKABLE or obj_is_attackable(o))
end
hook_mario_action(ACT_BIRDO_HOLD_WALKING, act_birdo_hold_walking)
hook_mario_action(ACT_SPIT_EGG, act_spit_egg)
hook_mario_action(ACT_SPIT_EGG_AIR, act_spit_egg_air)
hook_mario_action(ACT_SPIT_EGG_WALK, act_spit_egg_walk)
-- Fix object shadows getting messed up. Base coop bug
---@param o Object
function on_obj_load(o)
o.header.gfx.disableAutomaticShadowPos = false
o.header.gfx.shadowInvisible = false
end
hook_event(HOOK_ON_OBJECT_LOAD, on_obj_load)

View file

@ -0,0 +1,55 @@
-------------------
-- Daisy Moveset --
-------------------
if not charSelect then return end
local midairJumpActs = {
[ACT_JUMP] = true,
[ACT_DOUBLE_JUMP] = true,
[ACT_TRIPLE_JUMP] = true,
[ACT_LONG_JUMP] = true,
[ACT_BACKFLIP] = true,
[ACT_SIDE_FLIP] = true,
[ACT_WALL_KICK_AIR] = true,
}
_G.ACT_MIDAIR_JUMP = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_MOVING)
--- @param m MarioState
local function act_midair_jump(m)
-- apply movement when using action
common_air_action_step(m, ACT_JUMP_LAND, CHAR_ANIM_BEND_KNESS_RIDING_SHELL, AIR_STEP_NONE)
-- setup when action starts (vertical speed and voiceline)
if m.actionTimer == 0 then
m.vel.y = m.forwardVel * 0.3 + 40
m.forwardVel = m.forwardVel * 0.7
play_character_sound(m, CHAR_SOUND_HELLO)
end
set_mario_particle_flags(m, PARTICLE_LEAF, 0)
-- avoid issue with flying and then make the hover end after 2 secs or when stopping holding the button
if m.prevAction ~= ACT_TRIPLE_JUMP and (m.flags & MARIO_WING_CAP) ~= 0 then
if m.actionTimer >= 10 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
else
if m.actionTimer >= 10 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
end
-- increment the action timer to make the hover stop
m.actionTimer = m.actionTimer + 1
end
--- @param m MarioState
function daisy_update(m)
if (m.input & INPUT_A_PRESSED) ~= 0 and m.vel.y < 10 and m.prevAction ~= ACT_MIDAIR_JUMP and midairJumpActs[m.action] then
set_mario_action(m, ACT_MIDAIR_JUMP, 0)
end
end
hook_mario_action(ACT_MIDAIR_JUMP, act_midair_jump)

View file

@ -0,0 +1,955 @@
-------------------------
-- Donkey Kong Moveset --
-------------------------
if not charSelect then return end
local DONKEY_KONG_ROLL_SPEED = 60
local DONKEY_KONG_ROLL_DECAY_PERCENT = 0.98
local DONKEY_KONG_ROLL_DECAY_TIME = 10
local DONKEY_KONG_ROLL_STARTUP = 4
local DONKEY_KONG_ROLL_END = 25
local DONKEY_KONG_SLIP_TIME = 20
local DONKEY_KONG_SLIDE_TIME = 40
----------------
-- DK Gravity --
----------------
--- @param m MarioState
--- @param wall Surface
--- @param intendedPos Vec3f
--- @param nextPos Vec3f
--- @return integer
--- Checks ledge grab for donkey kong
local function donkey_kong_check_ledge_grab(m, wall, intendedPos, nextPos)
if not m then return 0 end
local ledgeFloor
local ledgePos = gVec3fZero()
local displacementX
local displacementZ
if m.vel.y > 0 then
return 0
end
displacementX = nextPos.x - intendedPos.x
displacementZ = nextPos.z - intendedPos.z
-- Only ledge grab if the wall displaced Mario in the opposite direction of
-- his velocity.
if displacementX * m.vel.x + displacementZ * m.vel.z > 0.0 then
return 0
end
--! Since the search for floors starts at y + m.marioObj.hitboxHeight (160.0f), we will sometimes grab
-- a higher ledge than expected (glitchy ledge grab)
ledgePos.x = nextPos.x - wall.normal.x * 60.0
ledgePos.z = nextPos.z - wall.normal.z * 60.0
ledgePos.y, ledgeFloor = find_floor(ledgePos.x, nextPos.y + m.marioObj.hitboxHeight, ledgePos.z)
if not ledgeFloor then return 0 end
if gLevelValues.fixCollisionBugs ~= 0 and gLevelValues.fixCollisionBugsFalseLedgeGrab ~= 0 then
-- fix false ledge grabs
if (not ledgeFloor or ledgeFloor.normal.y < 0.90630779) then
return 0
end
end
if ledgePos.y - nextPos.y <= 100.0 then
return 0
end
vec3f_copy(m.pos, ledgePos)
m.floor = ledgeFloor
m.floorHeight = ledgePos.y
m.floorAngle = atan2s(ledgeFloor.normal.z, ledgeFloor.normal.x)
m.faceAngle.x = 0
m.faceAngle.y = atan2s(wall.normal.z, wall.normal.x) + 0x8000
return 1
end
--- Turns a WallCollisionData object into a table
--- @param wcd WallCollisionData
--- @return table
local function wcd_to_table(wcd)
return {
x = wcd.x, -- number
y = wcd.y, -- number
z = wcd.z, -- number
offsetY = wcd.offsetY, -- number
radius = wcd.radius, -- number
unused = wcd.unused, -- integer
numWalls = wcd.numWalls, -- integer
walls = {
wcd.walls[1],
wcd.walls[2],
wcd.walls[3],
wcd.walls[4]
} , -- Surface[]
normalAddition = {
x = wcd.normalAddition.x,
y = wcd.normalAddition.y,
z = wcd.normalAddition.z,
}, -- Vec3f
normalCount = wcd.normalCount, -- integer
}
end
--- Fills a WallCollisionData object from a table
--- @param wcd WallCollisionData
--- @param t table
local function table_to_wcd(wcd, t)
wcd.x = t.x
wcd.y = t.y
wcd.z = t.z
wcd.offsetY = t.offsetY
wcd.radius = t.radius
wcd.unused = t.unused
wcd.numWalls = t.numWalls
wcd.walls[1] = t.walls[1]
wcd.walls[2] = t.walls[2]
wcd.walls[3] = t.walls[3]
wcd.walls[4] = t.walls[4]
wcd.normalAddition.x = t.normalAddition.x
wcd.normalAddition.y = t.normalAddition.y
wcd.normalAddition.z = t.normalAddition.z
wcd.normalCount = t.normalCount
end
--- @param m MarioState
--- @param intendedPos Vec3f
--- @param stepArg integer
--- @return integer
--- Performs an air quarter step for donkey kong
local function perform_donkey_kong_air_quarter_step(m, intendedPos, stepArg)
if not m then return 0 end
local wallDYaw
local nextPos = gVec3fZero()
local ceil
local floor
local ceilHeight
local floorHeight
local waterLevel
local tempWcd
vec3f_copy(nextPos, intendedPos)
-- Important note:
-- The WallCollisionData pointer is always the same, meaning it cannot be used for both upperWcd and lowerWcd
-- Fortunately, it's read-write, so we can turn it into a table for the Lua part of the function and
-- turn it back into a WallCollisionData object for the C function calls
tempWcd = collision_get_temp_wall_collision_data()
resolve_and_return_wall_collisions_data(nextPos, 150.0, 50.0, tempWcd)
local upperWcd = wcd_to_table(tempWcd)
tempWcd = collision_get_temp_wall_collision_data()
resolve_and_return_wall_collisions_data(nextPos, 30.0, 50.0, tempWcd)
local lowerWcd = wcd_to_table(tempWcd)
floorHeight, floor = find_floor(nextPos.x, nextPos.y, nextPos.z)
ceilHeight, ceil = vec3f_mario_ceil(nextPos, floorHeight)
waterLevel = find_water_level(nextPos.x, nextPos.z)
m.wall = nil
--! The water pseudo floor is not referenced when your intended qstep is
-- out of bounds, so it won't detect you as landing.
if not floor then
if nextPos.y <= m.floorHeight then
m.pos.y = m.floorHeight
return AIR_STEP_LANDED
end
m.pos.y = nextPos.y
if gServerSettings.bouncyLevelBounds ~= BOUNCY_LEVEL_BOUNDS_OFF then
m.faceAngle.y = m.faceAngle.y + 0x8000
mario_set_forward_vel(m, gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_ON_CAP and math.clamp(1.5 * m.forwardVel, -500, 500) or 1.5 * m.forwardVel)
end
return AIR_STEP_HIT_WALL
end
if (m.action & ACT_FLAG_RIDING_SHELL) ~= 0 and floorHeight < waterLevel then
local allowForceAction = TRIPLET_BUTTERFLY_ACT_ACTIVATE
if allowForceAction then
floorHeight = waterLevel
floor = get_water_surface_pseudo_floor()
floor.originOffset = floorHeight
end
end
--! This check uses f32, but findFloor uses short (overflow jumps)
if nextPos.y <= floorHeight then
if ceilHeight - floorHeight > m.marioObj.hitboxHeight then
m.pos.x = nextPos.x
m.pos.z = nextPos.z
m.floor = floor
m.floorHeight = floorHeight
end
--! When ceilHeight - floorHeight <= m->marioObj->hitboxHeight (160.0f), the step result says that
-- Mario landed, but his movement is cancelled and his referenced floor
-- isn't updated (pedro spots)
m.pos.y = floorHeight
return AIR_STEP_LANDED
end
if nextPos.y + m.marioObj.hitboxHeight > ceilHeight then
if m.vel.y >= 0.0 then
m.vel.y = 0.0
--! Uses referenced ceiling instead of ceil (ceiling hang upwarp)
if (stepArg and (stepArg & AIR_STEP_CHECK_HANG) ~= 0) and m.ceil and m.ceil.type == SURFACE_HANGABLE then
return AIR_STEP_GRABBED_CEILING
end
return AIR_STEP_NONE
end
if nextPos.y <= m.floorHeight then
m.pos.y = m.floorHeight
return AIR_STEP_LANDED
end
m.pos.y = nextPos.y
return AIR_STEP_HIT_WALL
end
--! When the wall is not completely vertical or there is a slight wall
-- misalignment, you can activate these conditions in unexpected situations
if (stepArg and (stepArg & AIR_STEP_CHECK_LEDGE_GRAB) ~= 0) and upperWcd.numWalls == 0 and lowerWcd.numWalls > 0 then
for i = 1, lowerWcd.numWalls do
if gLevelValues.fixCollisionBugs == 0 then
i = lowerWcd.numWalls
end
local wall = lowerWcd.walls[i]
if donkey_kong_check_ledge_grab(m, wall, intendedPos, nextPos) ~= 0 then
return AIR_STEP_GRABBED_LEDGE
end
end
vec3f_copy(m.pos, nextPos)
m.floor = floor
m.floorHeight = floorHeight
return AIR_STEP_NONE
end
vec3f_copy(m.pos, nextPos)
m.floor = floor
m.floorHeight = floorHeight
if upperWcd.numWalls > 0 then
table_to_wcd(tempWcd, upperWcd)
mario_update_wall(m, tempWcd)
upperWcd = wcd_to_table(tempWcd)
for i = 1, upperWcd.numWalls do
if gLevelValues.fixCollisionBugs == 0 then
i = upperWcd.numWalls
end
local wall = upperWcd.walls[i]
wallDYaw = atan2s(wall.normal.z, wall.normal.x) - m.faceAngle.y
if wall.type == SURFACE_BURNING then
m.wall = wall
return AIR_STEP_HIT_LAVA_WALL
end
if wallDYaw < -0x6000 or wallDYaw > 0x6000 then
m.wall = wall
m.flags = m.flags | MARIO_UNKNOWN_30
return AIR_STEP_HIT_WALL
end
end
elseif lowerWcd.numWalls > 0 then
table_to_wcd(tempWcd, lowerWcd)
mario_update_wall(m, tempWcd)
lowerWcd = wcd_to_table(tempWcd)
for i = 1, lowerWcd.numWalls do
if gLevelValues.fixCollisionBugs == 0 then
i = lowerWcd.numWalls
end
local wall = lowerWcd.walls[i]
wallDYaw = atan2s(wall.normal.z, wall.normal.x) - m.faceAngle.y
if wall.type == SURFACE_BURNING then
m.wall = wall
return AIR_STEP_HIT_LAVA_WALL
end
if wallDYaw < -0x6000 or wallDYaw > 0x6000 then
m.wall = wall
m.flags = m.flags | MARIO_UNKNOWN_30
return AIR_STEP_HIT_WALL
end
end
end
return AIR_STEP_NONE
end
--- @param m MarioState
--- Applies twirl gravity to donkey kong
local function apply_donkey_kong_twirl_gravity(m)
if not m then return end
local terminalVelocity
local heaviness = 1.0
if m.angleVel.y > 1024 then
heaviness = 1024.0 / m.angleVel.y
end
terminalVelocity = -75.0 * heaviness
m.vel.y = m.vel.y - 4.0 * heaviness
if m.vel.y < terminalVelocity then
m.vel.y = terminalVelocity
end
end
--- @param m MarioState
--- @return integer
--- Checks if gravity should be strengthen for donkey kong jump ascent
local function should_strengthen_gravity_for_donkey_kong_jump_ascent(m)
if not m then return 0 end
if m.flags & MARIO_UNKNOWN_08 == 0 then
return 0
end
if m.action & ACT_FLAG_INTANGIBLE ~= 0 or m.action & ACT_FLAG_INVULNERABLE ~= 0 then
return 0
end
if m.input & INPUT_A_DOWN == 0 and m.vel.y > 20.0 then
return m.action & ACT_FLAG_CONTROL_JUMP_HEIGHT ~= 0 and 1 or 0
end
return 0
end
--- @param m MarioState
--- Applies gravity to donkey kong
local function apply_donkey_kong_gravity(m)
if m.action == ACT_TWIRLING and m.vel.y < 0.0 then
apply_donkey_kong_twirl_gravity(m)
elseif m.action == ACT_SHOT_FROM_CANNON then
m.vel.y = math.max(-75, m.vel.y - 1)
elseif m.action == ACT_LONG_JUMP or m.action == ACT_SLIDE_KICK or m.action == ACT_BBH_ENTER_SPIN then
m.vel.y = math.max(-75, m.vel.y - 3.0)
elseif m.action == ACT_LAVA_BOOST or m.action == ACT_FALL_AFTER_STAR_GRAB then
m.vel.y = math.max(-65, m.vel.y - 4.8)
elseif m.action == ACT_GETTING_BLOWN then
m.vel.y = math.max(-75, m.vel.y - (1.5 * m.unkC4))
elseif should_strengthen_gravity_for_donkey_kong_jump_ascent(m) ~= 0 then
m.vel.y = m.vel.y / 4.0
elseif m.action & ACT_FLAG_METAL_WATER ~= 0 then
m.vel.y = math.max(-16, m.vel.y - 2.4)
elseif m.flags & MARIO_WING_CAP ~= 0 and m.vel.y < 0.0 and m.input & INPUT_A_DOWN ~= 0 then
m.marioBodyState.wingFlutter = 1
m.vel.y = m.vel.y - 3.0
if m.vel.y < -37.5 then
m.vel.y = math.min(-37.5, m.vel.y + 4)
end
else
if m.vel.y < 0 then
m.vel.y = math.max(-75, m.vel.y - 6)
else
m.vel.y = math.max(-75, m.vel.y - 4.25)
end
end
end
---@param m MarioState
--- Applies vertical wind to donkey kong
local function apply_donkey_kong_vertical_wind(m)
if not m then return end
local maxVelY
local offsetY
if m.action ~= ACT_GROUND_POUND then
offsetY = m.pos.y - -1500.0
if m.floor and m.floor.type == SURFACE_VERTICAL_WIND and -3000.0 < offsetY and offsetY < 2000.0 then
if offsetY >= 0.0 then
maxVelY = 10000.0 / (offsetY + 200.0)
else
maxVelY = 50.0
end
if m.vel.y < maxVelY then
m.vel.y = (m.vel.y + maxVelY / 6.0) -- Bit stronger so DK doesn't fall through
if m.vel.y > maxVelY then
m.vel.y = maxVelY
end
end
end
end
end
--- @param m MarioState
--- @param stepArg integer
--- @return integer
--- Performs an air step for donkey kong
local function perform_donkey_kong_air_step(m, stepArg)
local intendedPos = gVec3fZero()
local quarterStepResult
local stepResult = AIR_STEP_NONE
m.wall = nil
for i = 0, 3 do
local step = gVec3fZero()
step = {
x = m.vel.x / 4.0,
y = m.vel.y / 4.0,
z = m.vel.z / 4.0,
}
intendedPos.x = m.pos.x + step.x
intendedPos.y = m.pos.y + step.y
intendedPos.z = m.pos.z + step.z
vec3f_normalize(step)
set_find_wall_direction(step, true, true)
quarterStepResult = perform_donkey_kong_air_quarter_step(m, intendedPos, stepArg)
set_find_wall_direction(step, false, false)
--! On one qf, hit OOB/ceil/wall to store the 2 return value, and continue
-- getting 0s until your last qf. Graze a wall on your last qf, and it will
-- return the stored 2 with a sharply angled reference wall. (some gwks)
if (quarterStepResult ~= AIR_STEP_NONE) then
stepResult = quarterStepResult
end
if (quarterStepResult == AIR_STEP_LANDED or quarterStepResult == AIR_STEP_GRABBED_LEDGE
or quarterStepResult == AIR_STEP_GRABBED_CEILING
or quarterStepResult == AIR_STEP_HIT_LAVA_WALL) then
break
end
end
if (m.vel.y >= 0.0) then
m.peakHeight = m.pos.y
end
m.terrainSoundAddend = mario_get_terrain_sound_addend(m)
-- Start climbing
if m.wall ~= nil and m.action ~= ACT_DONKEY_CLIMB and m.prevAction ~= ACT_DONKEY_CLIMB
and (m.action & ACT_FLAG_INVULNERABLE == 0) and stepResult ~= AIR_STEP_HIT_LAVA_WALL
and m.input & INPUT_A_DOWN ~= 0 and m.heldObj == nil then
local wallangle = atan2s(m.wallNormal.z, m.wallNormal.x) + 0x8000
-- Only grab wall if within certain angle of the wall
if abs_angle_diff(wallangle, m.faceAngle.y) < 0x3000 then
set_mario_action(m, ACT_DONKEY_CLIMB, 0)
if stepResult == AIR_STEP_HIT_WALL then return 0 end
return stepResult
end
end
if (m.action ~= ACT_FLYING and m.action ~= ACT_BUBBLED) then
apply_donkey_kong_gravity(m)
end
apply_donkey_kong_vertical_wind(m)
vec3f_copy(m.marioObj.header.gfx.pos, m.pos)
vec3s_set(m.marioObj.header.gfx.angle, 0, m.faceAngle.y, 0)
return stepResult
end
function donkey_kong_before_phys_step(m, stepType, stepArg)
if stepType == STEP_TYPE_GROUND then
-- return perform_donkey_kong_ground_step(m) -- TBA
elseif stepType == STEP_TYPE_AIR then
return perform_donkey_kong_air_step(m, stepArg)
elseif stepType == STEP_TYPE_WATER then
-- return perform_donkey_kong_water_step(m) -- TBA
elseif stepType == STEP_TYPE_HANG then
-- return perform_donkey_kong_hanging_step(m) -- TBA
end
end
function donkey_kong_before_action(m, action, actionArg)
if (action == ACT_DIVE or action == ACT_MOVE_PUNCHING) and m.action & ACT_FLAG_AIR == 0 and m.input & INPUT_A_DOWN == 0 and m.forwardVel >= 20 then
m.vel.y = 20
m.faceAngle.x = 0
return ACT_DONKEY_KONG_ROLL
elseif (action == ACT_PUNCHING and actionArg == 9) then
return ACT_DONKEY_KONG_POUND
end
end
function donkey_kong_on_interact(m, o, type, value)
-- allow donkey kong to grab objects with the roll
if type == INTERACT_GRABBABLE and m.action == ACT_DONKEY_KONG_ROLL then
if ((o.oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE) == 0) then
m.interactObj = o
m.input = m.input | INPUT_INTERACT_OBJ_GRABBABLE
if (o.oSyncID ~= 0) then network_send_object(o, false) end
return 1
end
end
end
function on_attack_object(m, o, interaction)
-- speed up when hitting enemies with roll
if (m.action == ACT_DONKEY_KONG_ROLL or m.action == ACT_DONKEY_KONG_ROLL_AIR) and (interaction & INT_FAST_ATTACK_OR_SHELL ~= 0) then
if o.oInteractType == INTERACT_BULLY then
mario_set_forward_vel(m, -25)
m.actionTimer = DONKEY_KONG_ROLL_DECAY_TIME
m.actionArg = 1
else
local newForwardVel = math.min(m.forwardVel * 1.1, 70)
mario_set_forward_vel(m, newForwardVel)
m.actionTimer = 0
m.actionArg = 0
end
end
-- Bounce code
if (CT_DONKEY_KONG ~= _G.charSelect.character_get_current_number(m.playerIndex)) then return end
if (_G.charSelect.get_options_status(6) ~= 0) then
if (interaction == INT_HIT_FROM_ABOVE and m.framesSinceA < 5) then
m.actionTimer = 0
if (m.action == ACT_DONKEY_KONG_BOUNCE) then
set_mario_action(m, ACT_DONKEY_KONG_BOUNCE, m.actionArg + 1)
else
set_mario_action(m, ACT_DONKEY_KONG_BOUNCE, 1)
end
end
end
end
hook_event(HOOK_ON_ATTACK_OBJECT, on_attack_object)
_G.ACT_DONKEY_KONG_ROLL = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_ATTACKING | ACT_FLAG_MOVING)
_G.ACT_DONKEY_KONG_ROLL_AIR = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_ATTACKING | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_DONKEY_KONG_POUND = allocate_mario_action(ACT_GROUP_STATIONARY | ACT_FLAG_ATTACKING)
_G.ACT_DONKEY_KONG_POUND_HIT = allocate_mario_action(ACT_GROUP_STATIONARY | ACT_FLAG_ATTACKING)
_G.ACT_DONKEY_KONG_BOUNCE = (ACT_GROUP_AIRBORNE | ACT_FLAG_MOVING | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
---------------
-- DK Bounce --
---------------
--- SwagSkeleton95
--- Allows the player to bounce across enemies with well-timed A presses. Spawns coins when chained across multiple enemies. Credit to baconator2558 for the vast majority of this code.
--- action
local bounceSounds = {
audio_sample_load("z_sfx_dk_bounce1.ogg"),
audio_sample_load("z_sfx_dk_bounce2.ogg"),
audio_sample_load("z_sfx_dk_bounce3.ogg")
}
local coinObj = nil
function act_dk_bounce(m)
if (m.actionTimer == 0) then
set_character_animation(m, CHAR_ANIM_FORWARD_SPINNING)
set_anim_to_frame(m, 0)
m.forwardVel = 0
m.vel.x = 0
m.vel.y = 80
play_character_sound(m, CHAR_SOUND_YAHOO_WAHA_YIPPEE)
m.vel.z = 0
m.slideVelX = 0
m.slideVelZ = 0
m.faceAngle.y = m.intendedYaw
if (m.actionArg >= 3) then
coinObj = spawn_non_sync_object(id_bhvBlueCoinJumping, E_MODEL_SPARKLES, m.pos.x, m.pos.y, m.pos.z, nil)
end
audio_sample_play(bounceSounds[math.min(m.actionArg,3)], m.pos, 0.5)
-- plays a random sound from a table ('bounceSounds') of 3 sound files.
-- I didn't include them here because I ripped them straight from DKCR myself
-- and I'm under the impression that this mod mainly uses self-made sound effects
set_mario_particle_flags(m, PARTICLE_HORIZONTAL_STAR, 0)
end
if (m.actionTimer >= 1 and coinObj ~= nil) then
coinObj.oPosX = m.pos.x
coinObj.oPosY = m.pos.y
coinObj.oPosZ = m.pos.z
interact_coin(m, INTERACT_COIN, coinObj)
coinObj = nil
end
if (m.actionTimer > 5 and m.marioObj.header.gfx.animInfo.animID == CHAR_ANIM_FORWARD_SPINNING) then
set_character_animation(m, CHAR_ANIM_TRIPLE_JUMP)
set_anim_to_frame(m, 21)
end
m.forwardVel = math.min(m.forwardVel, 95)
update_air_without_turn(m)
if (m.actionTimer > 20) then
update_air_without_turn(m)
end
if (m.vel.y < 10) then
update_air_without_turn(m)
if (m.vel.y < -10) then
update_air_without_turn(m)
update_air_without_turn(m)
update_air_without_turn(m)
update_air_without_turn(m)
update_air_without_turn(m)
end
end
local stepResult = perform_air_step(m, AIR_STEP_CHECK_HANG | AIR_STEP_CHECK_LEDGE_GRAB)
if (stepResult == AIR_STEP_LANDED) then
set_character_animation(m, CHAR_ANIM_FORWARD_SPINNING)
set_anim_to_frame(m, 0)
return set_mario_action(m, ACT_DOUBLE_JUMP_LAND, 0)
elseif (stepResult == AIR_STEP_GRABBED_LEDGE) then
set_character_animation(m, CHAR_ANIM_IDLE_ON_LEDGE)
return drop_and_set_mario_action(m, ACT_LEDGE_GRAB, 0)
elseif (stepResult == AIR_STEP_GRABBED_CEILING) then
return set_mario_action(m, ACT_START_HANGING, 0)
end
m.faceAngle.y = approach_s16_symmetric(m.faceAngle.y, m.intendedYaw, (abs_angle_diff(m.faceAngle.y, m.intendedYaw) / (25 * m.actionTimer + 1)) + 750)
update_air_without_turn(m)
m.actionTimer = m.actionTimer + 1
if (check_kick_or_dive_in_air(m) ~= 0) then
return 1
end
return 0
end
hook_mario_action(ACT_DONKEY_KONG_BOUNCE, act_dk_bounce, INT_HIT_FROM_ABOVE)
--- Roll
---@param m MarioState
local function act_donkey_kong_roll(m)
if (not m) then return 0 end
local isSliding = (mario_floor_is_slippery(m)) ~= 0
if isSliding then
if update_sliding(m, 4) ~= 0 or m.actionState == 0 then
m.faceAngle.x = 0
return set_mario_action(m, ACT_DECELERATING, 0)
end
end
if mario_check_object_grab(m) ~= 0 then
m.faceAngle.x = 0
set_character_animation(m, CHAR_ANIM_FIRST_PUNCH)
set_anim_to_frame(m, 2)
return 1
end
if (m.input & INPUT_A_PRESSED) ~= 0 then
m.faceAngle.x = 0
m.marioObj.header.gfx.angle.x = m.faceAngle.x
local result = set_jumping_action(m, ACT_JUMP, 0)
if not isSliding then
m.forwardVel = m.forwardVel / 0.8 - 5 -- conserve all jump momentum minus 5
end
return result
end
local doSpinAnim = true
m.actionTimer = m.actionTimer + 1
set_character_animation(m, CHAR_ANIM_START_CROUCHING)
if m.actionState == 0 then
doSpinAnim = false
local newForwardVel = m.forwardVel
newForwardVel = DONKEY_KONG_ROLL_SPEED * (m.actionTimer / DONKEY_KONG_ROLL_STARTUP)
if m.actionTimer >= DONKEY_KONG_ROLL_STARTUP then
newForwardVel = DONKEY_KONG_ROLL_SPEED
m.actionState = 1
end
mario_set_forward_vel(m, newForwardVel)
elseif m.actionTimer >= DONKEY_KONG_ROLL_DECAY_TIME and not isSliding then
-- slow down after a time
local newForwardVel = m.forwardVel
newForwardVel = newForwardVel * DONKEY_KONG_ROLL_DECAY_PERCENT
mario_set_forward_vel(m, newForwardVel)
end
-- influence direction slightly
m.marioObj.oMoveAngleYaw = m.faceAngle.y
cur_obj_rotate_yaw_toward(m.intendedYaw, 0x100)
m.faceAngle.y = m.marioObj.oMoveAngleYaw
local result = perform_ground_step(m)
if result == GROUND_STEP_LEFT_GROUND then
if m.actionState == 0 then
mario_set_forward_vel(m, DONKEY_KONG_ROLL_SPEED)
end
return set_mario_action(m, ACT_DONKEY_KONG_ROLL_AIR, 0)
elseif result == GROUND_STEP_HIT_WALL then
if (m.wall or gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_OFF) then
m.faceAngle.x = 0
set_mario_particle_flags(m, PARTICLE_VERTICAL_STAR, 0)
slide_bonk(m, ACT_GROUND_BONK, ACT_WALKING)
return
end
end
if doSpinAnim then
local prevFaceAngleX = m.faceAngle.x
m.faceAngle.x = m.faceAngle.x + 0x60 * m.forwardVel
m.marioObj.header.gfx.angle.x = m.faceAngle.x
m.marioObj.header.gfx.pos.y = m.marioObj.header.gfx.pos.y + 50
if prevFaceAngleX <= 0 and m.faceAngle.x > 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
end
-- end roll
if m.actionTimer > DONKEY_KONG_ROLL_END then
m.faceAngle.x = 0
return set_mario_action(m, ACT_WALKING, 0)
end
return 0
end
hook_mario_action(ACT_DONKEY_KONG_ROLL, { every_frame = act_donkey_kong_roll }, INT_FAST_ATTACK_OR_SHELL)
---@param m MarioState
local function act_donkey_kong_roll_air(m)
if (not m) then return 0 end
if (m.input & INPUT_A_PRESSED) ~= 0 then
m.terrainSoundAddend = 0
m.faceAngle.x = 0
m.marioObj.header.gfx.angle.x = m.faceAngle.x
local result = set_mario_action(m, ACT_JUMP, 0)
m.forwardVel = m.forwardVel / 0.8 - 5 -- conserve all jump momentum minus 5
return result
end
m.actionTimer = m.actionTimer + 1
-- influence direction slightly
m.marioObj.oMoveAngleYaw = m.faceAngle.y
cur_obj_rotate_yaw_toward(m.intendedYaw, 0x100)
m.faceAngle.y = m.marioObj.oMoveAngleYaw
mario_set_forward_vel(m, m.forwardVel)
local result = perform_air_step(m, AIR_STEP_CHECK_LEDGE_GRAB)
if result == AIR_STEP_LANDED then
if (check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB) == 0) then
set_mario_action(m, ACT_DONKEY_KONG_ROLL, 0)
m.actionState = 1
return 1
end
elseif result == AIR_STEP_HIT_WALL then
if (m.wall or gServerSettings.bouncyLevelBounds == BOUNCY_LEVEL_BOUNDS_OFF) then
mario_bonk_reflection(m, 1)
if (m.vel.y > 0) then m.vel.y = 0 end
set_mario_particle_flags(m, PARTICLE_VERTICAL_STAR, 0)
drop_and_set_mario_action(m, ACT_BACKWARD_AIR_KB, 0)
return 1
end
elseif result == AIR_STEP_HIT_LAVA_WALL then
lava_boost_on_wall(m)
return 1
end
local prevFaceAngleX = m.faceAngle.x
m.faceAngle.x = m.faceAngle.x + 0x60 * m.forwardVel
m.marioObj.header.gfx.angle.x = m.faceAngle.x
m.marioObj.header.gfx.pos.y = m.marioObj.header.gfx.pos.y + 50
if prevFaceAngleX <= 0 and m.faceAngle.x > 0 then
play_sound(SOUND_ACTION_SPIN, m.marioObj.header.gfx.cameraToObject)
end
if m.actionTimer > DONKEY_KONG_ROLL_END then
m.faceAngle.x = 0
return set_mario_action(m, ACT_FREEFALL, 0)
end
return 0
end
hook_mario_action(ACT_DONKEY_KONG_ROLL_AIR, { every_frame = act_donkey_kong_roll_air }, INT_FAST_ATTACK_OR_SHELL)
local function act_donkey_kong_pound(m)
if (not m) then return 0 end
mario_set_forward_vel(m, 0)
if (mario_floor_is_slippery(m)) ~= 0 then
return set_mario_action(m, ACT_BEGIN_SLIDING, 0)
end
if (m.input & INPUT_A_PRESSED) ~= 0 then
local result = set_jumping_action(m, ACT_JUMP, 0)
return result
elseif (m.input & INPUT_B_PRESSED) ~= 0 and m.actionTimer ~= 0 then
m.actionState = 1
end
m.actionTimer = m.actionTimer + 1
if m.actionTimer == 1 then
play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_HEAVY_LANDING)
-- Spawn particles at hand that hit ground
local pos = {x = 0, y = 0, z = 0}
if m.marioObj.header.gfx.animInfo.animFrame >= 8 then
get_mario_anim_part_pos(m, MARIO_ANIM_PART_RIGHT_HAND, pos)
else
get_mario_anim_part_pos(m, MARIO_ANIM_PART_LEFT_HAND, pos)
end
pos.y = m.pos.y -- always appear on ground
spawn_non_sync_object(id_bhvHorStarParticleSpawner, E_MODEL_NONE, pos.x, pos.y, pos.z, nil)
spawn_non_sync_object(id_bhvMistCircParticleSpawner, E_MODEL_NONE, pos.x, pos.y, pos.z, nil)
m.action = ACT_DONKEY_KONG_POUND_HIT
m.marioObj.hitboxRadius = 100 -- larger hitbox
elseif m.action == ACT_DONKEY_KONG_POUND_HIT then
m.action = ACT_DONKEY_KONG_POUND
m.marioObj.hitboxRadius = 37 -- reset hitbox
elseif m.actionTimer >= 8 then
if m.actionState ~= 0 then
-- pound again
m.actionTimer = 0
m.actionState = 0
elseif m.input & INPUT_Z_DOWN ~= 0 then
set_mario_action(m, ACT_START_CROUCHING, 0)
else
set_mario_action(m, ACT_IDLE, 0)
end
end
--set_character_anim_with_accel(m, CHAR_ANIM_PLACE_LIGHT_OBJ, 0x20000)
-- 28 anim frames in 16 frames
if m.marioObj.header.gfx.animInfo.animFrame > 15 and m.actionTimer == 0 then
--djui_chat_message_create(tostring(m.marioObj.header.gfx.animInfo.animFrame))
set_anim_to_frame(m, 0)
end
play_custom_anim(m, "donkey_ground_slap", 0x10000 * 28 // 16)
--[[set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.animFrame)
if m.controller.buttonPressed & L_TRIG ~= 0 then
set_anim_to_frame(m, m.marioObj.header.gfx.animInfo.animFrame + 1)
end]]
local result = perform_ground_step(m)
if result == GROUND_STEP_LEFT_GROUND then
return set_mario_action(m, ACT_FREEFALL, 0)
end
end
hook_mario_action(ACT_DONKEY_KONG_POUND, { every_frame = act_donkey_kong_pound })
hook_mario_action(ACT_DONKEY_KONG_POUND_HIT, { every_frame = act_donkey_kong_pound }, INT_GROUND_POUND) -- same action but with ground pound interaction
-----------------------
--- Donkey Climbing ---
--- -------------------
DK_ANIM_CLIMBING = 'donkey_custom_climbing'
ACT_DONKEY_CLIMB = allocate_mario_action(ACT_FLAG_AIR | ACT_GROUP_AIRBORNE)
-- Climbing ability action
--- @param m MarioState
function act_donkey_climb(m)
--No wall, no climb
if m.wall == nil then
set_mario_action(m, ACT_TRIPLE_JUMP, 0)
mario_set_forward_vel(m, 10)
return true
--Press A to jump off
elseif (m.input & INPUT_A_PRESSED) ~= 0 then
set_mario_action(m, ACT_JUMP, 0)
m.faceAngle.y = m.faceAngle.y - 0x8000
mario_set_forward_vel(m, 20)
return true
--Press Z to just fall off
elseif (m.input & INPUT_Z_PRESSED) ~= 0 then
m.input = m.input &~ INPUT_Z_PRESSED
play_character_sound(m, CHAR_SOUND_UH)
mario_set_forward_vel(m, -8)
return set_mario_action(m, ACT_FREEFALL, 0)
end
--Woah!
if m.actionTimer == 0 then
play_character_sound(m, CHAR_SOUND_WHOA)
end
local climbAnimSpeed = m.intendedMag
local wallangle = atan2s(m.wallNormal.z, m.wallNormal.x) + 0x8000
local transwall
if m.actionTimer >= 4 then
--Face beside wall and move around it
m.faceAngle.y = wallangle - 0x4000
if m.actionTimer <= DONKEY_KONG_SLIP_TIME then
mario_set_forward_vel(m, m.controller.stickX/3)
m.vel.y = m.controller.stickY/3
elseif m.actionTimer <= DONKEY_KONG_SLIDE_TIME then -- Slip on wall after some time
climbAnimSpeed = 32
m.vel.y = 0
else
climbAnimSpeed = 0
m.vel.y = m.vel.y + 8 -- counteract gravity
end
--Perform air step
local air_step = perform_air_step(m, 0)
transwall = m.wall
if air_step == AIR_STEP_LANDED then
return set_mario_action(m, ACT_FREEFALL_LAND, 0)
end
end
--Face directly towards wall to make sure we're latched on
m.faceAngle.y = wallangle
mario_set_forward_vel(m, 1)
if m.actionTimer <= DONKEY_KONG_SLIP_TIME then
m.vel.y = 0
elseif m.actionTimer <= DONKEY_KONG_SLIDE_TIME then
m.vel.y = 5
end
--Perform air step
air_step = perform_air_step(m, 0)
if air_step == AIR_STEP_LANDED then
return set_mario_action(m, ACT_FREEFALL_LAND, 0)
elseif m.wall == nil then
if transwall == nil then
set_mario_action(m, ACT_TRIPLE_JUMP, 0)
mario_set_forward_vel(m, 10)
return true
else
m.wall = transwall
end
end
--Climbing animation
play_custom_anim(m, "donkey_custom_climbing", climbAnimSpeed * 0x3000)
if m.actionTimer < 8 or climbAnimSpeed == 0 then
set_anim_to_frame(m, 0)
else
m.particleFlags = m.particleFlags | PARTICLE_DUST
m.terrainSoundAddend = SOUND_TERRAIN_SAND << 16
play_step_sound(m, 26, 79)
end
local inward_offset = 25
m.marioObj.header.gfx.pos.x = m.marioObj.header.gfx.pos.x + inward_offset * sins(m.faceAngle.y)
m.marioObj.header.gfx.pos.z = m.marioObj.header.gfx.pos.z + inward_offset * coss(m.faceAngle.y)
m.actionTimer = m.actionTimer + 1
end
hook_mario_action(ACT_DONKEY_CLIMB, {every_frame = act_donkey_climb, gravity = function() end})

View file

@ -1,4 +1,9 @@
if not _G.charSelectExists then return end
---------------------
-- Pauline Moveset --
---------------------
local OmmEnabled = OmmEnabled
if not charSelect then return end
---------------
-- Constants --
@ -74,12 +79,12 @@ local CAPPY_EVENT_UNLOAD = 5
local CAPPY_EVENT_BOUNCE = 6
-- Actions
local ACT_CAPPY_THROW_GROUND = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_MOVING)
local ACT_CAPPY_THROW_AIRBORNE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR)
local ACT_CAPPY_BOUNCE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
local ACT_CAPPY_VAULT = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
local ACT_CAPPY_RAINBOW_SPIN = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_ATTACKING)
local ACT_CAPPY_THROW_WATER = allocate_mario_action(ACT_GROUP_SUBMERGED | ACT_FLAG_SWIMMING)
_G.ACT_CAPPY_THROW_GROUND = allocate_mario_action(ACT_GROUP_MOVING | ACT_FLAG_MOVING)
_G.ACT_CAPPY_THROW_AIRBORNE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR)
_G.ACT_CAPPY_BOUNCE = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_CAPPY_VAULT = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)
_G.ACT_CAPPY_RAINBOW_SPIN = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_ATTACKING)
_G.ACT_CAPPY_THROW_WATER = allocate_mario_action(ACT_GROUP_SUBMERGED | ACT_FLAG_SWIMMING)
-- Animations
local MARIO_ANIM_PAULINE_CAPPY_VAULT = "anim_pauline_cappy_vault"
@ -135,11 +140,10 @@ local function mario_anim_play_custom(m, animName, animAccel)
m.marioObj.header.gfx.animInfo.animAccel = animAccel or 0x10000
end
-- Hacky way to get Pauline
local function get_pauline()
for i = 1, #extraCharacters do
local extraCharacter = extraCharacters[i]
if extraCharacter.name == "Pauline" then
if extraCharacter.tablePos == CT_PAULINE then
return extraCharacter
end
end
@ -147,7 +151,7 @@ local function get_pauline()
end
local function is_pauline(m)
return _G.charSelect.character_get_current_number(m.playerIndex) == get_pauline().tablePos
return character_get_current_number(m.playerIndex) == CT_PAULINE
end
-----------
@ -172,7 +176,7 @@ local id_bhvCappy = hook_behavior(
true,
function (o) o.oFlags = o.oFlags | OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE end,
function (o) bhv_cappy_loop(o) end,
"bhvCappy"
"bhvPaulineCappy"
)
local function cappy_get_object(m)
@ -897,7 +901,6 @@ local function cappy_process_mario_interactions(cappy)
if cappy_mario_can_bounce() then
local marioObj = m0.marioObj
obj_set_pos(marioObj, m0.pos.x, m0.pos.y, m0.pos.z)
marioObj.hitboxRadius = 50
local obj = obj_get_first_with_behavior_id(id_bhvCappy)
while obj ~= nil do
if (obj.oSubAction == 1 and -- Cappy is spawned
@ -913,7 +916,12 @@ local function cappy_process_mario_interactions(cappy)
if (marioObj.oIntangibleTimer == 0 or mAction == ACT_BUBBLED) then
-- Check hitbox overlap
if obj_check_hitbox_overlap(marioObj, obj) then
local marioHitboxRadius = marioObj.hitboxRadius
marioObj.hitboxRadius = 50
local hitboxOverlap = obj_check_hitbox_overlap(marioObj, obj)
marioObj.hitboxRadius = marioHitboxRadius
if hitboxOverlap then
local marioGfx = marioObj.header.gfx
-- Pop bubble
@ -1175,7 +1183,7 @@ end
local function cappy_update(m, cappy)
-- Unload Cappy if...
if (_G.OmmEnabled or -- OMM Rebirth is enabled
if (OmmEnabled or -- OMM Rebirth is enabled
not is_pauline(m) or -- Not Pauline
not gNetworkPlayers[m.playerIndex].connected or -- Not connected
not is_player_active(m) -- Not active
@ -1296,7 +1304,6 @@ function pauline_update(m)
end
end
cappy_process_mario_interactions(cappy)
m.marioObj.hitboxRadius = 37
end
-- Process Cappy events

View file

@ -0,0 +1,59 @@
-------------------
-- Peach Moveset --
-------------------
if not charSelect then return end
local floatActs = {
[ACT_JUMP] = true,
[ACT_DOUBLE_JUMP] = true,
[ACT_TRIPLE_JUMP] = true,
[ACT_LONG_JUMP] = true,
[ACT_BACKFLIP] = true,
[ACT_SIDE_FLIP] = true,
[ACT_WALL_KICK_AIR] = true,
}
_G.ACT_FLOAT = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_MOVING)
--- @param m MarioState
local function act_float(m)
-- apply movement when using action
common_air_action_step(m, ACT_JUMP_LAND, CHAR_ANIM_BEND_KNESS_RIDING_SHELL, AIR_STEP_NONE)
-- setup when action starts (horizontal speed and voiceline)
if m.actionTimer == 0 then
play_character_sound(m, CHAR_SOUND_HELLO)
end
if m.forwardVel > 20 then
m.forwardVel = m.forwardVel - 0.5
end
-- Slowly decend
m.vel.y = -1
set_mario_particle_flags(m, PARTICLE_SPARKLES, 0)
-- avoid issue with flying and then make the hover end after 2 secs or when stopping holding the button
if m.prevAction ~= ACT_TRIPLE_JUMP and (m.flags & MARIO_WING_CAP) ~= 0 then
if m.actionTimer >= 50 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
else
if m.actionTimer >= 50 or (m.controller.buttonDown & A_BUTTON) == 0 then
set_mario_action(m, ACT_FREEFALL, 0)
end
end
-- increment the action timer to make the hover stop
m.actionTimer = m.actionTimer + 1
end
--- @param m MarioState
function peach_update(m)
if (m.input & INPUT_A_DOWN) ~= 0 and m.vel.y < -10 and m.prevAction ~= ACT_FLOAT and floatActs[m.action] then
set_mario_action(m, ACT_FLOAT, 0)
end
end
hook_mario_action(ACT_FLOAT, act_float)

View file

@ -0,0 +1,203 @@
----------------------
-- Rosalina Moveset --
----------------------
if not charSelect then return end
_G.ACT_JUMP_TWIRL = allocate_mario_action(ACT_GROUP_AIRBORNE | ACT_FLAG_AIR | ACT_FLAG_ATTACKING)
E_MODEL_TWIRL_EFFECT = smlua_model_util_get_id("spin_attack_geo")
---@param o Object
local function bhv_spin_attack_init(o)
o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE -- Allows you to change the position and angle
end
---@param o Object
local function bhv_spin_attack_loop(o)
-- Retrieves the Mario state corresponding to its global index
local m = gMarioStates[network_local_index_from_global(o.globalPlayerIndex)]
if m == nil or m.marioObj == nil then
obj_mark_for_deletion(o)
return
end
o.parentObj = m.marioObj -- Sets the Mario object as its parent
cur_obj_set_pos_relative_to_parent(0, 20, 0) -- Makes it move to its parent's position
o.oFaceAngleYaw = o.oFaceAngleYaw + 0x2000 -- Rotates it
if m.action ~= ACT_JUMP_TWIRL or o.oTimer > 15 then -- Deletes itself once the action changes
obj_mark_for_deletion(o)
end
end
local id_bhvTwirlEffect = hook_behavior(nil, OBJ_LIST_GENACTOR, true, bhv_spin_attack_init, bhv_spin_attack_loop,
"bhvRosalinaTwirlEffect")
-- Spinable actions, these are actions you can spin out of that don't normally allow a kick/dive
local extraSpinActs = {
[ACT_LONG_JUMP] = true,
[ACT_BACKFLIP] = true,
}
-- Spin overridable actions, these are overriden instantly
local spinOverrides = {
[ACT_PUNCHING] = true,
[ACT_MOVE_PUNCHING] = true,
[ACT_JUMP_KICK] = true,
[ACT_DIVE] = true
}
local ROSALINA_SOUND_SPIN = audio_sample_load("z_sfx_rosalina_spinattack.ogg") -- Load audio sample
---@param m MarioState
function act_jump_twirl(m)
local e = gCharacterStates[m.playerIndex]
if m.actionTimer >= 15 then
return set_mario_action(m, ACT_FREEFALL, 0) -- End the action
end
if m.actionTimer == 0 then
m.marioObj.header.gfx.animInfo.animID = -1
play_character_sound(m, CHAR_SOUND_HELLO) -- Plays the character sound
audio_sample_play(ROSALINA_SOUND_SPIN, m.pos, 1) -- Plays the spin sound sample
m.particleFlags = m.particleFlags | ACTIVE_PARTICLE_SPARKLES -- Spawns sparkle particles
if e.rosalina.canSpin then
m.vel.y = 30 -- Initial upward velocity
e.rosalina.canSpin = false
-- Spawn the spin effect
if m.playerIndex == 0 then
spawn_sync_object(id_bhvTwirlEffect, E_MODEL_TWIRL_EFFECT, m.pos.x, m.pos.y, m.pos.z, function(o)
o.globalPlayerIndex = m.marioObj.globalPlayerIndex
end)
end
else
m.vel.y = math.max(m.vel.y, 0)
end
m.marioObj.hitboxRadius = 100 -- Damage hitbox
else
m.marioObj.hitboxRadius = 37 -- Reset the hitbox after initial hit
end
common_air_action_step(m, ACT_FREEFALL_LAND, CHAR_ANIM_BEND_KNESS_RIDING_SHELL, AIR_STEP_NONE)
m.marioBodyState.handState = MARIO_HAND_PEACE_SIGN -- Hand State
-- Increments the action timer
m.actionTimer = m.actionTimer + 1
end
---@param m MarioState
---@param o Object
---@param intType InteractionType
function rosalina_allow_interact(m, o, intType)
local e = gCharacterStates[m.playerIndex]
if m.action == ACT_JUMP_TWIRL and intType == INTERACT_GRABBABLE and o.oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE == 0 then
local angleTo = mario_obj_angle_to_object(m, o)
if (o.oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO ~= 0 or obj_has_behavior_id(o, id_bhvBowser) ~= 0) then -- heavy grab objects
if m.pos.y - m.floorHeight < 100 and abs_angle_diff(m.faceAngle.y, angleTo) < 0x4000 then
m.action = ACT_MOVE_PUNCHING
m.actionArg = 1
end
elseif not e.rosalina.orbitObjActive then -- light grab objects
m.usedObj = o
e.rosalina.orbitObjActive = true
e.rosalina.orbitObjDist = 160 - m.actionTimer * 2
e.rosalina.orbitObjAngle = angleTo
return false
end
end
end
---@param m MarioState
function rosalina_update(m)
local e = gCharacterStates[m.playerIndex]
if m.controller.buttonPressed & B_BUTTON ~= 0 and extraSpinActs[m.action] then
return set_mario_action(m, ACT_JUMP_TWIRL, 0)
end
--if m.action & ACT_FLAG_AIR == 0 and m.playerIndex == 0 then
-- e.rosalina.canSpin = true
--end
if m.action ~= ACT_JUMP_TWIRL and m.marioObj.hitboxRadius ~= 37 then
m.marioObj.hitboxRadius = 37
end
if e.rosalina.orbitObjActive then
local o = m.usedObj
if not o or o.activeFlags == ACTIVE_FLAG_DEACTIVATED then
e.rosalina.orbitObjActive = false
o.oIntangibleTimer = 0
if m.playerIndex == 0 then m.usedObj = nil end
return
end
e.rosalina.orbitObjDist = e.rosalina.orbitObjDist - 6
if e.rosalina.orbitObjDist >= 90 then
e.rosalina.orbitObjAngle = e.rosalina.orbitObjAngle + 0x1800
else
e.rosalina.orbitObjAngle = approach_s16_asymptotic(e.rosalina.orbitObjAngle, m.faceAngle.y, 4)
end
o.oPosX = m.pos.x + sins(e.rosalina.orbitObjAngle) * e.rosalina.orbitObjDist
o.oPosZ = m.pos.z + coss(e.rosalina.orbitObjAngle) * e.rosalina.orbitObjDist
o.oPosY = approach_f32_asymptotic(o.oPosY, m.pos.y + 50, 0.25)
obj_set_vel(o, 0, 0, 0)
o.oForwardVel = 0
o.oIntangibleTimer = -1
if m.playerIndex == 0 and e.rosalina.orbitObjDist <= 80 then
e.rosalina.orbitObjActive = false
o.oIntangibleTimer = 0
if m.action & (ACT_FLAG_INVULNERABLE | ACT_FLAG_INTANGIBLE) ~= 0 or m.action & ACT_GROUP_MASK >= ACT_GROUP_SUBMERGED then
m.usedObj = nil
else
o.oIntangibleTimer = 0
m.interactObj = o
m.usedObj = o
if o.oSyncID ~= 0 then network_send_object(o, true) end
if m.action & ACT_FLAG_AIR == 0 then
set_mario_action(m, ACT_HOLD_IDLE, 0)
mario_grab_used_object(m)
else
set_mario_action(m, ACT_HOLD_FREEFALL, 0)
mario_grab_used_object(m)
end
end
end
end
end
---@param m MarioState
function rosalina_before_action(m, action)
if not action then return end
local e = gCharacterStates[m.playerIndex]
if spinOverrides[action] and m.controller.buttonDown & (Z_TRIG | A_BUTTON) == 0 and m.action ~= ACT_STEEP_JUMP then
return ACT_JUMP_TWIRL
end
if action & ACT_FLAG_AIR == 0 and not e.rosalina.canSpin then
play_sound_with_freq_scale(SOUND_GENERAL_COIN_SPURT_EU, m.marioObj.header.gfx.cameraToObject, 1.6)
if m.playerIndex == 0 then
spawn_sync_object(id_bhvSparkle, E_MODEL_SPARKLES_ANIMATION, m.pos.x, m.pos.y + 200, m.pos.z,
function(o) obj_scale(o, 0.75) end)
end
e.rosalina.canSpin = true
end
end
hook_mario_action(ACT_JUMP_TWIRL, act_jump_twirl, INT_KICK)

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more