From 107a12030ae87c48ce7dfdf657946480db768fd9 Mon Sep 17 00:00:00 2001 From: jaburns Date: Thu, 8 Oct 2020 15:59:06 -0600 Subject: [PATCH] Initial commit with hacked up sm64 decomp source --- .gitignore | 3 + configure | 42 + src/engine/geo_layout.c | 797 ++++ src/engine/geo_layout.h | 87 + src/engine/graph_node.c | 898 +++++ src/engine/graph_node.h | 434 ++ src/engine/graph_node_manager.c | 81 + src/engine/math_util.c | 2306 +++++++++++ src/engine/math_util.h | 75 + src/engine/surface_collision.c | 423 ++ src/engine/surface_collision.h | 44 + src/engine/surface_load.c | 378 ++ src/engine/surface_load.h | 8 + src/game/area.h | 157 + src/game/camera.h | 776 ++++ src/game/interaction.c | 928 +++++ src/game/interaction.h | 117 + src/game/level_update.h | 132 + src/game/mario.c | 1903 +++++++++ src/game/mario.h | 54 + src/game/mario_actions_airborne.c | 2122 ++++++++++ src/game/mario_actions_airborne.h | 10 + src/game/mario_actions_automatic.c | 896 +++++ src/game/mario_actions_automatic.h | 10 + src/game/mario_actions_cutscene.c | 2731 +++++++++++++ src/game/mario_actions_cutscene.h | 17 + src/game/mario_actions_moving.c | 2045 ++++++++++ src/game/mario_actions_moving.h | 11 + src/game/mario_actions_object.c | 496 +++ src/game/mario_actions_object.h | 11 + src/game/mario_actions_stationary.c | 1170 ++++++ src/game/mario_actions_stationary.h | 50 + src/game/mario_actions_submerged.c | 1579 ++++++++ src/game/mario_actions_submerged.h | 10 + src/game/mario_misc.c | 656 +++ src/game/mario_misc.h | 31 + src/game/mario_step.c | 668 ++++ src/game/mario_step.h | 36 + src/game/object_stuff.c | 308 ++ src/game/object_stuff.h | 9 + src/game/rendering_graph_node.c | 1166 ++++++ src/game/rendering_graph_node.h | 34 + src/game/save_file.h | 175 + src/gfx_adapter.c | 145 + src/gfx_adapter.h | 21 + src/gfx_adapter_commands.h | 11 + src/guMtxF2L.c | 69 + src/guMtxF2L.h | 6 + src/include/PR/gbi.h | 4850 +++++++++++++++++++++++ src/include/PR/os_cont.h | 207 + src/include/PR/ultratypes.h | 44 + src/include/audio_defines.h | 561 +++ src/include/command_macros_base.h | 28 + src/include/geo_commands.h | 432 ++ src/include/level_misc_macros.h | 28 + src/include/macros.h | 77 + src/include/mario_animation_ids.h | 219 + src/include/mario_geo_switch_case_ids.h | 45 + src/include/object_fields.h | 1161 ++++++ src/include/platform_info.h | 15 + src/include/seq_ids.h | 45 + src/include/sm64.h | 435 ++ src/include/special_preset_names.h | 93 + src/include/surface_terrains.h | 223 ++ src/include/types.h | 348 ++ src/libsm64.c | 154 + src/libsm64.h | 49 + src/memory.c | 32 + src/memory.h | 10 + src/shim.c | 145 + src/shim.h | 82 + 71 files changed, 33419 insertions(+) create mode 100644 .gitignore create mode 100755 configure create mode 100644 src/engine/geo_layout.c create mode 100644 src/engine/geo_layout.h create mode 100644 src/engine/graph_node.c create mode 100644 src/engine/graph_node.h create mode 100644 src/engine/graph_node_manager.c create mode 100644 src/engine/math_util.c create mode 100644 src/engine/math_util.h create mode 100644 src/engine/surface_collision.c create mode 100644 src/engine/surface_collision.h create mode 100644 src/engine/surface_load.c create mode 100644 src/engine/surface_load.h create mode 100644 src/game/area.h create mode 100644 src/game/camera.h create mode 100644 src/game/interaction.c create mode 100644 src/game/interaction.h create mode 100644 src/game/level_update.h create mode 100644 src/game/mario.c create mode 100644 src/game/mario.h create mode 100644 src/game/mario_actions_airborne.c create mode 100644 src/game/mario_actions_airborne.h create mode 100644 src/game/mario_actions_automatic.c create mode 100644 src/game/mario_actions_automatic.h create mode 100644 src/game/mario_actions_cutscene.c create mode 100644 src/game/mario_actions_cutscene.h create mode 100644 src/game/mario_actions_moving.c create mode 100644 src/game/mario_actions_moving.h create mode 100644 src/game/mario_actions_object.c create mode 100644 src/game/mario_actions_object.h create mode 100644 src/game/mario_actions_stationary.c create mode 100644 src/game/mario_actions_stationary.h create mode 100644 src/game/mario_actions_submerged.c create mode 100644 src/game/mario_actions_submerged.h create mode 100644 src/game/mario_misc.c create mode 100644 src/game/mario_misc.h create mode 100644 src/game/mario_step.c create mode 100644 src/game/mario_step.h create mode 100644 src/game/object_stuff.c create mode 100644 src/game/object_stuff.h create mode 100644 src/game/rendering_graph_node.c create mode 100644 src/game/rendering_graph_node.h create mode 100644 src/game/save_file.h create mode 100644 src/gfx_adapter.c create mode 100644 src/gfx_adapter.h create mode 100644 src/gfx_adapter_commands.h create mode 100644 src/guMtxF2L.c create mode 100644 src/guMtxF2L.h create mode 100644 src/include/PR/gbi.h create mode 100644 src/include/PR/os_cont.h create mode 100644 src/include/PR/ultratypes.h create mode 100644 src/include/audio_defines.h create mode 100644 src/include/command_macros_base.h create mode 100644 src/include/geo_commands.h create mode 100644 src/include/level_misc_macros.h create mode 100644 src/include/macros.h create mode 100644 src/include/mario_animation_ids.h create mode 100644 src/include/mario_geo_switch_case_ids.h create mode 100644 src/include/object_fields.h create mode 100644 src/include/platform_info.h create mode 100644 src/include/seq_ids.h create mode 100644 src/include/sm64.h create mode 100644 src/include/special_preset_names.h create mode 100644 src/include/surface_terrains.h create mode 100644 src/include/types.h create mode 100644 src/libsm64.c create mode 100644 src/libsm64.h create mode 100644 src/memory.c create mode 100644 src/memory.h create mode 100644 src/shim.c create mode 100644 src/shim.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1ab439 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build/ +/libsm64.so +Makefile diff --git a/configure b/configure new file mode 100755 index 0000000..dc95e54 --- /dev/null +++ b/configure @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -e +cd "$(dirname "${BASH_SOURCE[0]}")" + +CC='cc -g' +CFLAGS='-Wall -fPIC' +BIN_FILE='libsm64.so' +LDFLAGS='' + +c_to_obj() { + printf 'build/' + echo "$1" | sed 's/cp*$/o/;s:/:_:g' +} + +make_cmd() { + local obj_file="$(c_to_obj "$1")" + $CC $CFLAGS -MM -MT "$obj_file" -c "$1" + echo -e "\t$CC $CFLAGS -o $obj_file -c $1" +} + +file_list() { + find src -iname '*.c' +} + +print_makefile() { + local all_objs='' + for f in $(file_list); do + all_objs="$all_objs $(c_to_obj $f)" + done + echo "$BIN_FILE: $all_objs" + echo -e "\t$CC -shared -o $BIN_FILE $all_objs $LDFLAGS" + + for f in $(file_list); do + make_cmd "$f" + done + + echo '.PHONY: clean run' + echo -e "clean:\n\t find . -iname '*.o' | xargs rm && rm -f ./$BIN_FILE" +} + +mkdir -p build +print_makefile > Makefile diff --git a/src/engine/geo_layout.c b/src/engine/geo_layout.c new file mode 100644 index 0000000..7738c9f --- /dev/null +++ b/src/engine/geo_layout.c @@ -0,0 +1,797 @@ +#include "../include/sm64.h" + +#include "geo_layout.h" +#include "math_util.h" +#include "../memory.h" +#include "graph_node.h" +#include "../shim.h" + +static Vec3s gVec3sZero = { 0, 0, 0 }; + +typedef void (*GeoLayoutCommandProc)(void); + +GeoLayoutCommandProc GeoLayoutJumpTable[] = { + geo_layout_cmd_branch_and_link, + geo_layout_cmd_end, + geo_layout_cmd_branch, + geo_layout_cmd_return, + geo_layout_cmd_open_node, + geo_layout_cmd_close_node, + geo_layout_cmd_assign_as_view, + geo_layout_cmd_update_node_flags, + geo_layout_cmd_node_root, + geo_layout_cmd_node_ortho_projection, + geo_layout_cmd_node_perspective, + geo_layout_cmd_node_start, + geo_layout_cmd_node_master_list, + geo_layout_cmd_node_level_of_detail, + geo_layout_cmd_node_switch_case, + geo_layout_cmd_node_camera, + geo_layout_cmd_node_translation_rotation, + geo_layout_cmd_node_translation, + geo_layout_cmd_node_rotation, + geo_layout_cmd_node_animated_part, + geo_layout_cmd_node_billboard, + geo_layout_cmd_node_display_list, + geo_layout_cmd_node_shadow, + geo_layout_cmd_node_object_parent, + geo_layout_cmd_node_generated, + geo_layout_cmd_node_background, + geo_layout_cmd_nop, + geo_layout_cmd_copy_view, + geo_layout_cmd_node_held_obj, + geo_layout_cmd_node_scale, + geo_layout_cmd_nop2, + geo_layout_cmd_nop3, + geo_layout_cmd_node_culling_radius, +}; + +struct GraphNode gObjParentGraphNode; +struct AllocOnlyPool *gGraphNodePool; +struct GraphNode *gCurRootGraphNode; + +UNUSED s32 D_8038BCA8; + +/* The gGeoViews array is a mysterious one. Some background: + * + * If there are e.g. multiple Goombas, the multiple Goomba objects share one + * Geo node tree describing the goomba 3D model. Since every node has a single + * parent field and not a parent array, the parent is dynamically rebinded to + * each goomba instance just before rendering and set to null afterwards. + * The same happens for ObjectParentNode, which has as his sharedChild a group + * of all 240 object nodes. Why does the ObjectParentNode exist at all, if its + * only purpose is to temporarily bind the actual group with objects? This might + * be another remnant to Luigi. + * + * When creating a root node, room for (2 + cmd+0x02) pointers is allocated in + * gGeoViews. Except for the title screen, cmd+0x02 is 10. The 2 default ones + * might be for Mario and Luigi, and the other 10 could be different cameras for + * different rooms / boss fights. An area might be structured like this: + * + * geo_camera mode_player //Mario cam + * geo_open_node + * geo_render_obj + * geo_assign_as_view 1 // currently unused geo command + * geo_close_node + * + * geo_camera mode_player //Luigi cam + * geo_open_node + * geo_render_obj + * geo_copy_view 1 // currently unused geo command + * geo_assign_as_view 2 + * geo_close_node + * + * geo_camera mode_boss //boss fight cam + * geo_assign_as_view 3 + * ... + * + * There might also be specific geo nodes for Mario or Luigi only. Or a fixed camera + * might not have display list nodes of parts of the level that are out of view. + * In the end Luigi got scrapped and the multiple-camera design did not pan out, + * so everything was reduced to a single ObjectParent with a single group, and + * camera switching was all done in one node. End of speculation. + */ +struct GraphNode **gGeoViews; +u16 gGeoNumViews; // length of gGeoViews array + +uintptr_t gGeoLayoutStack[16]; +struct GraphNode *gCurGraphNodeList[32]; +s16 gCurGraphNodeIndex; +s16 gGeoLayoutStackIndex; // similar to SP register in MIPS +UNUSED s16 D_8038BD7C; +s16 gGeoLayoutReturnIndex; // similar to RA register in MIPS +u8 *gGeoLayoutCommand; + +u32 unused_8038B894[3] = { 0 }; + +/* + 0x00: Branch and store return address + cmd+0x04: void *branchTarget +*/ +void geo_layout_cmd_branch_and_link(void) { + gGeoLayoutStack[gGeoLayoutStackIndex++] = (uintptr_t) (gGeoLayoutCommand + CMD_PROCESS_OFFSET(8)); + gGeoLayoutStack[gGeoLayoutStackIndex++] = (gCurGraphNodeIndex << 16) + gGeoLayoutReturnIndex; + gGeoLayoutReturnIndex = gGeoLayoutStackIndex; + gGeoLayoutCommand = segmented_to_virtual(cur_geo_cmd_ptr(0x04)); +} + +// 0x01: Terminate geo layout +void geo_layout_cmd_end(void) { + gGeoLayoutStackIndex = gGeoLayoutReturnIndex; + gGeoLayoutReturnIndex = gGeoLayoutStack[--gGeoLayoutStackIndex] & 0xFFFF; + gCurGraphNodeIndex = gGeoLayoutStack[gGeoLayoutStackIndex] >> 16; + gGeoLayoutCommand = (u8 *) gGeoLayoutStack[--gGeoLayoutStackIndex]; +} + +/* + 0x02: Branch + cmd+0x04: void *branchTarget +*/ +void geo_layout_cmd_branch(void) { + if (cur_geo_cmd_u8(0x01) == 1) { + gGeoLayoutStack[gGeoLayoutStackIndex++] = (uintptr_t) (gGeoLayoutCommand + CMD_PROCESS_OFFSET(8)); + } + + gGeoLayoutCommand = segmented_to_virtual(cur_geo_cmd_ptr(0x04)); +} + +// 0x03: Return from branch +void geo_layout_cmd_return(void) { + gGeoLayoutCommand = (u8 *) gGeoLayoutStack[--gGeoLayoutStackIndex]; +} + +// 0x04: Open node +void geo_layout_cmd_open_node(void) { + gCurGraphNodeList[gCurGraphNodeIndex + 1] = gCurGraphNodeList[gCurGraphNodeIndex]; + gCurGraphNodeIndex++; + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +// 0x05: Close node +void geo_layout_cmd_close_node(void) { + gCurGraphNodeIndex--; + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x06: Register the current node as a view + cmd+0x02: index + + Register the current node in the gGeoViews array at the given index +*/ +void geo_layout_cmd_assign_as_view(void) { + u16 index = cur_geo_cmd_s16(0x02); + + if (index < gGeoNumViews) { + gGeoViews[index] = gCurGraphNodeList[gCurGraphNodeIndex]; + } + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x07: Update current scene graph node flags + cmd+0x01: u8 operation (0 = reset, 1 = set, 2 = clear) + cmd+0x02: s16 bits +*/ +void geo_layout_cmd_update_node_flags(void) { + u16 operation = cur_geo_cmd_u8(0x01); + u16 flagBits = cur_geo_cmd_s16(0x02); + + switch (operation) { + case GEO_CMD_FLAGS_RESET: + gCurGraphNodeList[gCurGraphNodeIndex]->flags = flagBits; + break; + case GEO_CMD_FLAGS_SET: + gCurGraphNodeList[gCurGraphNodeIndex]->flags |= flagBits; + break; + case GEO_CMD_FLAGS_CLEAR: + gCurGraphNodeList[gCurGraphNodeIndex]->flags &= ~flagBits; + break; + } + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x08: Create a scene graph root node that specifies the viewport + cmd+0x02: s16 num entries (+2) to allocate for gGeoViews + cmd+0x04: s16 x + cmd+0x06: s16 y + cmd+0x08: s16 width + cmd+0x0A: s16 height +*/ +void geo_layout_cmd_node_root(void) { + s32 i; + struct GraphNodeRoot *graphNode; + + s16 x = cur_geo_cmd_s16(0x04); + s16 y = cur_geo_cmd_s16(0x06); + s16 width = cur_geo_cmd_s16(0x08); + s16 height = cur_geo_cmd_s16(0x0A); + + // number of entries to allocate for gGeoViews array + // at least 2 are allocated by default + // cmd+0x02 = 0x00: Mario face, 0x0A: all other levels + gGeoNumViews = cur_geo_cmd_s16(0x02) + 2; + + graphNode = init_graph_node_root(gGraphNodePool, NULL, 0, x, y, width, height); + + // TODO: check type + gGeoViews = alloc_only_pool_alloc(gGraphNodePool, gGeoNumViews * sizeof(struct GraphNode *)); + + graphNode->views = gGeoViews; + graphNode->numViews = gGeoNumViews; + + for (i = 0; i < gGeoNumViews; i++) { + gGeoViews[i] = NULL; + } + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT; +} + +/* + 0x09: Create orthographic projection scene graph node + cmd+0x02: s16 scale as a percentage (usually it's 100) +*/ +void geo_layout_cmd_node_ortho_projection(void) { + struct GraphNodeOrthoProjection *graphNode; + f32 scale = (f32) cur_geo_cmd_s16(0x02) / 100.0f; + + graphNode = init_graph_node_ortho_projection(gGraphNodePool, NULL, scale); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x0A: Create camera frustum scene graph node + cmd+0x01: u8 if nonzero, enable frustumFunc field + cmd+0x02: s16 field of view + cmd+0x04: s16 near + cmd+0x06: s16 far + [cmd+0x08: GraphNodeFunc frustumFunc] +*/ +void geo_layout_cmd_node_perspective(void) { + struct GraphNodePerspective *graphNode; + GraphNodeFunc frustumFunc = NULL; + s16 fov = cur_geo_cmd_s16(0x02); + s16 near = cur_geo_cmd_s16(0x04); + s16 far = cur_geo_cmd_s16(0x06); + + if (cur_geo_cmd_u8(0x01) != 0) { + // optional asm function + frustumFunc = (GraphNodeFunc) cur_geo_cmd_ptr(0x08); + gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT; + } + + graphNode = init_graph_node_perspective(gGraphNodePool, NULL, (f32) fov, near, far, frustumFunc, 0); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x0B: Create a scene graph node that groups other nodes without any + additional functionality +*/ +void geo_layout_cmd_node_start(void) { + struct GraphNodeStart *graphNode; + + graphNode = init_graph_node_start(gGraphNodePool, NULL); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +// 0x1F: No operation +void geo_layout_cmd_nop3(void) { + gGeoLayoutCommand += 0x10 << CMD_SIZE_SHIFT; +} + +/* + 0x0C: Create zbuffer-toggling scene graph node + cmd+0x01: u8 enableZBuffer (1 = on, 0 = off) +*/ +void geo_layout_cmd_node_master_list(void) { + struct GraphNodeMasterList *graphNode; + + graphNode = init_graph_node_master_list(gGraphNodePool, NULL, cur_geo_cmd_u8(0x01)); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x0D: Create a level of detail graph node, which only renders at a certain + distance interval from the camera. + cmd+0x04: s16 minDistance + cmd+0x06: s16 maxDistance +*/ +void geo_layout_cmd_node_level_of_detail(void) { + struct GraphNodeLevelOfDetail *graphNode; + s16 minDistance = cur_geo_cmd_s16(0x04); + s16 maxDistance = cur_geo_cmd_s16(0x06); + + graphNode = init_graph_node_render_range(gGraphNodePool, NULL, minDistance, maxDistance); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x0E: Create switch-case scene graph node + cmd+0x02: s16 initialSelectedCase + cmd+0x04: GraphNodeFunc caseSelectorFunc + + caseSelectorFunc returns an index which is used to select the child node to render. + Used for animating coins, blinking, color selection, etc. +*/ +void geo_layout_cmd_node_switch_case(void) { + struct GraphNodeSwitchCase *graphNode; + + graphNode = + init_graph_node_switch_case(gGraphNodePool, NULL, + cur_geo_cmd_s16(0x02), // case which is initially selected + 0, + (GraphNodeFunc) cur_geo_cmd_ptr(0x04), // case update function + 0); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x0F: Create a camera scene graph node (GraphNodeCamera). The focus sets the Camera's areaCen position. + cmd+0x02: s16 camera type (changes from course to course) + cmd+0x04: s16 posX + cmd+0x06: s16 posY + cmd+0x08: s16 posZ + cmd+0x0A: s16 focusX + cmd+0x0C: s16 focusY + cmd+0x0E: s16 focusZ + cmd+0x10: GraphNodeFunc func +*/ +void geo_layout_cmd_node_camera(void) { + struct GraphNodeCamera *graphNode; + s16 *cmdPos = (s16 *) &gGeoLayoutCommand[4]; + + Vec3f pos, focus; + + cmdPos = read_vec3s_to_vec3f(pos, cmdPos); + cmdPos = read_vec3s_to_vec3f(focus, cmdPos); + + graphNode = init_graph_node_camera(gGraphNodePool, NULL, pos, focus, + (GraphNodeFunc) cur_geo_cmd_ptr(0x10), cur_geo_cmd_s16(0x02)); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoViews[0] = &graphNode->fnNode.node; + + gGeoLayoutCommand += 0x14 << CMD_SIZE_SHIFT; +} + +/* + 0x10: Create translation & rotation scene graph node with optional display list + cmd+0x01: u8 params + (params & 0x80): if set, enable displayList field and drawingLayer + ((params & 0x70)>>4): fieldLayout + (params & 0x0F): drawingLayer + + fieldLayout == 0: + cmd+0x04: s16 xTranslation + cmd+0x06: s16 yTranslation + cmd+0x08: s16 zTranslation + cmd+0x0A: s16 xRotation + cmd+0x0C: s16 yRotation + cmd+0x0E: s16 zRotation + + fieldLayout == 1: + cmd+0x02: s16 xTranslation + cmd+0x04: s16 yTranslation + cmd+0x06: s16 zTranslation + (rotation gets copied from gVec3sZero) + + fieldLayout == 2: + cmd+0x02: s16 xRotation + cmd+0x04: s16 yRotation + cmd+0x06: s16 zRotation + (translation gets copied from gVec3sZero) + + fieldLayout == 3: + cmd+0x02: s16 yRotation + (translation gets copied from gVec3sZero) + (x and z translation are set to 0) + + [cmd+var: void *displayList] +*/ +void geo_layout_cmd_node_translation_rotation(void) { + struct GraphNodeTranslationRotation *graphNode; + + Vec3s translation, rotation; + + void *displayList = NULL; + s16 drawingLayer = 0; + + s16 params = cur_geo_cmd_u8(0x01); + s16 *cmdPos = (s16 *) gGeoLayoutCommand; + + switch ((params & 0x70) >> 4) { + case 0: + cmdPos = read_vec3s(translation, &cmdPos[2]); + cmdPos = read_vec3s_angle(rotation, cmdPos); + break; + case 1: + cmdPos = read_vec3s(translation, &cmdPos[1]); + vec3s_copy(rotation, gVec3sZero); + break; + case 2: + cmdPos = read_vec3s_angle(rotation, &cmdPos[1]); + vec3s_copy(translation, gVec3sZero); + break; + case 3: + vec3s_copy(translation, gVec3sZero); + vec3s_set(rotation, 0, (cmdPos[1] << 15) / 180, 0); + cmdPos += 2 << CMD_SIZE_SHIFT; + break; + } + + if (params & 0x80) { + displayList = *(void **) &cmdPos[0]; + drawingLayer = params & 0x0F; + cmdPos += 2 << CMD_SIZE_SHIFT; + } + + graphNode = init_graph_node_translation_rotation(gGraphNodePool, NULL, drawingLayer, displayList, + translation, rotation); + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand = (u8 *) cmdPos; +} + +/* + 0x11: Create translation scene graph node with optional display list + cmd+0x01: u8 params + (params & 0x80): if set, enable displayList field and drawingLayer + (params & 0x0F): drawingLayer + cmd+0x02: s16 xTranslation + cmd+0x04: s16 yTranslation + cmd+0x06: s16 zTranslation + [cmd+0x08: void *displayList] +*/ +void geo_layout_cmd_node_translation(void) { + struct GraphNodeTranslation *graphNode; + + Vec3s translation; + + s16 drawingLayer = 0; + s16 params = cur_geo_cmd_u8(0x01); + s16 *cmdPos = (s16 *) gGeoLayoutCommand; + void *displayList = NULL; + + cmdPos = read_vec3s(translation, &cmdPos[1]); + + if (params & 0x80) { + displayList = *(void **) &cmdPos[0]; + drawingLayer = params & 0x0F; + cmdPos += 2 << CMD_SIZE_SHIFT; + } + + graphNode = + init_graph_node_translation(gGraphNodePool, NULL, drawingLayer, displayList, translation); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand = (u8 *) cmdPos; +} + +/* + 0x12: Create ? scene graph node + cmd+0x01: u8 params + (params & 0x80): if set, enable displayList field and drawingLayer + (params & 0x0F): drawingLayer + cmd+0x02: s16 unkX + cmd+0x04: s16 unkY + cmd+0x06: s16 unkZ + [cmd+0x08: void *displayList] +*/ +void geo_layout_cmd_node_rotation(void) { + struct GraphNodeRotation *graphNode; + + Vec3s sp2c; + + s16 drawingLayer = 0; + s16 params = cur_geo_cmd_u8(0x01); + s16 *cmdPos = (s16 *) gGeoLayoutCommand; + void *displayList = NULL; + + cmdPos = read_vec3s_angle(sp2c, &cmdPos[1]); + + if (params & 0x80) { + displayList = *(void **) &cmdPos[0]; + drawingLayer = params & 0x0F; + cmdPos += 2 << CMD_SIZE_SHIFT; + } + + graphNode = init_graph_node_rotation(gGraphNodePool, NULL, drawingLayer, displayList, sp2c); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand = (u8 *) cmdPos; +} + +/* + 0x1D: Create scale scene graph node with optional display list + cmd+0x01: u8 params + (params & 0x80): if set, enable displayList field and drawingLayer + (params & 0x0F): drawingLayer + cmd+0x04: u32 scale (0x10000 = 1.0) + [cmd+0x08: void *displayList] +*/ +void geo_layout_cmd_node_scale(void) { + struct GraphNodeScale *graphNode; + + s16 drawingLayer = 0; + s16 params = cur_geo_cmd_u8(0x01); + f32 scale = cur_geo_cmd_u32(0x04) / 65536.0f; + void *displayList = NULL; + + if (params & 0x80) { + displayList = cur_geo_cmd_ptr(0x08); + drawingLayer = params & 0x0F; + gGeoLayoutCommand += 4 << CMD_SIZE_SHIFT; + } + + graphNode = init_graph_node_scale(gGraphNodePool, NULL, drawingLayer, displayList, scale); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +// 0x1E: No operation +void geo_layout_cmd_nop2(void) { + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x13: Create a scene graph node that is rotated by the object's animation. + cmd+0x01: u8 drawingLayer + cmd+0x02: s16 xTranslation + cmd+0x04: s16 yTranslation + cmd+0x06: s16 zTranslation + cmd+0x08: void *displayList +*/ +void geo_layout_cmd_node_animated_part(void) { + struct GraphNodeAnimatedPart *graphNode; + Vec3s translation; + s32 drawingLayer = cur_geo_cmd_u8(0x01); + void *displayList = cur_geo_cmd_ptr(0x08); + s16 *cmdPos = (s16 *) gGeoLayoutCommand; + + read_vec3s(translation, &cmdPos[1]); + + graphNode = + init_graph_node_animated_part(gGraphNodePool, NULL, drawingLayer, displayList, translation); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT; +} + +/* + 0x14: Create billboarding node with optional display list + cmd+0x01: u8 params + (params & 0x80): if set, enable displayList field and drawingLayer + (params & 0x0F): drawingLayer + cmd+0x02: s16 xTranslation + cmd+0x04: s16 yTranslation + cmd+0x06: s16 zTranslation + [cmd+0x08: void *displayList] +*/ +void geo_layout_cmd_node_billboard(void) { + struct GraphNodeBillboard *graphNode; + Vec3s translation; + s16 drawingLayer = 0; + s16 params = cur_geo_cmd_u8(0x01); + s16 *cmdPos = (s16 *) gGeoLayoutCommand; + void *displayList = NULL; + + cmdPos = read_vec3s(translation, &cmdPos[1]); + + if (params & 0x80) { + displayList = *(void **) &cmdPos[0]; + drawingLayer = params & 0x0F; + cmdPos += 2 << CMD_SIZE_SHIFT; + } + + graphNode = init_graph_node_billboard(gGraphNodePool, NULL, drawingLayer, displayList, translation); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand = (u8 *) cmdPos; +} + +/* + 0x15: Create plain display list scene graph node + cmd+0x01: u8 drawingLayer + cmd+0x04: void *displayList +*/ +void geo_layout_cmd_node_display_list(void) { + struct GraphNodeDisplayList *graphNode; + s32 drawingLayer = cur_geo_cmd_u8(0x01); + void *displayList = cur_geo_cmd_ptr(0x04); + + graphNode = init_graph_node_display_list(gGraphNodePool, NULL, drawingLayer, displayList); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x16: Create shadow scene graph node + cmd+0x02: s16 shadowType + cmd+0x04: s16 shadowSolidity + cmd+0x06: s16 shadowScale +*/ +void geo_layout_cmd_node_shadow(void) { + struct GraphNodeShadow *graphNode; + u8 shadowType = cur_geo_cmd_s16(0x02); + u8 shadowSolidity = cur_geo_cmd_s16(0x04); + s16 shadowScale = cur_geo_cmd_s16(0x06); + + graphNode = init_graph_node_shadow(gGraphNodePool, NULL, shadowScale, shadowSolidity, shadowType); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +// 0x17: Create scene graph node that manages the group of all object nodes +void geo_layout_cmd_node_object_parent(void) { + struct GraphNodeObjectParent *graphNode; + + graphNode = init_graph_node_object_parent(gGraphNodePool, NULL, &gObjParentGraphNode); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x18: Create dynamically generated displaylist scene graph node + cmd+0x02: s16 parameter + cmd+0x04: GraphNodeFunc func +*/ +void geo_layout_cmd_node_generated(void) { + struct GraphNodeGenerated *graphNode; + + graphNode = init_graph_node_generated(gGraphNodePool, NULL, + (GraphNodeFunc) cur_geo_cmd_ptr(0x04), // asm function + cur_geo_cmd_s16(0x02)); // parameter + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x19: Create background scene graph node + cmd+0x02: s16 background // background ID, or RGBA5551 color if backgroundFunc is null + cmd+0x04: GraphNodeFunc backgroundFunc +*/ +void geo_layout_cmd_node_background(void) { + struct GraphNodeBackground *graphNode; + + graphNode = init_graph_node_background( + gGraphNodePool, NULL, + cur_geo_cmd_s16(0x02), // background ID, or RGBA5551 color if asm function is null + (GraphNodeFunc) cur_geo_cmd_ptr(0x04), // asm function + 0); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +// 0x1A: No operation +void geo_layout_cmd_nop(void) { + gGeoLayoutCommand += 0x08 << CMD_SIZE_SHIFT; +} + +/* + 0x1B: Copy the shared children from the object parent from a specific view + to a newly created object parent node. + cmd+0x02: s16 index (of gGeoViews) +*/ +void geo_layout_cmd_copy_view(void) { + struct GraphNodeObjectParent *graphNode; + struct GraphNode *node = NULL; + s16 index = cur_geo_cmd_s16(0x02); + + if (index >= 0) { + node = gGeoViews[index]; + + if (node->type == GRAPH_NODE_TYPE_OBJECT_PARENT) { + node = ((struct GraphNodeObjectParent *) node)->sharedChild; + } else { + node = NULL; + } + } + + graphNode = init_graph_node_object_parent(gGraphNodePool, NULL, node); + + register_scene_graph_node(&graphNode->node); + + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +/* + 0x1C: Create a held object scene graph node + cmd+0x01: u8 unused + cmd+0x02: s16 offsetX + cmd+0x04: s16 offsetY + cmd+0x06: s16 offsetZ + cmd+0x08: GraphNodeFunc nodeFunc +*/ +void geo_layout_cmd_node_held_obj(void) { + struct GraphNodeHeldObject *graphNode; + Vec3s offset; + + read_vec3s(offset, (s16 *) &gGeoLayoutCommand[0x02]); + + graphNode = init_graph_node_held_object( + gGraphNodePool, NULL, NULL, offset, (GraphNodeFunc) cur_geo_cmd_ptr(0x08), cur_geo_cmd_u8(0x01)); + + register_scene_graph_node(&graphNode->fnNode.node); + + gGeoLayoutCommand += 0x0C << CMD_SIZE_SHIFT; +} + +/* + 0x20: Create a scene graph node that specifies for an object the radius that + is used for frustum culling. + cmd+0x02: s16 cullingRadius +*/ +void geo_layout_cmd_node_culling_radius(void) { + struct GraphNodeCullingRadius *graphNode; + graphNode = init_graph_node_culling_radius(gGraphNodePool, NULL, cur_geo_cmd_s16(0x02)); + register_scene_graph_node(&graphNode->node); + gGeoLayoutCommand += 0x04 << CMD_SIZE_SHIFT; +} + +struct GraphNode *process_geo_layout(struct AllocOnlyPool *pool, void *segptr) { + // set by register_scene_graph_node when gCurGraphNodeIndex is 0 + // and gCurRootGraphNode is NULL + gCurRootGraphNode = NULL; + + gGeoNumViews = 0; // number of entries in gGeoViews + + gCurGraphNodeList[0] = 0; + gCurGraphNodeIndex = 0; // incremented by cmd_open_node, decremented by cmd_close_node + + gGeoLayoutStackIndex = 2; + gGeoLayoutReturnIndex = 2; // stack index is often copied here? + + gGeoLayoutCommand = segmented_to_virtual(segptr); + + gGraphNodePool = pool; + + gGeoLayoutStack[0] = 0; + gGeoLayoutStack[1] = 0; + + while (gGeoLayoutCommand != NULL) { + GeoLayoutJumpTable[gGeoLayoutCommand[0x00]](); + } + + return gCurRootGraphNode; +} diff --git a/src/engine/geo_layout.h b/src/engine/geo_layout.h new file mode 100644 index 0000000..744f06d --- /dev/null +++ b/src/engine/geo_layout.h @@ -0,0 +1,87 @@ +#ifndef GEO_LAYOUT_H +#define GEO_LAYOUT_H + +#include "../include/PR/ultratypes.h" + +#include "../memory.h" +#include "../include/macros.h" +#include "../include/types.h" + +#define GEO_CMD_FLAGS_RESET 0 +#define GEO_CMD_FLAGS_SET 1 +#define GEO_CMD_FLAGS_CLEAR 2 + +#define CMD_SIZE_SHIFT (sizeof(void *) >> 3) +#define CMD_PROCESS_OFFSET(offset) (((offset) & 3) | (((offset) & ~3) << CMD_SIZE_SHIFT)) + +#define cur_geo_cmd_u8(offset) \ + (gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)]) + +#define cur_geo_cmd_s16(offset) \ + (*(s16 *) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)]) + +#define cur_geo_cmd_s32(offset) \ + (*(s32 *) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)]) + +#define cur_geo_cmd_u32(offset) \ + (*(u32 *) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)]) + +#define cur_geo_cmd_ptr(offset) \ + (*(void **) &gGeoLayoutCommand[CMD_PROCESS_OFFSET(offset)]) + +extern struct AllocOnlyPool *gGraphNodePool; +extern struct GraphNode *gCurRootGraphNode; +extern UNUSED s32 D_8038BCA8; +extern struct GraphNode **gGeoViews; +extern u16 gGeoNumViews; +extern uintptr_t gGeoLayoutStack[]; +extern struct GraphNode *gCurGraphNodeList[]; +extern s16 gCurGraphNodeIndex; +extern s16 gGeoLayoutStackIndex; +extern UNUSED s16 D_8038BD7C; +extern s16 gGeoLayoutReturnIndex; +extern u8 *gGeoLayoutCommand; +extern struct GraphNode gObjParentGraphNode; + +extern struct AllocOnlyPool *D_8038BCA0; +extern struct GraphNode *D_8038BCA4; +extern s16 D_8038BD78; +extern struct GraphNode *D_8038BCF8[]; + +void geo_layout_cmd_branch_and_link(void); +void geo_layout_cmd_end(void); +void geo_layout_cmd_branch(void); +void geo_layout_cmd_return(void); +void geo_layout_cmd_open_node(void); +void geo_layout_cmd_close_node(void); +void geo_layout_cmd_assign_as_view(void); +void geo_layout_cmd_update_node_flags(void); +void geo_layout_cmd_node_root(void); +void geo_layout_cmd_node_ortho_projection(void); +void geo_layout_cmd_node_perspective(void); +void geo_layout_cmd_node_start(void); +void geo_layout_cmd_nop3(void); +void geo_layout_cmd_node_master_list(void); +void geo_layout_cmd_node_level_of_detail(void); +void geo_layout_cmd_node_switch_case(void); +void geo_layout_cmd_node_camera(void); +void geo_layout_cmd_node_translation_rotation(void); +void geo_layout_cmd_node_translation(void); +void geo_layout_cmd_node_rotation(void); +void geo_layout_cmd_node_scale(void); +void geo_layout_cmd_nop2(void); +void geo_layout_cmd_node_animated_part(void); +void geo_layout_cmd_node_billboard(void); +void geo_layout_cmd_node_display_list(void); +void geo_layout_cmd_node_shadow(void); +void geo_layout_cmd_node_object_parent(void); +void geo_layout_cmd_node_generated(void); +void geo_layout_cmd_node_background(void); +void geo_layout_cmd_nop(void); +void geo_layout_cmd_copy_view(void); +void geo_layout_cmd_node_held_obj(void); +void geo_layout_cmd_node_culling_radius(void); + +struct GraphNode *process_geo_layout(struct AllocOnlyPool *a0, void *segptr); + +#endif // GEO_LAYOUT_H diff --git a/src/engine/graph_node.c b/src/engine/graph_node.c new file mode 100644 index 0000000..6424bdd --- /dev/null +++ b/src/engine/graph_node.c @@ -0,0 +1,898 @@ +#include "../include/sm64.h" + +#include "../game/level_update.h" +#include "math_util.h" +//#include "game/memory.h" +#include "graph_node.h" +#include "../game/rendering_graph_node.h" +#include "../game/area.h" +#include "geo_layout.h" +#include "../shim.h" + +// unused Mtx(s) +s16 identityMtx[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; +s16 zeroMtx[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; + +Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f }; +Vec3s gVec3sZero = { 0, 0, 0 }; +Vec3f gVec3fOne = { 1.0f, 1.0f, 1.0f }; +UNUSED Vec3s gVec3sOne = { 1, 1, 1 }; + +/** + * Initialize a geo node with a given type. Sets all links such that there + * are no siblings, parent or children for this node. + */ +void init_scene_graph_node_links(struct GraphNode *graphNode, s32 type) { + graphNode->type = type; + graphNode->flags = GRAPH_RENDER_ACTIVE; + graphNode->prev = graphNode; + graphNode->next = graphNode; + graphNode->parent = NULL; + graphNode->children = NULL; +} + +/** + * Allocated and returns a newly created root node + */ +struct GraphNodeRoot *init_graph_node_root(struct AllocOnlyPool *pool, struct GraphNodeRoot *graphNode, + s16 areaIndex, s16 x, s16 y, s16 width, s16 height) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRoot)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROOT); + + graphNode->areaIndex = areaIndex; + graphNode->unk15 = 0; + graphNode->x = x; + graphNode->y = y; + graphNode->width = width; + graphNode->height = height; + graphNode->views = NULL; + graphNode->numViews = 0; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created otrhographic projection node + */ +struct GraphNodeOrthoProjection * +init_graph_node_ortho_projection(struct AllocOnlyPool *pool, struct GraphNodeOrthoProjection *graphNode, + f32 scale) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeOrthoProjection)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ORTHO_PROJECTION); + graphNode->scale = scale; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created perspective node + */ +struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool, + struct GraphNodePerspective *graphNode, + f32 fov, s16 near, s16 far, + GraphNodeFunc nodeFunc, s32 unused) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodePerspective)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_PERSPECTIVE); + + graphNode->fov = fov; + graphNode->near = near; + graphNode->far = far; + graphNode->fnNode.func = nodeFunc; + graphNode->unused = unused; + + if (nodeFunc != NULL) { + nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool); + } + } + + return graphNode; +} + +/** + * Allocates and returns a newly created start node + */ +struct GraphNodeStart *init_graph_node_start(struct AllocOnlyPool *pool, + struct GraphNodeStart *graphNode) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeStart)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_START); + } + + return graphNode; +} + +/** + * Allocates and returns a newly created master list node + */ +struct GraphNodeMasterList *init_graph_node_master_list(struct AllocOnlyPool *pool, + struct GraphNodeMasterList *graphNode, s16 on) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeMasterList)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_MASTER_LIST); + + if (on) { + graphNode->node.flags |= GRAPH_RENDER_Z_BUFFER; + } + } + + return graphNode; +} + +/** + * Allocates and returns a newly created render range node + */ +struct GraphNodeLevelOfDetail *init_graph_node_render_range(struct AllocOnlyPool *pool, + struct GraphNodeLevelOfDetail *graphNode, + s16 minDistance, s16 maxDistance) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeLevelOfDetail)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_LEVEL_OF_DETAIL); + graphNode->minDistance = minDistance; + graphNode->maxDistance = maxDistance; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created switch case node + */ +struct GraphNodeSwitchCase *init_graph_node_switch_case(struct AllocOnlyPool *pool, + struct GraphNodeSwitchCase *graphNode, + s16 numCases, s16 selectedCase, + GraphNodeFunc nodeFunc, s32 unused) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeSwitchCase)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_SWITCH_CASE); + graphNode->numCases = numCases; + graphNode->selectedCase = selectedCase; + graphNode->fnNode.func = nodeFunc; + graphNode->unused = unused; + + if (nodeFunc != NULL) { + nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool); + } + } + + return graphNode; +} + +/** + * Allocates and returns a newly created camera node + */ +struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool, + struct GraphNodeCamera *graphNode, f32 *pos, + f32 *focus, GraphNodeFunc func, s32 mode) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCamera)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_CAMERA); + vec3f_copy(graphNode->pos, pos); + vec3f_copy(graphNode->focus, focus); + graphNode->fnNode.func = func; + graphNode->config.mode = mode; + graphNode->roll = 0; + graphNode->rollScreen = 0; + + if (func != NULL) { + func(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool); + } + } + + return graphNode; +} + +/** + * Allocates and returns a newly created translation rotation node + */ +struct GraphNodeTranslationRotation * +init_graph_node_translation_rotation(struct AllocOnlyPool *pool, + struct GraphNodeTranslationRotation *graphNode, s32 drawingLayer, + void *displayList, Vec3s translation, Vec3s rotation) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslationRotation)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION_ROTATION); + + vec3s_copy(graphNode->translation, translation); + vec3s_copy(graphNode->rotation, rotation); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created translation node + */ +struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool, + struct GraphNodeTranslation *graphNode, + s32 drawingLayer, void *displayList, + Vec3s translation) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeTranslation)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_TRANSLATION); + + vec3s_copy(graphNode->translation, translation); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created rotation node + */ +struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool, + struct GraphNodeRotation *graphNode, + s32 drawingLayer, void *displayList, + Vec3s rotation) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeRotation)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROTATION); + vec3s_copy(graphNode->rotation, rotation); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created scaling node + */ +struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool, + struct GraphNodeScale *graphNode, s32 drawingLayer, + void *displayList, f32 scale) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeScale)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SCALE); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->scale = scale; + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created object node + */ +struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool, + struct GraphNodeObject *graphNode, + struct GraphNode *sharedChild, Vec3f pos, Vec3s angle, + Vec3f scale) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObject)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT); + vec3f_copy(graphNode->pos, pos); + vec3f_copy(graphNode->scale, scale); + vec3s_copy(graphNode->angle, angle); +#ifdef USE_SYSTEM_MALLOC + // To avoid uninitialised memory usage in audio code + vec3f_copy(graphNode->cameraToObject, gVec3fZero); +#endif + graphNode->sharedChild = sharedChild; + graphNode->throwMatrix = NULL; + graphNode->animInfo.animID = 0; + graphNode->animInfo.curAnim = NULL; + graphNode->animInfo.animFrame = 0; + graphNode->animInfo.animFrameAccelAssist = 0; + graphNode->animInfo.animAccel = 0x10000; + graphNode->animInfo.animTimer = 0; + graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created frustum culling radius node + */ +struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool, + struct GraphNodeCullingRadius *graphNode, + s16 radius) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeCullingRadius)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_CULLING_RADIUS); + graphNode->cullingRadius = radius; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created animated part node + */ +struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool, + struct GraphNodeAnimatedPart *graphNode, + s32 drawingLayer, void *displayList, + Vec3s translation) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeAnimatedPart)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ANIMATED_PART); + vec3s_copy(graphNode->translation, translation); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created billboard node + */ +struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool, + struct GraphNodeBillboard *graphNode, + s32 drawingLayer, void *displayList, + Vec3s translation) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBillboard)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD); + vec3s_copy(graphNode->translation, translation); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created displaylist node + */ +struct GraphNodeDisplayList *init_graph_node_display_list(struct AllocOnlyPool *pool, + struct GraphNodeDisplayList *graphNode, + s32 drawingLayer, void *displayList) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeDisplayList)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_DISPLAY_LIST); + graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF); + graphNode->displayList = displayList; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created shadow node + */ +struct GraphNodeShadow *init_graph_node_shadow(struct AllocOnlyPool *pool, + struct GraphNodeShadow *graphNode, s16 shadowScale, + u8 shadowSolidity, u8 shadowType) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeShadow)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_SHADOW); + graphNode->shadowScale = shadowScale; + graphNode->shadowSolidity = shadowSolidity; + graphNode->shadowType = shadowType; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created object parent node + */ +struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool, + struct GraphNodeObjectParent *graphNode, + struct GraphNode *sharedChild) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeObjectParent)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT_PARENT); + graphNode->sharedChild = sharedChild; + } + + return graphNode; +} + +/** + * Allocates and returns a newly created generated node + */ +struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool, + struct GraphNodeGenerated *graphNode, + GraphNodeFunc gfxFunc, s32 parameter) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeGenerated)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_GENERATED_LIST); + graphNode->fnNode.func = gfxFunc; + graphNode->parameter = parameter; + + if (gfxFunc != NULL) { + gfxFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool); + } + } + + return graphNode; +} + +/** + * Allocates and returns a newly created background node + */ +struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool, + struct GraphNodeBackground *graphNode, + u16 background, GraphNodeFunc backgroundFunc, + s32 zero) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeBackground)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_BACKGROUND); + + graphNode->background = (background << 16) | background; + graphNode->fnNode.func = backgroundFunc; + graphNode->unused = zero; // always 0, unused + + if (backgroundFunc != NULL) { + backgroundFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool); + } + } + + return graphNode; +} + +/** + * Allocates and returns a newly created held object node + */ +struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool, + struct GraphNodeHeldObject *graphNode, + struct Object *objNode, + Vec3s translation, + GraphNodeFunc nodeFunc, s32 playerIndex) { + if (pool != NULL) { + graphNode = alloc_only_pool_alloc(pool, sizeof(struct GraphNodeHeldObject)); + } + + if (graphNode != NULL) { + init_scene_graph_node_links(&graphNode->fnNode.node, GRAPH_NODE_TYPE_HELD_OBJ); + vec3s_copy(graphNode->translation, translation); + graphNode->objNode = objNode; + graphNode->fnNode.func = nodeFunc; + graphNode->playerIndex = playerIndex; + + if (nodeFunc != NULL) { + nodeFunc(GEO_CONTEXT_CREATE, &graphNode->fnNode.node, pool); + } + } + + return graphNode; +} + +/** + * Adds 'childNode' to the end of the list children from 'parent' + */ +struct GraphNode *geo_add_child(struct GraphNode *parent, struct GraphNode *childNode) { + struct GraphNode *parentFirstChild; + struct GraphNode *parentLastChild; + + if (childNode != NULL) { + childNode->parent = parent; + parentFirstChild = parent->children; + + if (parentFirstChild == NULL) { + parent->children = childNode; + childNode->prev = childNode; + childNode->next = childNode; + } else { + parentLastChild = parentFirstChild->prev; + childNode->prev = parentLastChild; + childNode->next = parentFirstChild; + parentFirstChild->prev = childNode; + parentLastChild->next = childNode; + } + } + + return childNode; +} + +/** + * Remove a node from the scene graph. It changes the links with its + * siblings and with its parent, it doesn't deallocate the memory + * since geo nodes are allocated in a pointer-bumping pool that + * gets thrown out when changing areas. + */ +struct GraphNode *geo_remove_child(struct GraphNode *graphNode) { + struct GraphNode *parent; + struct GraphNode **firstChild; + + parent = graphNode->parent; + firstChild = &parent->children; + + // Remove link with siblings + graphNode->prev->next = graphNode->next; + graphNode->next->prev = graphNode->prev; + + // If this node was the first child, a new first child must be chosen + if (*firstChild == graphNode) { + // The list is circular, so this checks whether it was the only child + if (graphNode->next == graphNode) { + *firstChild = NULL; // Parent has no children anymore + } else { + *firstChild = graphNode->next; // Choose a new first child + } + } + + return parent; +} + +/** + * Reorders the given node so it's the first child of its parent. + * This is called on the Mario object when he is spawned. That's why Mario's + * object is always drawn before any other objects. (Note that the geo order + * is independent from processing group order, where Mario is not first.) + */ +struct GraphNode *geo_make_first_child(struct GraphNode *newFirstChild) { + struct GraphNode *lastSibling; + struct GraphNode *parent; + struct GraphNode **firstChild; + + parent = newFirstChild->parent; + firstChild = &parent->children; + + if (*firstChild != newFirstChild) { + if ((*firstChild)->prev != newFirstChild) { + newFirstChild->prev->next = newFirstChild->next; + newFirstChild->next->prev = newFirstChild->prev; + lastSibling = (*firstChild)->prev; + newFirstChild->prev = lastSibling; + newFirstChild->next = *firstChild; + (*firstChild)->prev = newFirstChild; + lastSibling->next = newFirstChild; + } + *firstChild = newFirstChild; + } + + return parent; +} + +/** + * Helper function for geo_call_global_function_nodes that recursively + * traverses the scene graph and calls the functions of global nodes. + */ +void geo_call_global_function_nodes_helper(struct GraphNode *graphNode, s32 callContext) { + struct GraphNode **globalPtr; + struct GraphNode *curNode; + struct FnGraphNode *asFnNode; + + curNode = graphNode; + + do { + asFnNode = (struct FnGraphNode *) curNode; + + if (curNode->type & GRAPH_NODE_TYPE_FUNCTIONAL) { + if (asFnNode->func != NULL) { + asFnNode->func(callContext, curNode, NULL); + } + } + + if (curNode->children != NULL) { + switch (curNode->type) { + case GRAPH_NODE_TYPE_MASTER_LIST: + globalPtr = (struct GraphNode **) &gCurGraphNodeMasterList; + break; + case GRAPH_NODE_TYPE_PERSPECTIVE: + globalPtr = (struct GraphNode **) &gCurGraphNodeCamFrustum; + break; + case GRAPH_NODE_TYPE_CAMERA: + globalPtr = (struct GraphNode **) &gCurGraphNodeCamera; + break; + case GRAPH_NODE_TYPE_OBJECT: + globalPtr = (struct GraphNode **) &gCurGraphNodeObject; + break; + default: + globalPtr = NULL; + break; + } + + if (globalPtr != NULL) { + *globalPtr = curNode; + } + + geo_call_global_function_nodes_helper(curNode->children, callContext); + + if (globalPtr != NULL) { + *globalPtr = NULL; + } + } + } while ((curNode = curNode->next) != graphNode); +} + +/** + * Call the update functions of geo nodes that are stored in global variables. + * These variables include gCurGraphNodeMasterList, gCurGraphNodeCamFrustum, + * gCurGraphNodeCamera and gCurGraphNodeObject. + * callContext is one of the GEO_CONTEXT_ defines. + * The graphNode argument should be of type GraphNodeRoot. + */ +void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 callContext) { + if (graphNode->flags & GRAPH_RENDER_ACTIVE) { + gCurGraphNodeRoot = (struct GraphNodeRoot *) graphNode; + + if (graphNode->children != NULL) { + geo_call_global_function_nodes_helper(graphNode->children, callContext); + } + + gCurGraphNodeRoot = 0; + } +} + +/** + * When objects are cleared, this is called on all object nodes (loaded or unloaded). + */ +void geo_reset_object_node(struct GraphNodeObject *graphNode) { + init_graph_node_object(NULL, graphNode, 0, gVec3fZero, gVec3sZero, gVec3fOne); + + geo_add_child(&gObjParentGraphNode, &graphNode->node); + graphNode->node.flags &= ~GRAPH_RENDER_ACTIVE; +} + +/** + * Initialize an object node using the given parameters + */ +void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle) { + vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f); + vec3f_copy(graphNode->pos, pos); + vec3s_copy(graphNode->angle, angle); + + graphNode->sharedChild = sharedChild; + graphNode->unk4C = 0; + graphNode->throwMatrix = NULL; + graphNode->animInfo.curAnim = NULL; + + graphNode->node.flags |= GRAPH_RENDER_ACTIVE; + graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE; + graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION; + graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD; +} + +/** + * Initialize and object node using the given SpawnInfo struct + */ +void geo_obj_init_spawninfo(struct GraphNodeObject *graphNode, struct SpawnInfo *spawn) { + vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f); + vec3s_copy(graphNode->angle, spawn->startAngle); + + graphNode->pos[0] = (f32) spawn->startPos[0]; + graphNode->pos[1] = (f32) spawn->startPos[1]; + graphNode->pos[2] = (f32) spawn->startPos[2]; + + graphNode->areaIndex = spawn->areaIndex; + graphNode->activeAreaIndex = spawn->activeAreaIndex; + graphNode->sharedChild = spawn->unk18; + graphNode->unk4C = spawn; + graphNode->throwMatrix = NULL; + graphNode->animInfo.curAnim = 0; + + graphNode->node.flags |= GRAPH_RENDER_ACTIVE; + graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE; + graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION; + graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD; +} + +/** + * Initialize the animation of an object node + */ +void geo_obj_init_animation(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr) { + struct Animation **animSegmented = segmented_to_virtual(animPtrAddr); + struct Animation *anim = segmented_to_virtual(*animSegmented); + + if (graphNode->animInfo.curAnim != anim) { + graphNode->animInfo.curAnim = anim; + graphNode->animInfo.animFrame = anim->startFrame + ((anim->flags & ANIM_FLAG_FORWARD) ? 1 : -1); + graphNode->animInfo.animAccel = 0; + graphNode->animInfo.animYTrans = 0; + } +} + +/** + * Initialize the animation of an object node + */ +void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr, u32 animAccel) { + struct Animation **animSegmented = segmented_to_virtual(animPtrAddr); + struct Animation *anim = segmented_to_virtual(*animSegmented); + + if (graphNode->animInfo.curAnim != anim) { + graphNode->animInfo.curAnim = anim; + graphNode->animInfo.animYTrans = 0; + graphNode->animInfo.animFrameAccelAssist = + (anim->startFrame << 16) + ((anim->flags & ANIM_FLAG_FORWARD) ? animAccel : -animAccel); + graphNode->animInfo.animFrame = graphNode->animInfo.animFrameAccelAssist >> 16; + } + + graphNode->animInfo.animAccel = animAccel; +} + +/** + * Retrieves an index into animation data based on the attribute pointer + * An attribute is an x-, y- or z-component of the translation / rotation for a part + * Each attribute is a pair of s16's, where the first s16 represents the maximum frame + * and the second s16 the actual index. This index can be used to index in the array + * with actual animation values. + */ +s32 retrieve_animation_index(s32 frame, u16 **attributes) { + s32 result; + + if (frame < (*attributes)[0]) { + result = (*attributes)[1] + frame; + } else { + result = (*attributes)[1] + (*attributes)[0] - 1; + } + + *attributes += 2; + + return result; +} + +/** + * Update the animation frame of an object. The animation flags determine + * whether it plays forwards or backwards, and whether it stops or loops at + * the end etc. + */ +s16 geo_update_animation_frame(struct AnimInfo *obj, s32 *accelAssist) { + s32 result; + struct Animation *anim; + + anim = obj->curAnim; + + if (obj->animTimer == gAreaUpdateCounter || anim->flags & ANIM_FLAG_2) { + if (accelAssist != NULL) { + accelAssist[0] = obj->animFrameAccelAssist; + } + + return obj->animFrame; + } + + if (anim->flags & ANIM_FLAG_FORWARD) { + if (obj->animAccel) { + result = obj->animFrameAccelAssist - obj->animAccel; + } else { + result = (obj->animFrame - 1) << 16; + } + + if (GET_HIGH_S16_OF_32(result) < anim->loopStart) { + if (anim->flags & ANIM_FLAG_NOLOOP) { + SET_HIGH_S16_OF_32(result, anim->loopStart); + } else { + SET_HIGH_S16_OF_32(result, anim->loopEnd - 1); + } + } + } else { + if (obj->animAccel != 0) { + result = obj->animFrameAccelAssist + obj->animAccel; + } else { + result = (obj->animFrame + 1) << 16; + } + + if (GET_HIGH_S16_OF_32(result) >= anim->loopEnd) { + if (anim->flags & ANIM_FLAG_NOLOOP) { + SET_HIGH_S16_OF_32(result, anim->loopEnd - 1); + } else { + SET_HIGH_S16_OF_32(result, anim->loopStart); + } + } + } + + if (accelAssist != 0) { + accelAssist[0] = result; + } + + return GET_HIGH_S16_OF_32(result); +} + +/** + * Unused function to retrieve an object's current animation translation + * Assumes that it has x, y and z data in animations, which isn't always the + * case since some animation types only have vertical or lateral translation. + * This might have been used for positioning the shadow under an object, which + * currently happens in-line in geo_process_shadow where it also accounts for + * animations without lateral translation. + */ +void geo_retreive_animation_translation(struct GraphNodeObject *obj, Vec3f position) { + struct Animation *animation = obj->animInfo.curAnim; + u16 *attribute; + s16 *values; + s16 frame; + + if (animation != NULL) { + attribute = segmented_to_virtual((void *) animation->index); + values = segmented_to_virtual((void *) animation->values); + + frame = obj->animInfo.animFrame; + + if (frame < 0) { + frame = 0; + } + + if (1) // ? necessary to match + { + position[0] = (f32) values[retrieve_animation_index(frame, &attribute)]; + position[1] = (f32) values[retrieve_animation_index(frame, &attribute)]; + position[2] = (f32) values[retrieve_animation_index(frame, &attribute)]; + } + } else { + vec3f_set(position, 0, 0, 0); + } +} + +/** + * Unused function to find the root of the geo node tree, which should be a + * GraphNodeRoot. If it is not for some reason, null is returned. + */ +struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode) { + struct GraphNodeRoot *resGraphNode = NULL; + + while (graphNode->parent != NULL) { + graphNode = graphNode->parent; + } + + if (graphNode->type == GRAPH_NODE_TYPE_ROOT) { + resGraphNode = (struct GraphNodeRoot *) graphNode; + } + + return resGraphNode; +} diff --git a/src/engine/graph_node.h b/src/engine/graph_node.h new file mode 100644 index 0000000..925bbcc --- /dev/null +++ b/src/engine/graph_node.h @@ -0,0 +1,434 @@ +#ifndef GRAPH_NODE_H +#define GRAPH_NODE_H + +#include "../include/PR/ultratypes.h" +#include "../include/PR/gbi.h" + +#include "../include/types.h" +#include "../memory.h" + +#define GRAPH_RENDER_ACTIVE (1 << 0) +#define GRAPH_RENDER_CHILDREN_FIRST (1 << 1) +#define GRAPH_RENDER_BILLBOARD (1 << 2) +#define GRAPH_RENDER_Z_BUFFER (1 << 3) +#define GRAPH_RENDER_INVISIBLE (1 << 4) +#define GRAPH_RENDER_HAS_ANIMATION (1 << 5) + +// Whether the node type has a function pointer of type GraphNodeFunc +#define GRAPH_NODE_TYPE_FUNCTIONAL 0x100 + +// Type used for Bowser and an unused geo function in obj_behaviors.c +#define GRAPH_NODE_TYPE_400 0x400 + +// The discriminant for different types of geo nodes +#define GRAPH_NODE_TYPE_ROOT 0x001 +#define GRAPH_NODE_TYPE_ORTHO_PROJECTION 0x002 +#define GRAPH_NODE_TYPE_PERSPECTIVE (0x003 | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_MASTER_LIST 0x004 +#define GRAPH_NODE_TYPE_START 0x00A +#define GRAPH_NODE_TYPE_LEVEL_OF_DETAIL 0x00B +#define GRAPH_NODE_TYPE_SWITCH_CASE (0x00C | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_CAMERA (0x014 | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_TRANSLATION_ROTATION 0x015 +#define GRAPH_NODE_TYPE_TRANSLATION 0x016 +#define GRAPH_NODE_TYPE_ROTATION 0x017 +#define GRAPH_NODE_TYPE_OBJECT 0x018 +#define GRAPH_NODE_TYPE_ANIMATED_PART 0x019 +#define GRAPH_NODE_TYPE_BILLBOARD 0x01A +#define GRAPH_NODE_TYPE_DISPLAY_LIST 0x01B +#define GRAPH_NODE_TYPE_SCALE 0x01C +#define GRAPH_NODE_TYPE_SHADOW 0x028 +#define GRAPH_NODE_TYPE_OBJECT_PARENT 0x029 +#define GRAPH_NODE_TYPE_GENERATED_LIST (0x02A | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_BACKGROUND (0x02C | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_HELD_OBJ (0x02E | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_CULLING_RADIUS 0x02F + +// The number of master lists. A master list determines the order and render +// mode with which display lists are drawn. +#define GFX_NUM_MASTER_LISTS 8 + +// Passed as first argument to a GraphNodeFunc to give information about in +// which context it was called and what it is expected to do. +#define GEO_CONTEXT_CREATE 0 // called when node is created from a geo command +#define GEO_CONTEXT_RENDER 1 // called from rendering_graph_node.c +#define GEO_CONTEXT_AREA_UNLOAD 2 // called when unloading an area +#define GEO_CONTEXT_AREA_LOAD 3 // called when loading an area +#define GEO_CONTEXT_AREA_INIT 4 // called when initializing the 8 areas +#define GEO_CONTEXT_HELD_OBJ 5 // called when processing a GraphNodeHeldObj + +// The signature for a function stored in a geo node +// The context argument depends on the callContext: +// - for GEO_CONTEXT_CREATE it is the AllocOnlyPool from which the node was allocated +// - for GEO_CONTEXT_RENDER or GEO_CONTEXT_HELD_OBJ it is the top of the float matrix stack with type Mat4 +// - for GEO_CONTEXT_AREA_* it is the root geo node +typedef Gfx *(*GraphNodeFunc)(s32 callContext, struct GraphNode *node, void *context); + +/** An extension of a graph node that includes a function pointer. + * Many graph node types have an update function that gets called + * when they are processed. + */ +struct FnGraphNode +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ GraphNodeFunc func; +}; + +/** The very root of the geo tree. Specifies the viewport. + */ +struct GraphNodeRoot +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ u8 areaIndex; + /*0x15*/ s8 unk15; // ? + /*0x16*/ s16 x; + /*0x18*/ s16 y; + /*0x1A*/ s16 width; // half width, 160 + /*0x1C*/ s16 height; // half height + /*0x1E*/ s16 numViews; // number of entries in mystery array + /*0x20*/ struct GraphNode **views; +}; + +/** A node that sets up an orthographic projection based on the global + * root node. Used to draw the skybox image. + */ +struct GraphNodeOrthoProjection +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ f32 scale; +}; + +/** A node that sets up a perspective projection. Used for drawing the + * game world. It does not set up the camera position, that is done by + * the child of this node, which has type GraphNodeCamera. + */ +struct GraphNodePerspective +{ + /*0x00*/ struct FnGraphNode fnNode; + /*0x18*/ s32 unused; + /*0x1C*/ f32 fov; // horizontal field of view in degrees + /*0x20*/ s16 near; // near clipping plane + /*0x22*/ s16 far; // far clipping plane +}; + +/** An entry in the master list. It is a linked list of display lists + * carrying a transformation matrix. + */ +struct DisplayListNode +{ + Mtx *transform; + void *displayList; + struct DisplayListNode *next; +}; + +/** GraphNode that manages the 8 top-level display lists that will be drawn + * Each list has its own render mode, so for example water is drawn in a + * different master list than opaque objects. + * It also sets the z-buffer on before rendering and off after. + */ +struct GraphNodeMasterList +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ struct DisplayListNode *listHeads[GFX_NUM_MASTER_LISTS]; + /*0x34*/ struct DisplayListNode *listTails[GFX_NUM_MASTER_LISTS]; +}; + +/** Simply used as a parent to group multiple children. + * Does not have any additional functionality. + */ +struct GraphNodeStart +{ + /*0x00*/ struct GraphNode node; +}; + +/** GraphNode that only renders its children if the current transformation matrix + * has a z-translation (in camera space) greater than minDistance and less than + * maxDistance. + * Usage examples: Mario has three level's of detail: Normal, low-poly arms only, and fully low-poly + * The tower in Whomp's fortress has two levels of detail. + */ +struct GraphNodeLevelOfDetail +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ s16 minDistance; + /*0x16*/ s16 maxDistance; +}; + +/** GraphNode that renders exactly one of its children. + * Which one is rendered is determined by the field 'selectedCase' + * which is set in the node's function. + * Usage examples: room visibility, coin animation, blinking, Mario's power-up / hand pose / cap + */ +struct GraphNodeSwitchCase +{ + /*0x00*/ struct FnGraphNode fnNode; + /*0x18*/ s32 unused; + /*0x1C*/ s16 numCases; + /*0x1E*/ s16 selectedCase; +}; + +/** + * GraphNode that specifies the location and aim of the camera. + * When the roll is 0, the up vector is (0, 1, 0). + */ +struct GraphNodeCamera +{ + /*0x00*/ struct FnGraphNode fnNode; + /*0x18*/ union { + // When the node is created, a mode is assigned to the node. + // Later in geo_camera_main a Camera is allocated, + // the mode is passed to the struct, and the field is overridden + // by a pointer to the struct. Gotta save those 4 bytes. + s32 mode; + struct Camera *camera; + } config; + /*0x1C*/ Vec3f pos; + /*0x28*/ Vec3f focus; + /*0x34*/ Mat4 *matrixPtr; // pointer to look-at matrix of this camera as a Mat4 + /*0x38*/ s16 roll; // roll in look at matrix. Doesn't account for light direction unlike rollScreen. + /*0x3A*/ s16 rollScreen; // rolls screen while keeping the light direction consistent +}; + +/** GraphNode that translates and rotates its children. + * Usage example: wing cap wings. + * There is a dprint function that sets the translation and rotation values + * based on the ENEMYINFO array. + * The display list can be null, in which case it won't draw anything itself. + */ +struct GraphNodeTranslationRotation +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; + /*0x18*/ Vec3s translation; + /*0x1E*/ Vec3s rotation; +}; + +/** GraphNode that translates itself and its children. + * Usage example: SUPER MARIO logo letters in debug level select. + * The display list can be null, in which case it won't draw anything itself. + */ +struct GraphNodeTranslation +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; + /*0x18*/ Vec3s translation; + u8 pad1E[2]; +}; + +/** GraphNode that rotates itself and its children. + * Usage example: Mario torso / head rotation. Its parameters are dynamically + * set by a parent script node in that case. + * The display list can be null, in which case it won't draw anything itself. + */ +struct GraphNodeRotation +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; + /*0x18*/ Vec3s rotation; + u8 pad1E[2]; +}; + +/** GraphNode part that transforms itself and its children based on animation + * data. This animation data is not stored in the node itself but in global + * variables that are set when object nodes are processed if the object has + * animation. + * Used for Mario, enemies and anything else with animation data. + * The display list can be null, in which case it won't draw anything itself. + */ +struct GraphNodeAnimatedPart +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; + /*0x18*/ Vec3s translation; +}; + +/** A GraphNode that draws a display list rotated in a way to always face the + * camera. Note that if the entire object is a billboard (like a coin or 1-up) + * then it simply sets the billboard flag for the entire object, this node is + * used for billboard parts (like a chuckya or goomba body). + */ +struct GraphNodeBillboard +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; + /*0x18*/ Vec3s translation; +}; + +/** A GraphNode that simply draws a display list without doing any + * transformation beforehand. It does inherit the parent's transformation. + */ +struct GraphNodeDisplayList +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; +}; + +/** GraphNode part that scales itself and its children. + * Usage example: Mario's fist or shoe, which grows when attacking. This can't + * be done with an animated part sine animation data doesn't support scaling. + * Note that many scaling animations (like a goomba getting stomped) happen on + * the entire object. This node is only used when a single part needs to be scaled. + * There is also a level command that scales the entire level, used for THI. + * The display list can be null, in which case it won't draw anything itself. + */ +struct GraphNodeScale +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ void *displayList; + /*0x18*/ f32 scale; +}; + +/** GraphNode that draws a shadow under an object. + * Every object starts with a shadow node. + * The shadow type determines the shape (round or rectangular), vertices (4 or 9) + * and other features. + */ +struct GraphNodeShadow +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ s16 shadowScale; // diameter (when a circle) or side (when a square) of shadow + /*0x16*/ u8 shadowSolidity; // opacity of shadow, 255 = opaque + /*0x17*/ u8 shadowType; // see ShadowType enum in shadow.h +}; + +/** GraphNode that contains as its sharedChild a group node containing all + * object nodes. + */ +struct GraphNodeObjectParent +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ struct GraphNode *sharedChild; +}; + +/** GraphNode that draws display lists not directly in memory but generated by + * a function. + * Used for wobbling paintings, water, environment effects. + * It might not draw anything, it could also just update something. + * For example: there is a node that stops water flow when the game is paused. + * The parameter field gives extra context info. For shifting sand or paintings, + * it can determine which texture to use. + */ +struct GraphNodeGenerated +{ + /*0x00*/ struct FnGraphNode fnNode; + /*0x18*/ u32 parameter; // extra context for the function +}; + +/** GraphNode that draws a background image or a rectangle of a color. + * Drawn in an orthographic projection, used for skyboxes. + */ +struct GraphNodeBackground +{ + /*0x00*/ struct FnGraphNode fnNode; + /*0x18*/ s32 unused; + /*0x1C*/ s32 background; // background ID, or rgba5551 color if fnNode.func is null +}; + +/** Renders the object that Mario is holding. + */ +struct GraphNodeHeldObject +{ + /*0x00*/ struct FnGraphNode fnNode; + /*0x18*/ s32 playerIndex; + /*0x1C*/ struct Object *objNode; + /*0x20*/ Vec3s translation; +}; + +/** A node that allows an object to specify a different culling radius than the + * default one of 300. For this to work, it needs to be a direct child of the + * object node. Used for very large objects, such as shock wave rings that Bowser + * creates, tornadoes, the big eel. + */ +struct GraphNodeCullingRadius +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ s16 cullingRadius; // specifies the 'sphere radius' for purposes of frustum culling + u8 pad1E[2]; +}; + +extern struct GraphNodeMasterList *gCurGraphNodeMasterList; +extern struct GraphNodePerspective *gCurGraphNodeCamFrustum; +extern struct GraphNodeCamera *gCurGraphNodeCamera; +extern struct GraphNodeHeldObject *gCurGraphNodeHeldObject; +extern u16 gAreaUpdateCounter; + +extern struct GraphNode *gCurRootGraphNode; +extern struct GraphNode *gCurGraphNodeList[]; + +extern s16 gCurGraphNodeIndex; + +//extern Vec3f gVec3fZero; +//extern Vec3s gVec3sZero; +//extern Vec3f gVec3fOne; +//extern Vec3s gVec3sOne; + +void init_scene_graph_node_links(struct GraphNode *graphNode, s32 type); + +struct GraphNodeRoot *init_graph_node_root(struct AllocOnlyPool *pool, struct GraphNodeRoot *graphNode, + s16 areaIndex, s16 x, s16 y, s16 width, s16 height); +struct GraphNodeOrthoProjection *init_graph_node_ortho_projection(struct AllocOnlyPool *pool, struct GraphNodeOrthoProjection *graphNode, f32 scale); +struct GraphNodePerspective *init_graph_node_perspective(struct AllocOnlyPool *pool, struct GraphNodePerspective *graphNode, + f32 fov, s16 near, s16 far, GraphNodeFunc nodeFunc, s32 unused); +struct GraphNodeStart *init_graph_node_start(struct AllocOnlyPool *pool, struct GraphNodeStart *graphNode); +struct GraphNodeMasterList *init_graph_node_master_list(struct AllocOnlyPool *pool, struct GraphNodeMasterList *graphNode, s16 on); +struct GraphNodeLevelOfDetail *init_graph_node_render_range(struct AllocOnlyPool *pool, struct GraphNodeLevelOfDetail *graphNode, + s16 minDistance, s16 maxDistance); +struct GraphNodeSwitchCase *init_graph_node_switch_case(struct AllocOnlyPool *pool, struct GraphNodeSwitchCase *graphNode, + s16 numCases, s16 selectedCase, GraphNodeFunc nodeFunc, s32 unused); +struct GraphNodeCamera *init_graph_node_camera(struct AllocOnlyPool *pool, struct GraphNodeCamera *graphNode, + f32 *pos, f32 *focus, GraphNodeFunc func, s32 mode); +struct GraphNodeTranslationRotation *init_graph_node_translation_rotation(struct AllocOnlyPool *pool, struct GraphNodeTranslationRotation *graphNode, + s32 drawingLayer, void *displayList, Vec3s translation, Vec3s rotation); +struct GraphNodeTranslation *init_graph_node_translation(struct AllocOnlyPool *pool, struct GraphNodeTranslation *graphNode, + s32 drawingLayer, void *displayList, Vec3s translation); +struct GraphNodeRotation *init_graph_node_rotation(struct AllocOnlyPool *pool, struct GraphNodeRotation *graphNode, + s32 drawingLayer, void *displayList, Vec3s rotation); +struct GraphNodeScale *init_graph_node_scale(struct AllocOnlyPool *pool, struct GraphNodeScale *graphNode, + s32 drawingLayer, void *displayList, f32 scale); +struct GraphNodeObject *init_graph_node_object(struct AllocOnlyPool *pool, struct GraphNodeObject *graphNode, + struct GraphNode *sharedChild, Vec3f pos, Vec3s angle, Vec3f scale); +struct GraphNodeCullingRadius *init_graph_node_culling_radius(struct AllocOnlyPool *pool, struct GraphNodeCullingRadius *graphNode, s16 radius); +struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct AllocOnlyPool *pool, struct GraphNodeAnimatedPart *graphNode, + s32 drawingLayer, void *displayList, Vec3s translation); +struct GraphNodeBillboard *init_graph_node_billboard(struct AllocOnlyPool *pool, struct GraphNodeBillboard *graphNode, + s32 drawingLayer, void *displayList, Vec3s translation); +struct GraphNodeDisplayList *init_graph_node_display_list(struct AllocOnlyPool *pool, struct GraphNodeDisplayList *graphNode, + s32 drawingLayer, void *displayList); +struct GraphNodeShadow *init_graph_node_shadow(struct AllocOnlyPool *pool, struct GraphNodeShadow *graphNode, + s16 shadowScale, u8 shadowSolidity, u8 shadowType); +struct GraphNodeObjectParent *init_graph_node_object_parent(struct AllocOnlyPool *pool, struct GraphNodeObjectParent *sp1c, + struct GraphNode *sharedChild); +struct GraphNodeGenerated *init_graph_node_generated(struct AllocOnlyPool *pool, struct GraphNodeGenerated *sp1c, + GraphNodeFunc gfxFunc, s32 parameter); +struct GraphNodeBackground *init_graph_node_background(struct AllocOnlyPool *pool, struct GraphNodeBackground *sp1c, + u16 background, GraphNodeFunc backgroundFunc, s32 zero); +struct GraphNodeHeldObject *init_graph_node_held_object(struct AllocOnlyPool *pool, struct GraphNodeHeldObject *sp1c, + struct Object *objNode, Vec3s translation, + GraphNodeFunc nodeFunc, s32 playerIndex); +struct GraphNode *geo_add_child(struct GraphNode *parent, struct GraphNode *childNode); +struct GraphNode *geo_remove_child(struct GraphNode *graphNode); +struct GraphNode *geo_make_first_child(struct GraphNode *newFirstChild); + +void geo_call_global_function_nodes_helper(struct GraphNode *graphNode, s32 callContext); +void geo_call_global_function_nodes(struct GraphNode *graphNode, s32 callContext); + +void geo_reset_object_node(struct GraphNodeObject *graphNode); +void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle); +void geo_obj_init_spawninfo(struct GraphNodeObject *graphNode, struct SpawnInfo *spawn); +void geo_obj_init_animation(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr); +void geo_obj_init_animation_accel(struct GraphNodeObject *graphNode, struct Animation **animPtrAddr, u32 animAccel); + +s32 retrieve_animation_index(s32 frame, u16 **attributes); + +s16 geo_update_animation_frame(struct AnimInfo *obj, s32 *accelAssist); +void geo_retreive_animation_translation(struct GraphNodeObject *obj, Vec3f position); + +struct GraphNodeRoot *geo_find_root(struct GraphNode *graphNode); + +// graph_node_manager +s16 *read_vec3s_to_vec3f(Vec3f, s16 *src); +s16 *read_vec3s(Vec3s dst, s16 *src); +s16 *read_vec3s_angle(Vec3s dst, s16 *src); +void register_scene_graph_node(struct GraphNode *graphNode); + +#endif // GRAPH_NODE_H diff --git a/src/engine/graph_node_manager.c b/src/engine/graph_node_manager.c new file mode 100644 index 0000000..edde5aa --- /dev/null +++ b/src/engine/graph_node_manager.c @@ -0,0 +1,81 @@ +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +#include "geo_layout.h" +#include "graph_node.h" + +#if IS_64_BIT +static s16 next_s16_in_geo_script(s16 **src) { + s16 ret; + if (((uintptr_t)(*src) & 7) == 4) { + *src += 2; // skip 32 bits + } + ret = *(*src)++; + if (((uintptr_t)(*src) & 7) == 4) { + *src += 2; // skip 32 bits + } + return ret; +} +#else +#define next_s16_in_geo_script(src) (*(*src)++) +#endif + +/** + * Takes a pointer to three shorts (supplied by a geo layout script) and + * copies it to the destination float vector. + */ +s16 *read_vec3s_to_vec3f(Vec3f dst, s16 *src) { + dst[0] = next_s16_in_geo_script(&src); + dst[1] = next_s16_in_geo_script(&src); + dst[2] = next_s16_in_geo_script(&src); + return src; +} + +/** + * Takes a pointer to three shorts (supplied by a geo layout script) and + * copies it to the destination vector. It's essentially a memcpy but consistent + * with the other two 'geo-script vector to internal vector' functions. + */ +s16 *read_vec3s(Vec3s dst, s16 *src) { + dst[0] = next_s16_in_geo_script(&src); + dst[1] = next_s16_in_geo_script(&src); + dst[2] = next_s16_in_geo_script(&src); + return src; +} + +/** + * Takes a pointer to three angles in degrees (supplied by a geo layout script) + * and converts it to a vector of three in-game angle units in [-32768, 32767] + * range. + */ +s16 *read_vec3s_angle(Vec3s dst, s16 *src) { + dst[0] = (next_s16_in_geo_script(&src) << 15) / 180; + dst[1] = (next_s16_in_geo_script(&src) << 15) / 180; + dst[2] = (next_s16_in_geo_script(&src) << 15) / 180; + return src; +} + +/** + * Add the given graph node as a child to the current top of the gfx stack: + * 'gCurGraphNodeList'. This is called from geo_layout commands to add nodes + * to the scene graph. + */ +void register_scene_graph_node(struct GraphNode *graphNode) { + if (graphNode != NULL) { + gCurGraphNodeList[gCurGraphNodeIndex] = graphNode; + + if (gCurGraphNodeIndex == 0) { + if (gCurRootGraphNode == NULL) { + gCurRootGraphNode = graphNode; + } + } else { + if (gCurGraphNodeList[gCurGraphNodeIndex - 1]->type == GRAPH_NODE_TYPE_OBJECT_PARENT) { + ((struct GraphNodeObjectParent *) gCurGraphNodeList[gCurGraphNodeIndex - 1]) + ->sharedChild = graphNode; + } else { + geo_add_child(gCurGraphNodeList[gCurGraphNodeIndex - 1], graphNode); + } + } + } +} diff --git a/src/engine/math_util.c b/src/engine/math_util.c new file mode 100644 index 0000000..ddb7381 --- /dev/null +++ b/src/engine/math_util.c @@ -0,0 +1,2306 @@ +#include "math_util.h" + +#include +#include "../shim.h" + +#include "../guMtxF2L.h" +#include "../include/sm64.h" +#include "graph_node.h" +#include "surface_collision.h" + +// PATCH +static Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f }; + +// Inlined tables +f32 gSineTable[] = { + 0.000000000f, 0.0015339801f,0.0030679568f,0.004601926f, + 0.0061358847f,0.007669829f, 0.009203754f, 0.010737659f, + 0.012271538f, 0.0138053885f,0.015339206f, 0.016872987f, + 0.018406730f, 0.019940428f, 0.021474080f, 0.023007682f, + 0.024541229f, 0.026074719f, 0.027608145f, 0.029141508f, + 0.030674804f, 0.032208025f, 0.033741172f, 0.035274237f, + 0.036807224f, 0.038340122f, 0.039872926f, 0.041405641f, + 0.042938258f, 0.044470772f, 0.046003181f, 0.047535483f, + 0.049067676f, 0.050599750f, 0.052131705f, 0.053663537f, + 0.055195246f, 0.056726821f, 0.058258265f, 0.059789572f, + 0.061320737f, 0.062851757f, 0.064382628f, 0.065913349f, + 0.067443922f, 0.068974331f, 0.070504576f, 0.072034650f, + 0.073564567f, 0.075094298f, 0.076623864f, 0.078153245f, + 0.079682440f, 0.081211448f, 0.082740262f, 0.084268890f, + 0.085797310f, 0.087325536f, 0.088853553f, 0.090381362f, + 0.091908954f, 0.093436338f, 0.094963498f, 0.096490428f, + 0.098017141f, 0.099543616f, 0.101069860f, 0.102595866f, + 0.104121633f, 0.105647154f, 0.107172422f, 0.108697444f, + 0.110222206f, 0.111746714f, 0.113270953f, 0.114794925f, + 0.116318628f, 0.117842063f, 0.119365215f, 0.120888084f, + 0.122410677f, 0.123932973f, 0.125454977f, 0.126976699f, + 0.128498107f, 0.130019218f, 0.131540030f, 0.133060530f, + 0.134580702f, 0.136100575f, 0.137620121f, 0.139139339f, + 0.140658244f, 0.142176807f, 0.143695027f, 0.145212919f, + 0.146730468f, 0.148247674f, 0.149764538f, 0.151281044f, + 0.152797192f, 0.154312968f, 0.155828401f, 0.157343462f, + 0.158858150f, 0.160372451f, 0.161886394f, 0.163399950f, + 0.164913118f, 0.166425899f, 0.167938292f, 0.169450298f, + 0.170961887f, 0.172473088f, 0.173983872f, 0.175494254f, + 0.177004218f, 0.178513765f, 0.180022895f, 0.181531608f, + 0.183039889f, 0.184547737f, 0.186055154f, 0.187562123f, + 0.189068660f, 0.190574750f, 0.192080393f, 0.193585590f, + 0.195090324f, 0.196594596f, 0.198098406f, 0.199601755f, + 0.201104641f, 0.202607036f, 0.204108968f, 0.205610409f, + 0.207111374f, 0.208611846f, 0.210111842f, 0.211611331f, + 0.213110313f, 0.214608818f, 0.216106802f, 0.217604280f, + 0.219101235f, 0.220597684f, 0.222093627f, 0.223589033f, + 0.225083917f, 0.226578265f, 0.228072077f, 0.229565367f, + 0.231058106f, 0.232550308f, 0.234041959f, 0.235533059f, + 0.237023607f, 0.238513589f, 0.240003020f, 0.241491884f, + 0.242980182f, 0.244467899f, 0.245955050f, 0.247441620f, + 0.248927608f, 0.250413001f, 0.251897812f, 0.253382027f, + 0.254865646f, 0.256348670f, 0.257831097f, 0.259312928f, + 0.260794103f, 0.262274712f, 0.263754666f, 0.265234023f, + 0.266712755f, 0.268190861f, 0.269668311f, 0.271145165f, + 0.272621363f, 0.274096906f, 0.275571823f, 0.277046084f, + 0.278519690f, 0.279992640f, 0.281464934f, 0.282936573f, + 0.284407526f, 0.285877824f, 0.287347466f, 0.288816422f, + 0.290284663f, 0.291752249f, 0.293219149f, 0.294685364f, + 0.296150893f, 0.297615707f, 0.299079835f, 0.300543249f, + 0.302005947f, 0.303467959f, 0.304929227f, 0.306389809f, + 0.307849646f, 0.309308767f, 0.310767144f, 0.312224805f, + 0.313681751f, 0.315137923f, 0.316593379f, 0.318048090f, + 0.319502026f, 0.320955247f, 0.322407693f, 0.323859364f, + 0.325310290f, 0.326760441f, 0.328209847f, 0.329658449f, + 0.331106305f, 0.332553357f, 0.333999664f, 0.335445136f, + 0.336889863f, 0.338333756f, 0.339776874f, 0.341219217f, + 0.342660725f, 0.344101429f, 0.345541328f, 0.346980423f, + 0.348418683f, 0.349856138f, 0.351292759f, 0.352728546f, + 0.354163527f, 0.355597675f, 0.357030958f, 0.358463407f, + 0.359895051f, 0.361325800f, 0.362755716f, 0.364184797f, + 0.365612984f, 0.367040336f, 0.368466824f, 0.369892448f, + 0.371317208f, 0.372741073f, 0.374164075f, 0.375586182f, + 0.377007425f, 0.378427744f, 0.379847199f, 0.381265759f, + 0.382683426f, 0.384100199f, 0.385516047f, 0.386931002f, + 0.388345033f, 0.389758170f, 0.391170382f, 0.392581671f, + 0.393992037f, 0.395401478f, 0.396809995f, 0.398217559f, + 0.399624199f, 0.401029885f, 0.402434647f, 0.403838456f, + 0.405241311f, 0.406643212f, 0.408044159f, 0.409444153f, + 0.410843164f, 0.412241220f, 0.413638324f, 0.415034413f, + 0.416429549f, 0.417823702f, 0.419216901f, 0.420609087f, + 0.422000259f, 0.423390478f, 0.424779683f, 0.426167876f, + 0.427555084f, 0.428941280f, 0.430326492f, 0.431710660f, + 0.433093816f, 0.434475958f, 0.435857087f, 0.437237173f, + 0.438616246f, 0.439994276f, 0.441371262f, 0.442747235f, + 0.444122136f, 0.445496023f, 0.446868837f, 0.448240608f, + 0.449611336f, 0.450980991f, 0.452349573f, 0.453717113f, + 0.455083579f, 0.456448972f, 0.457813293f, 0.459176540f, + 0.460538715f, 0.461899787f, 0.463259786f, 0.464618683f, + 0.465976506f, 0.467333198f, 0.468688816f, 0.470043331f, + 0.471396744f, 0.472749025f, 0.474100202f, 0.475450277f, + 0.476799220f, 0.478147060f, 0.479493767f, 0.480839342f, + 0.482183784f, 0.483527064f, 0.484869242f, 0.486210287f, + 0.487550169f, 0.488888890f, 0.490226477f, 0.491562903f, + 0.492898196f, 0.494232297f, 0.495565265f, 0.496897042f, + 0.498227656f, 0.499557108f, 0.500885367f, 0.502212465f, + 0.503538370f, 0.504863083f, 0.506186664f, 0.507508993f, + 0.508830130f, 0.510150075f, 0.511468828f, 0.512786388f, + 0.514102757f, 0.515417874f, 0.516731799f, 0.518044531f, + 0.519356012f, 0.520666242f, 0.521975279f, 0.523283124f, + 0.524589658f, 0.525895000f, 0.527199149f, 0.528501987f, + 0.529803634f, 0.531104028f, 0.532403111f, 0.533701003f, + 0.534997642f, 0.536292970f, 0.537587047f, 0.538879931f, + 0.540171444f, 0.541461766f, 0.542750776f, 0.544038534f, + 0.545324981f, 0.546610177f, 0.547894061f, 0.549176633f, + 0.550457954f, 0.551737964f, 0.553016722f, 0.554294109f, + 0.555570245f, 0.556845009f, 0.558118522f, 0.559390724f, + 0.560661554f, 0.561931133f, 0.563199341f, 0.564466238f, + 0.565731823f, 0.566996038f, 0.568258941f, 0.569520533f, + 0.570780754f, 0.572039604f, 0.573297143f, 0.574553370f, + 0.575808167f, 0.577061653f, 0.578313768f, 0.579564571f, + 0.580813944f, 0.582062006f, 0.583308637f, 0.584553957f, + 0.585797846f, 0.587040365f, 0.588281572f, 0.589521289f, + 0.590759695f, 0.591996670f, 0.593232274f, 0.594466507f, + 0.595699310f, 0.596930683f, 0.598160684f, 0.599389315f, + 0.600616455f, 0.601842225f, 0.603066623f, 0.604289532f, + 0.605511069f, 0.606731117f, 0.607949793f, 0.609167039f, + 0.610382795f, 0.611597180f, 0.612810075f, 0.614021540f, + 0.615231574f, 0.616440177f, 0.617647290f, 0.618852973f, + 0.620057225f, 0.621259987f, 0.622461259f, 0.623661101f, + 0.624859512f, 0.626056373f, 0.627251804f, 0.628445745f, + 0.629638255f, 0.630829215f, 0.632018745f, 0.633206785f, + 0.634393275f, 0.635578334f, 0.636761844f, 0.637943923f, + 0.639124453f, 0.640303493f, 0.641481042f, 0.642657042f, + 0.643831551f, 0.645004511f, 0.646176040f, 0.647345960f, + 0.648514390f, 0.649681330f, 0.650846660f, 0.652010560f, + 0.653172851f, 0.654333591f, 0.655492842f, 0.656650543f, + 0.657806695f, 0.658961296f, 0.660114348f, 0.661265850f, + 0.662415802f, 0.663564146f, 0.664710999f, 0.665856242f, + 0.666999936f, 0.668142021f, 0.669282615f, 0.670421541f, + 0.671558976f, 0.672694743f, 0.673829019f, 0.674961627f, + 0.676092684f, 0.677222192f, 0.678350031f, 0.679476321f, + 0.680601001f, 0.681724072f, 0.682845533f, 0.683965385f, + 0.685083687f, 0.686200321f, 0.687315345f, 0.688428760f, + 0.689540565f, 0.690650702f, 0.691759229f, 0.692866147f, + 0.693971455f, 0.695075095f, 0.696177125f, 0.697277486f, + 0.698376238f, 0.699473321f, 0.700568795f, 0.701662600f, + 0.702754736f, 0.703845263f, 0.704934061f, 0.706021249f, + 0.707106769f, 0.708190620f, 0.709272802f, 0.710353374f, + 0.711432219f, 0.712509394f, 0.713584840f, 0.714658678f, + 0.715730846f, 0.716801286f, 0.717870057f, 0.718937099f, + 0.720002532f, 0.721066177f, 0.722128212f, 0.723188460f, + 0.724247098f, 0.725303948f, 0.726359129f, 0.727412641f, + 0.728464365f, 0.729514420f, 0.730562747f, 0.731609404f, + 0.732654274f, 0.733697414f, 0.734738886f, 0.735778570f, + 0.736816585f, 0.737852812f, 0.738887310f, 0.739920080f, + 0.740951121f, 0.741980433f, 0.743007958f, 0.744033754f, + 0.745057762f, 0.746080101f, 0.747100592f, 0.748119354f, + 0.749136388f, 0.750151634f, 0.751165152f, 0.752176821f, + 0.753186822f, 0.754194975f, 0.755201399f, 0.756205976f, + 0.757208824f, 0.758209884f, 0.759209216f, 0.760206699f, + 0.761202395f, 0.762196302f, 0.763188422f, 0.764178753f, + 0.765167236f, 0.766153991f, 0.767138898f, 0.768122017f, + 0.769103348f, 0.770082831f, 0.771060526f, 0.772036374f, + 0.773010433f, 0.773982704f, 0.774953127f, 0.775921702f, + 0.776888490f, 0.777853429f, 0.778816521f, 0.779777765f, + 0.780737221f, 0.781694829f, 0.782650590f, 0.783604503f, + 0.784556568f, 0.785506845f, 0.786455214f, 0.787401736f, + 0.788346410f, 0.789289236f, 0.790230215f, 0.791169345f, + 0.792106569f, 0.793041945f, 0.793975472f, 0.794907153f, + 0.795836926f, 0.796764791f, 0.797690868f, 0.798614979f, + 0.799537241f, 0.800457656f, 0.801376164f, 0.802292824f, + 0.803207517f, 0.804120362f, 0.805031359f, 0.805940390f, + 0.806847572f, 0.807752848f, 0.808656156f, 0.809557617f, + 0.810457170f, 0.811354876f, 0.812250614f, 0.813144386f, + 0.814036310f, 0.814926326f, 0.815814435f, 0.816700578f, + 0.817584813f, 0.818467140f, 0.819347501f, 0.820225954f, + 0.821102500f, 0.821977139f, 0.822849810f, 0.823720515f, + 0.824589312f, 0.825456142f, 0.826321065f, 0.827184021f, + 0.828045070f, 0.828904092f, 0.829761207f, 0.830616415f, + 0.831469595f, 0.832320869f, 0.833170176f, 0.834017515f, + 0.834862888f, 0.835706294f, 0.836547732f, 0.837387204f, + 0.838224709f, 0.839060247f, 0.839893818f, 0.840725362f, + 0.841554999f, 0.842382610f, 0.843208253f, 0.844031870f, + 0.844853580f, 0.845673263f, 0.846490920f, 0.847306609f, + 0.848120332f, 0.848932028f, 0.849741757f, 0.850549459f, + 0.851355195f, 0.852158904f, 0.852960587f, 0.853760302f, + 0.854557991f, 0.855353653f, 0.856147349f, 0.856938958f, + 0.857728601f, 0.858516216f, 0.859301805f, 0.860085368f, + 0.860866964f, 0.861646473f, 0.862423956f, 0.863199413f, + 0.863972843f, 0.864744246f, 0.865513623f, 0.866280973f, + 0.867046237f, 0.867809474f, 0.868570685f, 0.869329870f, + 0.870086968f, 0.870842040f, 0.871595085f, 0.872346044f, + 0.873094976f, 0.873841822f, 0.874586642f, 0.875329375f, + 0.876070082f, 0.876808703f, 0.877545297f, 0.878279805f, + 0.879012227f, 0.879742622f, 0.880470872f, 0.881197095f, + 0.881921291f, 0.882643342f, 0.883363366f, 0.884081244f, + 0.884797096f, 0.885510862f, 0.886222541f, 0.886932135f, + 0.887639642f, 0.888345063f, 0.889048338f, 0.889749587f, + 0.890448749f, 0.891145766f, 0.891840696f, 0.892533541f, + 0.893224299f, 0.893912971f, 0.894599497f, 0.895283937f, + 0.895966232f, 0.896646500f, 0.897324562f, 0.898000598f, + 0.898674488f, 0.899346232f, 0.900015891f, 0.900683403f, + 0.901348829f, 0.902012169f, 0.902673304f, 0.903332353f, + 0.903989315f, 0.904644072f, 0.905296743f, 0.905947268f, + 0.906595707f, 0.907242000f, 0.907886088f, 0.908528090f, + 0.909168005f, 0.909805715f, 0.910441279f, 0.911074758f, + 0.911706030f, 0.912335157f, 0.912962198f, 0.913587034f, + 0.914209783f, 0.914830327f, 0.915448725f, 0.916064978f, + 0.916679084f, 0.917290986f, 0.917900801f, 0.918508410f, + 0.919113874f, 0.919717133f, 0.920318305f, 0.920917213f, + 0.921514034f, 0.922108650f, 0.922701120f, 0.923291445f, + 0.923879504f, 0.924465477f, 0.925049245f, 0.925630808f, + 0.926210225f, 0.926787496f, 0.927362502f, 0.927935421f, + 0.928506076f, 0.929074585f, 0.929640889f, 0.930205047f, + 0.930766940f, 0.931326687f, 0.931884289f, 0.932439625f, + 0.932992816f, 0.933543801f, 0.934092522f, 0.934639156f, + 0.935183525f, 0.935725689f, 0.936265647f, 0.936803460f, + 0.937339008f, 0.937872350f, 0.938403547f, 0.938932478f, + 0.939459205f, 0.939983726f, 0.940506041f, 0.941026151f, + 0.941544056f, 0.942059755f, 0.942573190f, 0.943084419f, + 0.943593442f, 0.944100261f, 0.944604814f, 0.945107222f, + 0.945607305f, 0.946105242f, 0.946600914f, 0.947094381f, + 0.947585583f, 0.948074579f, 0.948561370f, 0.949045897f, + 0.949528158f, 0.950008273f, 0.950486064f, 0.950961649f, + 0.951435030f, 0.951906145f, 0.952374995f, 0.952841640f, + 0.953306019f, 0.953768194f, 0.954228103f, 0.954685748f, + 0.955141187f, 0.955594361f, 0.956045270f, 0.956493914f, + 0.956940353f, 0.957384527f, 0.957826436f, 0.958266079f, + 0.958703458f, 0.959138632f, 0.959571540f, 0.960002124f, + 0.960430503f, 0.960856616f, 0.961280465f, 0.961702049f, + 0.962121427f, 0.962538481f, 0.962953269f, 0.963365793f, + 0.963776052f, 0.964184046f, 0.964589775f, 0.964993238f, + 0.965394437f, 0.965793371f, 0.966189981f, 0.966584384f, + 0.966976464f, 0.967366278f, 0.967753828f, 0.968139112f, + 0.968522072f, 0.968902826f, 0.969281256f, 0.969657362f, + 0.970031261f, 0.970402837f, 0.970772147f, 0.971139133f, + 0.971503913f, 0.971866310f, 0.972226501f, 0.972584367f, + 0.972939968f, 0.973293245f, 0.973644257f, 0.973992944f, + 0.974339366f, 0.974683523f, 0.975025356f, 0.975364864f, + 0.975702107f, 0.976037085f, 0.976369739f, 0.976700068f, + 0.977028131f, 0.977353871f, 0.977677345f, 0.977998495f, + 0.978317380f, 0.978633940f, 0.978948176f, 0.979260147f, + 0.979569793f, 0.979877114f, 0.980182111f, 0.980484843f, + 0.980785251f, 0.981083393f, 0.981379211f, 0.981672704f, + 0.981963873f, 0.982252717f, 0.982539296f, 0.982823551f, + 0.983105481f, 0.983385086f, 0.983662426f, 0.983937442f, + 0.984210074f, 0.984480441f, 0.984748483f, 0.985014260f, + 0.985277653f, 0.985538721f, 0.985797524f, 0.986053944f, + 0.986308098f, 0.986559927f, 0.986809373f, 0.987056553f, + 0.987301409f, 0.987543941f, 0.987784147f, 0.988022029f, + 0.988257587f, 0.988490820f, 0.988721669f, 0.988950253f, + 0.989176512f, 0.989400446f, 0.989621997f, 0.989841282f, + 0.990058184f, 0.990272820f, 0.990485072f, 0.990695000f, + 0.990902662f, 0.991107941f, 0.991310835f, 0.991511464f, + 0.991709769f, 0.991905689f, 0.992099285f, 0.992290616f, + 0.992479563f, 0.992666125f, 0.992850423f, 0.993032336f, + 0.993211925f, 0.993389189f, 0.993564129f, 0.993736744f, + 0.993906975f, 0.994074881f, 0.994240463f, 0.994403660f, + 0.994564593f, 0.994723141f, 0.994879305f, 0.995033205f, + 0.995184720f, 0.995333910f, 0.995480776f, 0.995625257f, + 0.995767415f, 0.995907247f, 0.996044695f, 0.996179819f, + 0.996312618f, 0.996443033f, 0.996571124f, 0.996696889f, + 0.996820271f, 0.996941328f, 0.997060061f, 0.997176409f, + 0.997290432f, 0.997402132f, 0.997511446f, 0.997618437f, + 0.997723043f, 0.997825325f, 0.997925282f, 0.998022854f, + 0.998118103f, 0.998211026f, 0.998301566f, 0.998389721f, + 0.998475552f, 0.998559058f, 0.998640239f, 0.998719037f, + 0.998795450f, 0.998869538f, 0.998941302f, 0.999010682f, + 0.999077737f, 0.999142408f, 0.999204755f, 0.999264777f, + 0.999322355f, 0.999377668f, 0.999430597f, 0.999481201f, + 0.999529421f, 0.999575317f, 0.999618828f, 0.999660015f, + 0.999698818f, 0.999735296f, 0.999769390f, 0.999801159f, + 0.999830604f, 0.999857664f, 0.999882340f, 0.999904692f, + 0.999924719f, 0.999942362f, 0.999957621f, 0.999970615f, + 0.999981165f, 0.999989390f, 0.999995291f, 0.999998808f, +#ifndef AVOID_UB +}; + +f32 gCosineTable[0x1000] = { +#endif + // cosine + 1.000000000f, 0.999998808f, 0.999995291f, 0.999989390f, + 0.999981165f, 0.999970615f, 0.999957621f, 0.999942362f, + 0.999924719f, 0.999904692f, 0.999882340f, 0.999857664f, + 0.999830604f, 0.999801159f, 0.999769390f, 0.999735296f, + 0.999698818f, 0.999660015f, 0.999618828f, 0.999575317f, + 0.999529421f, 0.999481201f, 0.999430597f, 0.999377668f, + 0.999322355f, 0.999264777f, 0.999204755f, 0.999142408f, + 0.999077737f, 0.999010682f, 0.998941302f, 0.998869538f, + 0.998795450f, 0.998719037f, 0.998640239f, 0.998559058f, + 0.998475552f, 0.998389721f, 0.998301566f, 0.998211026f, + 0.998118103f, 0.998022854f, 0.997925282f, 0.997825325f, + 0.997723043f, 0.997618437f, 0.997511446f, 0.997402132f, + 0.997290432f, 0.997176409f, 0.997060061f, 0.996941328f, + 0.996820271f, 0.996696889f, 0.996571124f, 0.996443033f, + 0.996312618f, 0.996179819f, 0.996044695f, 0.995907247f, + 0.995767415f, 0.995625257f, 0.995480776f, 0.995333910f, + 0.995184720f, 0.995033205f, 0.994879305f, 0.994723141f, + 0.994564593f, 0.994403660f, 0.994240463f, 0.994074881f, + 0.993906975f, 0.993736744f, 0.993564129f, 0.993389189f, + 0.993211925f, 0.993032336f, 0.992850423f, 0.992666125f, + 0.992479563f, 0.992290616f, 0.992099285f, 0.991905689f, + 0.991709769f, 0.991511464f, 0.991310835f, 0.991107941f, + 0.990902662f, 0.990695000f, 0.990485072f, 0.990272820f, + 0.990058184f, 0.989841282f, 0.989621997f, 0.989400446f, + 0.989176512f, 0.988950253f, 0.988721669f, 0.988490820f, + 0.988257587f, 0.988022029f, 0.987784147f, 0.987543941f, + 0.987301409f, 0.987056553f, 0.986809373f, 0.986559927f, + 0.986308098f, 0.986053944f, 0.985797524f, 0.985538721f, + 0.985277653f, 0.985014260f, 0.984748483f, 0.984480441f, + 0.984210074f, 0.983937442f, 0.983662426f, 0.983385086f, + 0.983105481f, 0.982823551f, 0.982539296f, 0.982252717f, + 0.981963873f, 0.981672704f, 0.981379211f, 0.981083393f, + 0.980785251f, 0.980484843f, 0.980182111f, 0.979877114f, + 0.979569793f, 0.979260147f, 0.978948176f, 0.978633940f, + 0.978317380f, 0.977998495f, 0.977677345f, 0.977353871f, + 0.977028131f, 0.976700068f, 0.976369739f, 0.976037085f, + 0.975702107f, 0.975364864f, 0.975025356f, 0.974683523f, + 0.974339366f, 0.973992944f, 0.973644257f, 0.973293245f, + 0.972939968f, 0.972584367f, 0.972226501f, 0.971866310f, + 0.971503913f, 0.971139133f, 0.970772147f, 0.970402837f, + 0.970031261f, 0.969657362f, 0.969281256f, 0.968902826f, + 0.968522072f, 0.968139112f, 0.967753828f, 0.967366278f, + 0.966976464f, 0.966584384f, 0.966189981f, 0.965793371f, + 0.965394437f, 0.964993238f, 0.964589775f, 0.964184046f, + 0.963776052f, 0.963365793f, 0.962953269f, 0.962538481f, + 0.962121427f, 0.961702049f, 0.961280465f, 0.960856616f, + 0.960430503f, 0.960002124f, 0.959571540f, 0.959138632f, + 0.958703458f, 0.958266079f, 0.957826436f, 0.957384527f, + 0.956940353f, 0.956493914f, 0.956045270f, 0.955594361f, + 0.955141187f, 0.954685748f, 0.954228103f, 0.953768194f, + 0.953306019f, 0.952841640f, 0.952374995f, 0.951906145f, + 0.951435030f, 0.950961649f, 0.950486064f, 0.950008273f, + 0.949528158f, 0.949045897f, 0.948561370f, 0.948074579f, + 0.947585583f, 0.947094381f, 0.946600914f, 0.946105242f, + 0.945607305f, 0.945107222f, 0.944604814f, 0.944100261f, + 0.943593442f, 0.943084419f, 0.942573190f, 0.942059755f, + 0.941544056f, 0.941026151f, 0.940506041f, 0.939983726f, + 0.939459205f, 0.938932478f, 0.938403547f, 0.937872350f, + 0.937339008f, 0.936803460f, 0.936265647f, 0.935725689f, + 0.935183525f, 0.934639156f, 0.934092522f, 0.933543801f, + 0.932992816f, 0.932439625f, 0.931884289f, 0.931326687f, + 0.930766940f, 0.930205047f, 0.929640889f, 0.929074585f, + 0.928506076f, 0.927935421f, 0.927362502f, 0.926787496f, + 0.926210225f, 0.925630808f, 0.925049245f, 0.924465477f, + 0.923879504f, 0.923291445f, 0.922701120f, 0.922108650f, + 0.921514034f, 0.920917213f, 0.920318305f, 0.919717133f, + 0.919113874f, 0.918508410f, 0.917900801f, 0.917290986f, + 0.916679084f, 0.916064978f, 0.915448725f, 0.914830327f, + 0.914209783f, 0.913587034f, 0.912962198f, 0.912335157f, + 0.911706030f, 0.911074758f, 0.910441279f, 0.909805715f, + 0.909168005f, 0.908528090f, 0.907886088f, 0.907242000f, + 0.906595707f, 0.905947268f, 0.905296743f, 0.904644072f, + 0.903989315f, 0.903332353f, 0.902673304f, 0.902012169f, + 0.901348829f, 0.900683403f, 0.900015891f, 0.899346232f, + 0.898674488f, 0.898000598f, 0.897324562f, 0.896646500f, + 0.895966232f, 0.895283937f, 0.894599497f, 0.893912971f, + 0.893224299f, 0.892533541f, 0.891840696f, 0.891145766f, + 0.890448749f, 0.889749587f, 0.889048338f, 0.888345063f, + 0.887639642f, 0.886932135f, 0.886222541f, 0.885510862f, + 0.884797096f, 0.884081244f, 0.883363366f, 0.882643342f, + 0.881921291f, 0.881197095f, 0.880470872f, 0.879742622f, + 0.879012227f, 0.878279805f, 0.877545297f, 0.876808703f, + 0.876070082f, 0.875329375f, 0.874586642f, 0.873841822f, + 0.873094976f, 0.872346044f, 0.871595085f, 0.870842040f, + 0.870086968f, 0.869329870f, 0.868570685f, 0.867809474f, + 0.867046237f, 0.866280973f, 0.865513623f, 0.864744246f, + 0.863972843f, 0.863199413f, 0.862423956f, 0.861646473f, + 0.860866964f, 0.860085368f, 0.859301805f, 0.858516216f, + 0.857728601f, 0.856938958f, 0.856147349f, 0.855353653f, + 0.854557991f, 0.853760302f, 0.852960587f, 0.852158904f, + 0.851355195f, 0.850549459f, 0.849741757f, 0.848932028f, + 0.848120332f, 0.847306609f, 0.846490920f, 0.845673263f, + 0.844853580f, 0.844031870f, 0.843208253f, 0.842382610f, + 0.841554999f, 0.840725362f, 0.839893818f, 0.839060247f, + 0.838224709f, 0.837387204f, 0.836547732f, 0.835706294f, + 0.834862888f, 0.834017515f, 0.833170176f, 0.832320869f, + 0.831469595f, 0.830616415f, 0.829761207f, 0.828904092f, + 0.828045070f, 0.827184021f, 0.826321065f, 0.825456142f, + 0.824589312f, 0.823720515f, 0.822849810f, 0.821977139f, + 0.821102500f, 0.820225954f, 0.819347501f, 0.818467140f, + 0.817584813f, 0.816700578f, 0.815814435f, 0.814926326f, + 0.814036310f, 0.813144386f, 0.812250614f, 0.811354876f, + 0.810457170f, 0.809557617f, 0.808656156f, 0.807752848f, + 0.806847572f, 0.805940390f, 0.805031359f, 0.804120362f, + 0.803207517f, 0.802292824f, 0.801376164f, 0.800457656f, + 0.799537241f, 0.798614979f, 0.797690868f, 0.796764791f, + 0.795836926f, 0.794907153f, 0.793975472f, 0.793041945f, + 0.792106569f, 0.791169345f, 0.790230215f, 0.789289236f, + 0.788346410f, 0.787401736f, 0.786455214f, 0.785506845f, + 0.784556568f, 0.783604503f, 0.782650590f, 0.781694829f, + 0.780737221f, 0.779777765f, 0.778816521f, 0.777853429f, + 0.776888490f, 0.775921702f, 0.774953127f, 0.773982704f, + 0.773010433f, 0.772036374f, 0.771060526f, 0.770082831f, + 0.769103348f, 0.768122017f, 0.767138898f, 0.766153991f, + 0.765167236f, 0.764178753f, 0.763188422f, 0.762196302f, + 0.761202395f, 0.760206699f, 0.759209216f, 0.758209884f, + 0.757208824f, 0.756205976f, 0.755201399f, 0.754194975f, + 0.753186822f, 0.752176821f, 0.751165152f, 0.750151634f, + 0.749136388f, 0.748119354f, 0.747100592f, 0.746080101f, + 0.745057762f, 0.744033754f, 0.743007958f, 0.741980433f, + 0.740951121f, 0.739920080f, 0.738887310f, 0.737852812f, + 0.736816585f, 0.735778570f, 0.734738886f, 0.733697414f, + 0.732654274f, 0.731609404f, 0.730562747f, 0.729514420f, + 0.728464365f, 0.727412641f, 0.726359129f, 0.725303948f, + 0.724247098f, 0.723188460f, 0.722128212f, 0.721066177f, + 0.720002532f, 0.718937099f, 0.717870057f, 0.716801286f, + 0.715730846f, 0.714658678f, 0.713584840f, 0.712509394f, + 0.711432219f, 0.710353374f, 0.709272802f, 0.708190620f, + 0.707106769f, 0.706021249f, 0.704934061f, 0.703845263f, + 0.702754736f, 0.701662600f, 0.700568795f, 0.699473321f, + 0.698376238f, 0.697277486f, 0.696177125f, 0.695075095f, + 0.693971455f, 0.692866147f, 0.691759229f, 0.690650702f, + 0.689540565f, 0.688428760f, 0.687315345f, 0.686200321f, + 0.685083687f, 0.683965385f, 0.682845533f, 0.681724072f, + 0.680601001f, 0.679476321f, 0.678350031f, 0.677222192f, + 0.676092684f, 0.674961627f, 0.673829019f, 0.672694743f, + 0.671558976f, 0.670421541f, 0.669282615f, 0.668142021f, + 0.666999936f, 0.665856242f, 0.664710999f, 0.663564146f, + 0.662415802f, 0.661265850f, 0.660114348f, 0.658961296f, + 0.657806695f, 0.656650543f, 0.655492842f, 0.654333591f, + 0.653172851f, 0.652010560f, 0.650846660f, 0.649681330f, + 0.648514390f, 0.647345960f, 0.646176040f, 0.645004511f, + 0.643831551f, 0.642657042f, 0.641481042f, 0.640303493f, + 0.639124453f, 0.637943923f, 0.636761844f, 0.635578334f, + 0.634393275f, 0.633206785f, 0.632018745f, 0.630829215f, + 0.629638255f, 0.628445745f, 0.627251804f, 0.626056373f, + 0.624859512f, 0.623661101f, 0.622461259f, 0.621259987f, + 0.620057225f, 0.618852973f, 0.617647290f, 0.616440177f, + 0.615231574f, 0.614021540f, 0.612810075f, 0.611597180f, + 0.610382795f, 0.609167039f, 0.607949793f, 0.606731117f, + 0.605511069f, 0.604289532f, 0.603066623f, 0.601842225f, + 0.600616455f, 0.599389315f, 0.598160684f, 0.596930683f, + 0.595699310f, 0.594466507f, 0.593232274f, 0.591996670f, + 0.590759695f, 0.589521289f, 0.588281572f, 0.587040365f, + 0.585797846f, 0.584553957f, 0.583308637f, 0.582062006f, + 0.580813944f, 0.579564571f, 0.578313768f, 0.577061653f, + 0.575808167f, 0.574553370f, 0.573297143f, 0.572039604f, + 0.570780754f, 0.569520533f, 0.568258941f, 0.566996038f, + 0.565731823f, 0.564466238f, 0.563199341f, 0.561931133f, + 0.560661554f, 0.559390724f, 0.558118522f, 0.556845009f, + 0.555570245f, 0.554294109f, 0.553016722f, 0.551737964f, + 0.550457954f, 0.549176633f, 0.547894061f, 0.546610177f, + 0.545324981f, 0.544038534f, 0.542750776f, 0.541461766f, + 0.540171444f, 0.538879931f, 0.537587047f, 0.536292970f, + 0.534997642f, 0.533701003f, 0.532403111f, 0.531104028f, + 0.529803634f, 0.528501987f, 0.527199149f, 0.525895000f, + 0.524589658f, 0.523283124f, 0.521975279f, 0.520666242f, + 0.519356012f, 0.518044531f, 0.516731799f, 0.515417874f, + 0.514102757f, 0.512786388f, 0.511468828f, 0.510150075f, + 0.508830130f, 0.507508993f, 0.506186664f, 0.504863083f, + 0.503538370f, 0.502212465f, 0.500885367f, 0.499557108f, + 0.498227656f, 0.496897042f, 0.495565265f, 0.494232297f, + 0.492898196f, 0.491562903f, 0.490226477f, 0.488888890f, + 0.487550169f, 0.486210287f, 0.484869242f, 0.483527064f, + 0.482183784f, 0.480839342f, 0.479493767f, 0.478147060f, + 0.476799220f, 0.475450277f, 0.474100202f, 0.472749025f, + 0.471396744f, 0.470043331f, 0.468688816f, 0.467333198f, + 0.465976506f, 0.464618683f, 0.463259786f, 0.461899787f, + 0.460538715f, 0.459176540f, 0.457813293f, 0.456448972f, + 0.455083579f, 0.453717113f, 0.452349573f, 0.450980991f, + 0.449611336f, 0.448240608f, 0.446868837f, 0.445496023f, + 0.444122136f, 0.442747235f, 0.441371262f, 0.439994276f, + 0.438616246f, 0.437237173f, 0.435857087f, 0.434475958f, + 0.433093816f, 0.431710660f, 0.430326492f, 0.428941280f, + 0.427555084f, 0.426167876f, 0.424779683f, 0.423390478f, + 0.422000259f, 0.420609087f, 0.419216901f, 0.417823702f, + 0.416429549f, 0.415034413f, 0.413638324f, 0.412241220f, + 0.410843164f, 0.409444153f, 0.408044159f, 0.406643212f, + 0.405241311f, 0.403838456f, 0.402434647f, 0.401029885f, + 0.399624199f, 0.398217559f, 0.396809995f, 0.395401478f, + 0.393992037f, 0.392581671f, 0.391170382f, 0.389758170f, + 0.388345033f, 0.386931002f, 0.385516047f, 0.384100199f, + 0.382683426f, 0.381265759f, 0.379847199f, 0.378427744f, + 0.377007425f, 0.375586182f, 0.374164075f, 0.372741073f, + 0.371317208f, 0.369892448f, 0.368466824f, 0.367040336f, + 0.365612984f, 0.364184797f, 0.362755716f, 0.361325800f, + 0.359895051f, 0.358463407f, 0.357030958f, 0.355597675f, + 0.354163527f, 0.352728546f, 0.351292759f, 0.349856138f, + 0.348418683f, 0.346980423f, 0.345541328f, 0.344101429f, + 0.342660725f, 0.341219217f, 0.339776874f, 0.338333756f, + 0.336889863f, 0.335445136f, 0.333999664f, 0.332553357f, + 0.331106305f, 0.329658449f, 0.328209847f, 0.326760441f, + 0.325310290f, 0.323859364f, 0.322407693f, 0.320955247f, + 0.319502026f, 0.318048090f, 0.316593379f, 0.315137923f, + 0.313681751f, 0.312224805f, 0.310767144f, 0.309308767f, + 0.307849646f, 0.306389809f, 0.304929227f, 0.303467959f, + 0.302005947f, 0.300543249f, 0.299079835f, 0.297615707f, + 0.296150893f, 0.294685364f, 0.293219149f, 0.291752249f, + 0.290284663f, 0.288816422f, 0.287347466f, 0.285877824f, + 0.284407526f, 0.282936573f, 0.281464934f, 0.279992640f, + 0.278519690f, 0.277046084f, 0.275571823f, 0.274096906f, + 0.272621363f, 0.271145165f, 0.269668311f, 0.268190861f, + 0.266712755f, 0.265234023f, 0.263754666f, 0.262274712f, + 0.260794103f, 0.259312928f, 0.257831097f, 0.256348670f, + 0.254865646f, 0.253382027f, 0.251897812f, 0.250413001f, + 0.248927608f, 0.247441620f, 0.245955050f, 0.244467899f, + 0.242980182f, 0.241491884f, 0.240003020f, 0.238513589f, + 0.237023607f, 0.235533059f, 0.234041959f, 0.232550308f, + 0.231058106f, 0.229565367f, 0.228072077f, 0.226578265f, + 0.225083917f, 0.223589033f, 0.222093627f, 0.220597684f, + 0.219101235f, 0.217604280f, 0.216106802f, 0.214608818f, + 0.213110313f, 0.211611331f, 0.210111842f, 0.208611846f, + 0.207111374f, 0.205610409f, 0.204108968f, 0.202607036f, + 0.201104641f, 0.199601755f, 0.198098406f, 0.196594596f, + 0.195090324f, 0.193585590f, 0.192080393f, 0.190574750f, + 0.189068660f, 0.187562123f, 0.186055154f, 0.184547737f, + 0.183039889f, 0.181531608f, 0.180022895f, 0.178513765f, + 0.177004218f, 0.175494254f, 0.173983872f, 0.172473088f, + 0.170961887f, 0.169450298f, 0.167938292f, 0.166425899f, + 0.164913118f, 0.163399950f, 0.161886394f, 0.160372451f, + 0.158858150f, 0.157343462f, 0.155828401f, 0.154312968f, + 0.152797192f, 0.151281044f, 0.149764538f, 0.148247674f, + 0.146730468f, 0.145212919f, 0.143695027f, 0.142176807f, + 0.140658244f, 0.139139339f, 0.137620121f, 0.136100575f, + 0.134580702f, 0.133060530f, 0.131540030f, 0.130019218f, + 0.128498107f, 0.126976699f, 0.125454977f, 0.123932973f, + 0.122410677f, 0.120888084f, 0.119365215f, 0.117842063f, + 0.116318628f, 0.114794925f, 0.113270953f, 0.111746714f, + 0.110222206f, 0.108697444f, 0.107172422f, 0.105647154f, + 0.104121633f, 0.102595866f, 0.101069860f, 0.099543616f, + 0.098017141f, 0.096490428f, 0.094963498f, 0.093436338f, + 0.091908954f, 0.090381362f, 0.088853553f, 0.087325536f, + 0.085797310f, 0.084268890f, 0.082740262f, 0.081211448f, + 0.079682440f, 0.078153245f, 0.076623864f, 0.075094298f, + 0.073564567f, 0.072034650f, 0.070504576f, 0.068974331f, + 0.067443922f, 0.065913349f, 0.064382628f, 0.062851757f, + 0.061320737f, 0.059789572f, 0.058258265f, 0.056726821f, + 0.055195246f, 0.053663537f, 0.052131705f, 0.050599750f, + 0.049067676f, 0.047535483f, 0.046003181f, 0.044470772f, + 0.042938258f, 0.041405641f, 0.039872926f, 0.038340122f, + 0.036807224f, 0.035274237f, 0.033741172f, 0.032208025f, + 0.030674804f, 0.029141508f, 0.027608145f, 0.026074719f, + 0.024541229f, 0.023007682f, 0.021474080f, 0.019940428f, + 0.018406730f, 0.016872987f, 0.015339206f, 0.0138053885f, + 0.012271538f, 0.010737659f, 0.009203754f, 0.007669829f, + 0.0061358847f,0.004601926f, 0.0030679568f,0.0015339801f, + + // negative sine + 0.000000000f, -0.0015339801f,-0.0030679568f,-0.004601926f, + -0.0061358847f,-0.007669829f, -0.009203754f, -0.010737659f, + -0.012271538f, -0.0138053885f,-0.015339206f, -0.016872987f, + -0.018406730f, -0.019940428f, -0.021474080f, -0.023007682f, + -0.024541229f, -0.026074719f, -0.027608145f, -0.029141508f, + -0.030674804f, -0.032208025f, -0.033741172f, -0.035274237f, + -0.036807224f, -0.038340122f, -0.039872926f, -0.041405641f, + -0.042938258f, -0.044470772f, -0.046003181f, -0.047535483f, + -0.049067676f, -0.050599750f, -0.052131705f, -0.053663537f, + -0.055195246f, -0.056726821f, -0.058258265f, -0.059789572f, + -0.061320737f, -0.062851757f, -0.064382628f, -0.065913349f, + -0.067443922f, -0.068974331f, -0.070504576f, -0.072034650f, + -0.073564567f, -0.075094298f, -0.076623864f, -0.078153245f, + -0.079682440f, -0.081211448f, -0.082740262f, -0.084268890f, + -0.085797310f, -0.087325536f, -0.088853553f, -0.090381362f, + -0.091908954f, -0.093436338f, -0.094963498f, -0.096490428f, + -0.098017141f, -0.099543616f, -0.101069860f, -0.102595866f, + -0.104121633f, -0.105647154f, -0.107172422f, -0.108697444f, + -0.110222206f, -0.111746714f, -0.113270953f, -0.114794925f, + -0.116318628f, -0.117842063f, -0.119365215f, -0.120888084f, + -0.122410677f, -0.123932973f, -0.125454977f, -0.126976699f, + -0.128498107f, -0.130019218f, -0.131540030f, -0.133060530f, + -0.134580702f, -0.136100575f, -0.137620121f, -0.139139339f, + -0.140658244f, -0.142176807f, -0.143695027f, -0.145212919f, + -0.146730468f, -0.148247674f, -0.149764538f, -0.151281044f, + -0.152797192f, -0.154312968f, -0.155828401f, -0.157343462f, + -0.158858150f, -0.160372451f, -0.161886394f, -0.163399950f, + -0.164913118f, -0.166425899f, -0.167938292f, -0.169450298f, + -0.170961887f, -0.172473088f, -0.173983872f, -0.175494254f, + -0.177004218f, -0.178513765f, -0.180022895f, -0.181531608f, + -0.183039889f, -0.184547737f, -0.186055154f, -0.187562123f, + -0.189068660f, -0.190574750f, -0.192080393f, -0.193585590f, + -0.195090324f, -0.196594596f, -0.198098406f, -0.199601755f, + -0.201104641f, -0.202607036f, -0.204108968f, -0.205610409f, + -0.207111374f, -0.208611846f, -0.210111842f, -0.211611331f, + -0.213110313f, -0.214608818f, -0.216106802f, -0.217604280f, + -0.219101235f, -0.220597684f, -0.222093627f, -0.223589033f, + -0.225083917f, -0.226578265f, -0.228072077f, -0.229565367f, + -0.231058106f, -0.232550308f, -0.234041959f, -0.235533059f, + -0.237023607f, -0.238513589f, -0.240003020f, -0.241491884f, + -0.242980182f, -0.244467899f, -0.245955050f, -0.247441620f, + -0.248927608f, -0.250413001f, -0.251897812f, -0.253382027f, + -0.254865646f, -0.256348670f, -0.257831097f, -0.259312928f, + -0.260794103f, -0.262274712f, -0.263754666f, -0.265234023f, + -0.266712755f, -0.268190861f, -0.269668311f, -0.271145165f, + -0.272621363f, -0.274096906f, -0.275571823f, -0.277046084f, + -0.278519690f, -0.279992640f, -0.281464934f, -0.282936573f, + -0.284407526f, -0.285877824f, -0.287347466f, -0.288816422f, + -0.290284663f, -0.291752249f, -0.293219149f, -0.294685364f, + -0.296150893f, -0.297615707f, -0.299079835f, -0.300543249f, + -0.302005947f, -0.303467959f, -0.304929227f, -0.306389809f, + -0.307849646f, -0.309308767f, -0.310767144f, -0.312224805f, + -0.313681751f, -0.315137923f, -0.316593379f, -0.318048090f, + -0.319502026f, -0.320955247f, -0.322407693f, -0.323859364f, + -0.325310290f, -0.326760441f, -0.328209847f, -0.329658449f, + -0.331106305f, -0.332553357f, -0.333999664f, -0.335445136f, + -0.336889863f, -0.338333756f, -0.339776874f, -0.341219217f, + -0.342660725f, -0.344101429f, -0.345541328f, -0.346980423f, + -0.348418683f, -0.349856138f, -0.351292759f, -0.352728546f, + -0.354163527f, -0.355597675f, -0.357030958f, -0.358463407f, + -0.359895051f, -0.361325800f, -0.362755716f, -0.364184797f, + -0.365612984f, -0.367040336f, -0.368466824f, -0.369892448f, + -0.371317208f, -0.372741073f, -0.374164075f, -0.375586182f, + -0.377007425f, -0.378427744f, -0.379847199f, -0.381265759f, + -0.382683426f, -0.384100199f, -0.385516047f, -0.386931002f, + -0.388345033f, -0.389758170f, -0.391170382f, -0.392581671f, + -0.393992037f, -0.395401478f, -0.396809995f, -0.398217559f, + -0.399624199f, -0.401029885f, -0.402434647f, -0.403838456f, + -0.405241311f, -0.406643212f, -0.408044159f, -0.409444153f, + -0.410843164f, -0.412241220f, -0.413638324f, -0.415034413f, + -0.416429549f, -0.417823702f, -0.419216901f, -0.420609087f, + -0.422000259f, -0.423390478f, -0.424779683f, -0.426167876f, + -0.427555084f, -0.428941280f, -0.430326492f, -0.431710660f, + -0.433093816f, -0.434475958f, -0.435857087f, -0.437237173f, + -0.438616246f, -0.439994276f, -0.441371262f, -0.442747235f, + -0.444122136f, -0.445496023f, -0.446868837f, -0.448240608f, + -0.449611336f, -0.450980991f, -0.452349573f, -0.453717113f, + -0.455083579f, -0.456448972f, -0.457813293f, -0.459176540f, + -0.460538715f, -0.461899787f, -0.463259786f, -0.464618683f, + -0.465976506f, -0.467333198f, -0.468688816f, -0.470043331f, + -0.471396744f, -0.472749025f, -0.474100202f, -0.475450277f, + -0.476799220f, -0.478147060f, -0.479493767f, -0.480839342f, + -0.482183784f, -0.483527064f, -0.484869242f, -0.486210287f, + -0.487550169f, -0.488888890f, -0.490226477f, -0.491562903f, + -0.492898196f, -0.494232297f, -0.495565265f, -0.496897042f, + -0.498227656f, -0.499557108f, -0.500885367f, -0.502212465f, + -0.503538370f, -0.504863083f, -0.506186664f, -0.507508993f, + -0.508830130f, -0.510150075f, -0.511468828f, -0.512786388f, + -0.514102757f, -0.515417874f, -0.516731799f, -0.518044531f, + -0.519356012f, -0.520666242f, -0.521975279f, -0.523283124f, + -0.524589658f, -0.525895000f, -0.527199149f, -0.528501987f, + -0.529803634f, -0.531104028f, -0.532403111f, -0.533701003f, + -0.534997642f, -0.536292970f, -0.537587047f, -0.538879931f, + -0.540171444f, -0.541461766f, -0.542750776f, -0.544038534f, + -0.545324981f, -0.546610177f, -0.547894061f, -0.549176633f, + -0.550457954f, -0.551737964f, -0.553016722f, -0.554294109f, + -0.555570245f, -0.556845009f, -0.558118522f, -0.559390724f, + -0.560661554f, -0.561931133f, -0.563199341f, -0.564466238f, + -0.565731823f, -0.566996038f, -0.568258941f, -0.569520533f, + -0.570780754f, -0.572039604f, -0.573297143f, -0.574553370f, + -0.575808167f, -0.577061653f, -0.578313768f, -0.579564571f, + -0.580813944f, -0.582062006f, -0.583308637f, -0.584553957f, + -0.585797846f, -0.587040365f, -0.588281572f, -0.589521289f, + -0.590759695f, -0.591996670f, -0.593232274f, -0.594466507f, + -0.595699310f, -0.596930683f, -0.598160684f, -0.599389315f, + -0.600616455f, -0.601842225f, -0.603066623f, -0.604289532f, + -0.605511069f, -0.606731117f, -0.607949793f, -0.609167039f, + -0.610382795f, -0.611597180f, -0.612810075f, -0.614021540f, + -0.615231574f, -0.616440177f, -0.617647290f, -0.618852973f, + -0.620057225f, -0.621259987f, -0.622461259f, -0.623661101f, + -0.624859512f, -0.626056373f, -0.627251804f, -0.628445745f, + -0.629638255f, -0.630829215f, -0.632018745f, -0.633206785f, + -0.634393275f, -0.635578334f, -0.636761844f, -0.637943923f, + -0.639124453f, -0.640303493f, -0.641481042f, -0.642657042f, + -0.643831551f, -0.645004511f, -0.646176040f, -0.647345960f, + -0.648514390f, -0.649681330f, -0.650846660f, -0.652010560f, + -0.653172851f, -0.654333591f, -0.655492842f, -0.656650543f, + -0.657806695f, -0.658961296f, -0.660114348f, -0.661265850f, + -0.662415802f, -0.663564146f, -0.664710999f, -0.665856242f, + -0.666999936f, -0.668142021f, -0.669282615f, -0.670421541f, + -0.671558976f, -0.672694743f, -0.673829019f, -0.674961627f, + -0.676092684f, -0.677222192f, -0.678350031f, -0.679476321f, + -0.680601001f, -0.681724072f, -0.682845533f, -0.683965385f, + -0.685083687f, -0.686200321f, -0.687315345f, -0.688428760f, + -0.689540565f, -0.690650702f, -0.691759229f, -0.692866147f, + -0.693971455f, -0.695075095f, -0.696177125f, -0.697277486f, + -0.698376238f, -0.699473321f, -0.700568795f, -0.701662600f, + -0.702754736f, -0.703845263f, -0.704934061f, -0.706021249f, + -0.707106769f, -0.708190620f, -0.709272802f, -0.710353374f, + -0.711432219f, -0.712509394f, -0.713584840f, -0.714658678f, + -0.715730846f, -0.716801286f, -0.717870057f, -0.718937099f, + -0.720002532f, -0.721066177f, -0.722128212f, -0.723188460f, + -0.724247098f, -0.725303948f, -0.726359129f, -0.727412641f, + -0.728464365f, -0.729514420f, -0.730562747f, -0.731609404f, + -0.732654274f, -0.733697414f, -0.734738886f, -0.735778570f, + -0.736816585f, -0.737852812f, -0.738887310f, -0.739920080f, + -0.740951121f, -0.741980433f, -0.743007958f, -0.744033754f, + -0.745057762f, -0.746080101f, -0.747100592f, -0.748119354f, + -0.749136388f, -0.750151634f, -0.751165152f, -0.752176821f, + -0.753186822f, -0.754194975f, -0.755201399f, -0.756205976f, + -0.757208824f, -0.758209884f, -0.759209216f, -0.760206699f, + -0.761202395f, -0.762196302f, -0.763188422f, -0.764178753f, + -0.765167236f, -0.766153991f, -0.767138898f, -0.768122017f, + -0.769103348f, -0.770082831f, -0.771060526f, -0.772036374f, + -0.773010433f, -0.773982704f, -0.774953127f, -0.775921702f, + -0.776888490f, -0.777853429f, -0.778816521f, -0.779777765f, + -0.780737221f, -0.781694829f, -0.782650590f, -0.783604503f, + -0.784556568f, -0.785506845f, -0.786455214f, -0.787401736f, + -0.788346410f, -0.789289236f, -0.790230215f, -0.791169345f, + -0.792106569f, -0.793041945f, -0.793975472f, -0.794907153f, + -0.795836926f, -0.796764791f, -0.797690868f, -0.798614979f, + -0.799537241f, -0.800457656f, -0.801376164f, -0.802292824f, + -0.803207517f, -0.804120362f, -0.805031359f, -0.805940390f, + -0.806847572f, -0.807752848f, -0.808656156f, -0.809557617f, + -0.810457170f, -0.811354876f, -0.812250614f, -0.813144386f, + -0.814036310f, -0.814926326f, -0.815814435f, -0.816700578f, + -0.817584813f, -0.818467140f, -0.819347501f, -0.820225954f, + -0.821102500f, -0.821977139f, -0.822849810f, -0.823720515f, + -0.824589312f, -0.825456142f, -0.826321065f, -0.827184021f, + -0.828045070f, -0.828904092f, -0.829761207f, -0.830616415f, + -0.831469595f, -0.832320869f, -0.833170176f, -0.834017515f, + -0.834862888f, -0.835706294f, -0.836547732f, -0.837387204f, + -0.838224709f, -0.839060247f, -0.839893818f, -0.840725362f, + -0.841554999f, -0.842382610f, -0.843208253f, -0.844031870f, + -0.844853580f, -0.845673263f, -0.846490920f, -0.847306609f, + -0.848120332f, -0.848932028f, -0.849741757f, -0.850549459f, + -0.851355195f, -0.852158904f, -0.852960587f, -0.853760302f, + -0.854557991f, -0.855353653f, -0.856147349f, -0.856938958f, + -0.857728601f, -0.858516216f, -0.859301805f, -0.860085368f, + -0.860866964f, -0.861646473f, -0.862423956f, -0.863199413f, + -0.863972843f, -0.864744246f, -0.865513623f, -0.866280973f, + -0.867046237f, -0.867809474f, -0.868570685f, -0.869329870f, + -0.870086968f, -0.870842040f, -0.871595085f, -0.872346044f, + -0.873094976f, -0.873841822f, -0.874586642f, -0.875329375f, + -0.876070082f, -0.876808703f, -0.877545297f, -0.878279805f, + -0.879012227f, -0.879742622f, -0.880470872f, -0.881197095f, + -0.881921291f, -0.882643342f, -0.883363366f, -0.884081244f, + -0.884797096f, -0.885510862f, -0.886222541f, -0.886932135f, + -0.887639642f, -0.888345063f, -0.889048338f, -0.889749587f, + -0.890448749f, -0.891145766f, -0.891840696f, -0.892533541f, + -0.893224299f, -0.893912971f, -0.894599497f, -0.895283937f, + -0.895966232f, -0.896646500f, -0.897324562f, -0.898000598f, + -0.898674488f, -0.899346232f, -0.900015891f, -0.900683403f, + -0.901348829f, -0.902012169f, -0.902673304f, -0.903332353f, + -0.903989315f, -0.904644072f, -0.905296743f, -0.905947268f, + -0.906595707f, -0.907242000f, -0.907886088f, -0.908528090f, + -0.909168005f, -0.909805715f, -0.910441279f, -0.911074758f, + -0.911706030f, -0.912335157f, -0.912962198f, -0.913587034f, + -0.914209783f, -0.914830327f, -0.915448725f, -0.916064978f, + -0.916679084f, -0.917290986f, -0.917900801f, -0.918508410f, + -0.919113874f, -0.919717133f, -0.920318305f, -0.920917213f, + -0.921514034f, -0.922108650f, -0.922701120f, -0.923291445f, + -0.923879504f, -0.924465477f, -0.925049245f, -0.925630808f, + -0.926210225f, -0.926787496f, -0.927362502f, -0.927935421f, + -0.928506076f, -0.929074585f, -0.929640889f, -0.930205047f, + -0.930766940f, -0.931326687f, -0.931884289f, -0.932439625f, + -0.932992816f, -0.933543801f, -0.934092522f, -0.934639156f, + -0.935183525f, -0.935725689f, -0.936265647f, -0.936803460f, + -0.937339008f, -0.937872350f, -0.938403547f, -0.938932478f, + -0.939459205f, -0.939983726f, -0.940506041f, -0.941026151f, + -0.941544056f, -0.942059755f, -0.942573190f, -0.943084419f, + -0.943593442f, -0.944100261f, -0.944604814f, -0.945107222f, + -0.945607305f, -0.946105242f, -0.946600914f, -0.947094381f, + -0.947585583f, -0.948074579f, -0.948561370f, -0.949045897f, + -0.949528158f, -0.950008273f, -0.950486064f, -0.950961649f, + -0.951435030f, -0.951906145f, -0.952374995f, -0.952841640f, + -0.953306019f, -0.953768194f, -0.954228103f, -0.954685748f, + -0.955141187f, -0.955594361f, -0.956045270f, -0.956493914f, + -0.956940353f, -0.957384527f, -0.957826436f, -0.958266079f, + -0.958703458f, -0.959138632f, -0.959571540f, -0.960002124f, + -0.960430503f, -0.960856616f, -0.961280465f, -0.961702049f, + -0.962121427f, -0.962538481f, -0.962953269f, -0.963365793f, + -0.963776052f, -0.964184046f, -0.964589775f, -0.964993238f, + -0.965394437f, -0.965793371f, -0.966189981f, -0.966584384f, + -0.966976464f, -0.967366278f, -0.967753828f, -0.968139112f, + -0.968522072f, -0.968902826f, -0.969281256f, -0.969657362f, + -0.970031261f, -0.970402837f, -0.970772147f, -0.971139133f, + -0.971503913f, -0.971866310f, -0.972226501f, -0.972584367f, + -0.972939968f, -0.973293245f, -0.973644257f, -0.973992944f, + -0.974339366f, -0.974683523f, -0.975025356f, -0.975364864f, + -0.975702107f, -0.976037085f, -0.976369739f, -0.976700068f, + -0.977028131f, -0.977353871f, -0.977677345f, -0.977998495f, + -0.978317380f, -0.978633940f, -0.978948176f, -0.979260147f, + -0.979569793f, -0.979877114f, -0.980182111f, -0.980484843f, + -0.980785251f, -0.981083393f, -0.981379211f, -0.981672704f, + -0.981963873f, -0.982252717f, -0.982539296f, -0.982823551f, + -0.983105481f, -0.983385086f, -0.983662426f, -0.983937442f, + -0.984210074f, -0.984480441f, -0.984748483f, -0.985014260f, + -0.985277653f, -0.985538721f, -0.985797524f, -0.986053944f, + -0.986308098f, -0.986559927f, -0.986809373f, -0.987056553f, + -0.987301409f, -0.987543941f, -0.987784147f, -0.988022029f, + -0.988257587f, -0.988490820f, -0.988721669f, -0.988950253f, + -0.989176512f, -0.989400446f, -0.989621997f, -0.989841282f, + -0.990058184f, -0.990272820f, -0.990485072f, -0.990695000f, + -0.990902662f, -0.991107941f, -0.991310835f, -0.991511464f, + -0.991709769f, -0.991905689f, -0.992099285f, -0.992290616f, + -0.992479563f, -0.992666125f, -0.992850423f, -0.993032336f, + -0.993211925f, -0.993389189f, -0.993564129f, -0.993736744f, + -0.993906975f, -0.994074881f, -0.994240463f, -0.994403660f, + -0.994564593f, -0.994723141f, -0.994879305f, -0.995033205f, + -0.995184720f, -0.995333910f, -0.995480776f, -0.995625257f, + -0.995767415f, -0.995907247f, -0.996044695f, -0.996179819f, + -0.996312618f, -0.996443033f, -0.996571124f, -0.996696889f, + -0.996820271f, -0.996941328f, -0.997060061f, -0.997176409f, + -0.997290432f, -0.997402132f, -0.997511446f, -0.997618437f, + -0.997723043f, -0.997825325f, -0.997925282f, -0.998022854f, + -0.998118103f, -0.998211026f, -0.998301566f, -0.998389721f, + -0.998475552f, -0.998559058f, -0.998640239f, -0.998719037f, + -0.998795450f, -0.998869538f, -0.998941302f, -0.999010682f, + -0.999077737f, -0.999142408f, -0.999204755f, -0.999264777f, + -0.999322355f, -0.999377668f, -0.999430597f, -0.999481201f, + -0.999529421f, -0.999575317f, -0.999618828f, -0.999660015f, + -0.999698818f, -0.999735296f, -0.999769390f, -0.999801159f, + -0.999830604f, -0.999857664f, -0.999882340f, -0.999904692f, + -0.999924719f, -0.999942362f, -0.999957621f, -0.999970615f, + -0.999981165f, -0.999989390f, -0.999995291f, -0.999998808f, + + // negative cosine + -1.000000000f, -0.999998808f, -0.999995291f, -0.999989390f, + -0.999981165f, -0.999970615f, -0.999957621f, -0.999942362f, + -0.999924719f, -0.999904692f, -0.999882340f, -0.999857664f, + -0.999830604f, -0.999801159f, -0.999769390f, -0.999735296f, + -0.999698818f, -0.999660015f, -0.999618828f, -0.999575317f, + -0.999529421f, -0.999481201f, -0.999430597f, -0.999377668f, + -0.999322355f, -0.999264777f, -0.999204755f, -0.999142408f, + -0.999077737f, -0.999010682f, -0.998941302f, -0.998869538f, + -0.998795450f, -0.998719037f, -0.998640239f, -0.998559058f, + -0.998475552f, -0.998389721f, -0.998301566f, -0.998211026f, + -0.998118103f, -0.998022854f, -0.997925282f, -0.997825325f, + -0.997723043f, -0.997618437f, -0.997511446f, -0.997402132f, + -0.997290432f, -0.997176409f, -0.997060061f, -0.996941328f, + -0.996820271f, -0.996696889f, -0.996571124f, -0.996443033f, + -0.996312618f, -0.996179819f, -0.996044695f, -0.995907247f, + -0.995767415f, -0.995625257f, -0.995480776f, -0.995333910f, + -0.995184720f, -0.995033205f, -0.994879305f, -0.994723141f, + -0.994564593f, -0.994403660f, -0.994240463f, -0.994074881f, + -0.993906975f, -0.993736744f, -0.993564129f, -0.993389189f, + -0.993211925f, -0.993032336f, -0.992850423f, -0.992666125f, + -0.992479563f, -0.992290616f, -0.992099285f, -0.991905689f, + -0.991709769f, -0.991511464f, -0.991310835f, -0.991107941f, + -0.990902662f, -0.990695000f, -0.990485072f, -0.990272820f, + -0.990058184f, -0.989841282f, -0.989621997f, -0.989400446f, + -0.989176512f, -0.988950253f, -0.988721669f, -0.988490820f, + -0.988257587f, -0.988022029f, -0.987784147f, -0.987543941f, + -0.987301409f, -0.987056553f, -0.986809373f, -0.986559927f, + -0.986308098f, -0.986053944f, -0.985797524f, -0.985538721f, + -0.985277653f, -0.985014260f, -0.984748483f, -0.984480441f, + -0.984210074f, -0.983937442f, -0.983662426f, -0.983385086f, + -0.983105481f, -0.982823551f, -0.982539296f, -0.982252717f, + -0.981963873f, -0.981672704f, -0.981379211f, -0.981083393f, + -0.980785251f, -0.980484843f, -0.980182111f, -0.979877114f, + -0.979569793f, -0.979260147f, -0.978948176f, -0.978633940f, + -0.978317380f, -0.977998495f, -0.977677345f, -0.977353871f, + -0.977028131f, -0.976700068f, -0.976369739f, -0.976037085f, + -0.975702107f, -0.975364864f, -0.975025356f, -0.974683523f, + -0.974339366f, -0.973992944f, -0.973644257f, -0.973293245f, + -0.972939968f, -0.972584367f, -0.972226501f, -0.971866310f, + -0.971503913f, -0.971139133f, -0.970772147f, -0.970402837f, + -0.970031261f, -0.969657362f, -0.969281256f, -0.968902826f, + -0.968522072f, -0.968139112f, -0.967753828f, -0.967366278f, + -0.966976464f, -0.966584384f, -0.966189981f, -0.965793371f, + -0.965394437f, -0.964993238f, -0.964589775f, -0.964184046f, + -0.963776052f, -0.963365793f, -0.962953269f, -0.962538481f, + -0.962121427f, -0.961702049f, -0.961280465f, -0.960856616f, + -0.960430503f, -0.960002124f, -0.959571540f, -0.959138632f, + -0.958703458f, -0.958266079f, -0.957826436f, -0.957384527f, + -0.956940353f, -0.956493914f, -0.956045270f, -0.955594361f, + -0.955141187f, -0.954685748f, -0.954228103f, -0.953768194f, + -0.953306019f, -0.952841640f, -0.952374995f, -0.951906145f, + -0.951435030f, -0.950961649f, -0.950486064f, -0.950008273f, + -0.949528158f, -0.949045897f, -0.948561370f, -0.948074579f, + -0.947585583f, -0.947094381f, -0.946600914f, -0.946105242f, + -0.945607305f, -0.945107222f, -0.944604814f, -0.944100261f, + -0.943593442f, -0.943084419f, -0.942573190f, -0.942059755f, + -0.941544056f, -0.941026151f, -0.940506041f, -0.939983726f, + -0.939459205f, -0.938932478f, -0.938403547f, -0.937872350f, + -0.937339008f, -0.936803460f, -0.936265647f, -0.935725689f, + -0.935183525f, -0.934639156f, -0.934092522f, -0.933543801f, + -0.932992816f, -0.932439625f, -0.931884289f, -0.931326687f, + -0.930766940f, -0.930205047f, -0.929640889f, -0.929074585f, + -0.928506076f, -0.927935421f, -0.927362502f, -0.926787496f, + -0.926210225f, -0.925630808f, -0.925049245f, -0.924465477f, + -0.923879504f, -0.923291445f, -0.922701120f, -0.922108650f, + -0.921514034f, -0.920917213f, -0.920318305f, -0.919717133f, + -0.919113874f, -0.918508410f, -0.917900801f, -0.917290986f, + -0.916679084f, -0.916064978f, -0.915448725f, -0.914830327f, + -0.914209783f, -0.913587034f, -0.912962198f, -0.912335157f, + -0.911706030f, -0.911074758f, -0.910441279f, -0.909805715f, + -0.909168005f, -0.908528090f, -0.907886088f, -0.907242000f, + -0.906595707f, -0.905947268f, -0.905296743f, -0.904644072f, + -0.903989315f, -0.903332353f, -0.902673304f, -0.902012169f, + -0.901348829f, -0.900683403f, -0.900015891f, -0.899346232f, + -0.898674488f, -0.898000598f, -0.897324562f, -0.896646500f, + -0.895966232f, -0.895283937f, -0.894599497f, -0.893912971f, + -0.893224299f, -0.892533541f, -0.891840696f, -0.891145766f, + -0.890448749f, -0.889749587f, -0.889048338f, -0.888345063f, + -0.887639642f, -0.886932135f, -0.886222541f, -0.885510862f, + -0.884797096f, -0.884081244f, -0.883363366f, -0.882643342f, + -0.881921291f, -0.881197095f, -0.880470872f, -0.879742622f, + -0.879012227f, -0.878279805f, -0.877545297f, -0.876808703f, + -0.876070082f, -0.875329375f, -0.874586642f, -0.873841822f, + -0.873094976f, -0.872346044f, -0.871595085f, -0.870842040f, + -0.870086968f, -0.869329870f, -0.868570685f, -0.867809474f, + -0.867046237f, -0.866280973f, -0.865513623f, -0.864744246f, + -0.863972843f, -0.863199413f, -0.862423956f, -0.861646473f, + -0.860866964f, -0.860085368f, -0.859301805f, -0.858516216f, + -0.857728601f, -0.856938958f, -0.856147349f, -0.855353653f, + -0.854557991f, -0.853760302f, -0.852960587f, -0.852158904f, + -0.851355195f, -0.850549459f, -0.849741757f, -0.848932028f, + -0.848120332f, -0.847306609f, -0.846490920f, -0.845673263f, + -0.844853580f, -0.844031870f, -0.843208253f, -0.842382610f, + -0.841554999f, -0.840725362f, -0.839893818f, -0.839060247f, + -0.838224709f, -0.837387204f, -0.836547732f, -0.835706294f, + -0.834862888f, -0.834017515f, -0.833170176f, -0.832320869f, + -0.831469595f, -0.830616415f, -0.829761207f, -0.828904092f, + -0.828045070f, -0.827184021f, -0.826321065f, -0.825456142f, + -0.824589312f, -0.823720515f, -0.822849810f, -0.821977139f, + -0.821102500f, -0.820225954f, -0.819347501f, -0.818467140f, + -0.817584813f, -0.816700578f, -0.815814435f, -0.814926326f, + -0.814036310f, -0.813144386f, -0.812250614f, -0.811354876f, + -0.810457170f, -0.809557617f, -0.808656156f, -0.807752848f, + -0.806847572f, -0.805940390f, -0.805031359f, -0.804120362f, + -0.803207517f, -0.802292824f, -0.801376164f, -0.800457656f, + -0.799537241f, -0.798614979f, -0.797690868f, -0.796764791f, + -0.795836926f, -0.794907153f, -0.793975472f, -0.793041945f, + -0.792106569f, -0.791169345f, -0.790230215f, -0.789289236f, + -0.788346410f, -0.787401736f, -0.786455214f, -0.785506845f, + -0.784556568f, -0.783604503f, -0.782650590f, -0.781694829f, + -0.780737221f, -0.779777765f, -0.778816521f, -0.777853429f, + -0.776888490f, -0.775921702f, -0.774953127f, -0.773982704f, + -0.773010433f, -0.772036374f, -0.771060526f, -0.770082831f, + -0.769103348f, -0.768122017f, -0.767138898f, -0.766153991f, + -0.765167236f, -0.764178753f, -0.763188422f, -0.762196302f, + -0.761202395f, -0.760206699f, -0.759209216f, -0.758209884f, + -0.757208824f, -0.756205976f, -0.755201399f, -0.754194975f, + -0.753186822f, -0.752176821f, -0.751165152f, -0.750151634f, + -0.749136388f, -0.748119354f, -0.747100592f, -0.746080101f, + -0.745057762f, -0.744033754f, -0.743007958f, -0.741980433f, + -0.740951121f, -0.739920080f, -0.738887310f, -0.737852812f, + -0.736816585f, -0.735778570f, -0.734738886f, -0.733697414f, + -0.732654274f, -0.731609404f, -0.730562747f, -0.729514420f, + -0.728464365f, -0.727412641f, -0.726359129f, -0.725303948f, + -0.724247098f, -0.723188460f, -0.722128212f, -0.721066177f, + -0.720002532f, -0.718937099f, -0.717870057f, -0.716801286f, + -0.715730846f, -0.714658678f, -0.713584840f, -0.712509394f, + -0.711432219f, -0.710353374f, -0.709272802f, -0.708190620f, + -0.707106769f, -0.706021249f, -0.704934061f, -0.703845263f, + -0.702754736f, -0.701662600f, -0.700568795f, -0.699473321f, + -0.698376238f, -0.697277486f, -0.696177125f, -0.695075095f, + -0.693971455f, -0.692866147f, -0.691759229f, -0.690650702f, + -0.689540565f, -0.688428760f, -0.687315345f, -0.686200321f, + -0.685083687f, -0.683965385f, -0.682845533f, -0.681724072f, + -0.680601001f, -0.679476321f, -0.678350031f, -0.677222192f, + -0.676092684f, -0.674961627f, -0.673829019f, -0.672694743f, + -0.671558976f, -0.670421541f, -0.669282615f, -0.668142021f, + -0.666999936f, -0.665856242f, -0.664710999f, -0.663564146f, + -0.662415802f, -0.661265850f, -0.660114348f, -0.658961296f, + -0.657806695f, -0.656650543f, -0.655492842f, -0.654333591f, + -0.653172851f, -0.652010560f, -0.650846660f, -0.649681330f, + -0.648514390f, -0.647345960f, -0.646176040f, -0.645004511f, + -0.643831551f, -0.642657042f, -0.641481042f, -0.640303493f, + -0.639124453f, -0.637943923f, -0.636761844f, -0.635578334f, + -0.634393275f, -0.633206785f, -0.632018745f, -0.630829215f, + -0.629638255f, -0.628445745f, -0.627251804f, -0.626056373f, + -0.624859512f, -0.623661101f, -0.622461259f, -0.621259987f, + -0.620057225f, -0.618852973f, -0.617647290f, -0.616440177f, + -0.615231574f, -0.614021540f, -0.612810075f, -0.611597180f, + -0.610382795f, -0.609167039f, -0.607949793f, -0.606731117f, + -0.605511069f, -0.604289532f, -0.603066623f, -0.601842225f, + -0.600616455f, -0.599389315f, -0.598160684f, -0.596930683f, + -0.595699310f, -0.594466507f, -0.593232274f, -0.591996670f, + -0.590759695f, -0.589521289f, -0.588281572f, -0.587040365f, + -0.585797846f, -0.584553957f, -0.583308637f, -0.582062006f, + -0.580813944f, -0.579564571f, -0.578313768f, -0.577061653f, + -0.575808167f, -0.574553370f, -0.573297143f, -0.572039604f, + -0.570780754f, -0.569520533f, -0.568258941f, -0.566996038f, + -0.565731823f, -0.564466238f, -0.563199341f, -0.561931133f, + -0.560661554f, -0.559390724f, -0.558118522f, -0.556845009f, + -0.555570245f, -0.554294109f, -0.553016722f, -0.551737964f, + -0.550457954f, -0.549176633f, -0.547894061f, -0.546610177f, + -0.545324981f, -0.544038534f, -0.542750776f, -0.541461766f, + -0.540171444f, -0.538879931f, -0.537587047f, -0.536292970f, + -0.534997642f, -0.533701003f, -0.532403111f, -0.531104028f, + -0.529803634f, -0.528501987f, -0.527199149f, -0.525895000f, + -0.524589658f, -0.523283124f, -0.521975279f, -0.520666242f, + -0.519356012f, -0.518044531f, -0.516731799f, -0.515417874f, + -0.514102757f, -0.512786388f, -0.511468828f, -0.510150075f, + -0.508830130f, -0.507508993f, -0.506186664f, -0.504863083f, + -0.503538370f, -0.502212465f, -0.500885367f, -0.499557108f, + -0.498227656f, -0.496897042f, -0.495565265f, -0.494232297f, + -0.492898196f, -0.491562903f, -0.490226477f, -0.488888890f, + -0.487550169f, -0.486210287f, -0.484869242f, -0.483527064f, + -0.482183784f, -0.480839342f, -0.479493767f, -0.478147060f, + -0.476799220f, -0.475450277f, -0.474100202f, -0.472749025f, + -0.471396744f, -0.470043331f, -0.468688816f, -0.467333198f, + -0.465976506f, -0.464618683f, -0.463259786f, -0.461899787f, + -0.460538715f, -0.459176540f, -0.457813293f, -0.456448972f, + -0.455083579f, -0.453717113f, -0.452349573f, -0.450980991f, + -0.449611336f, -0.448240608f, -0.446868837f, -0.445496023f, + -0.444122136f, -0.442747235f, -0.441371262f, -0.439994276f, + -0.438616246f, -0.437237173f, -0.435857087f, -0.434475958f, + -0.433093816f, -0.431710660f, -0.430326492f, -0.428941280f, + -0.427555084f, -0.426167876f, -0.424779683f, -0.423390478f, + -0.422000259f, -0.420609087f, -0.419216901f, -0.417823702f, + -0.416429549f, -0.415034413f, -0.413638324f, -0.412241220f, + -0.410843164f, -0.409444153f, -0.408044159f, -0.406643212f, + -0.405241311f, -0.403838456f, -0.402434647f, -0.401029885f, + -0.399624199f, -0.398217559f, -0.396809995f, -0.395401478f, + -0.393992037f, -0.392581671f, -0.391170382f, -0.389758170f, + -0.388345033f, -0.386931002f, -0.385516047f, -0.384100199f, + -0.382683426f, -0.381265759f, -0.379847199f, -0.378427744f, + -0.377007425f, -0.375586182f, -0.374164075f, -0.372741073f, + -0.371317208f, -0.369892448f, -0.368466824f, -0.367040336f, + -0.365612984f, -0.364184797f, -0.362755716f, -0.361325800f, + -0.359895051f, -0.358463407f, -0.357030958f, -0.355597675f, + -0.354163527f, -0.352728546f, -0.351292759f, -0.349856138f, + -0.348418683f, -0.346980423f, -0.345541328f, -0.344101429f, + -0.342660725f, -0.341219217f, -0.339776874f, -0.338333756f, + -0.336889863f, -0.335445136f, -0.333999664f, -0.332553357f, + -0.331106305f, -0.329658449f, -0.328209847f, -0.326760441f, + -0.325310290f, -0.323859364f, -0.322407693f, -0.320955247f, + -0.319502026f, -0.318048090f, -0.316593379f, -0.315137923f, + -0.313681751f, -0.312224805f, -0.310767144f, -0.309308767f, + -0.307849646f, -0.306389809f, -0.304929227f, -0.303467959f, + -0.302005947f, -0.300543249f, -0.299079835f, -0.297615707f, + -0.296150893f, -0.294685364f, -0.293219149f, -0.291752249f, + -0.290284663f, -0.288816422f, -0.287347466f, -0.285877824f, + -0.284407526f, -0.282936573f, -0.281464934f, -0.279992640f, + -0.278519690f, -0.277046084f, -0.275571823f, -0.274096906f, + -0.272621363f, -0.271145165f, -0.269668311f, -0.268190861f, + -0.266712755f, -0.265234023f, -0.263754666f, -0.262274712f, + -0.260794103f, -0.259312928f, -0.257831097f, -0.256348670f, + -0.254865646f, -0.253382027f, -0.251897812f, -0.250413001f, + -0.248927608f, -0.247441620f, -0.245955050f, -0.244467899f, + -0.242980182f, -0.241491884f, -0.240003020f, -0.238513589f, + -0.237023607f, -0.235533059f, -0.234041959f, -0.232550308f, + -0.231058106f, -0.229565367f, -0.228072077f, -0.226578265f, + -0.225083917f, -0.223589033f, -0.222093627f, -0.220597684f, + -0.219101235f, -0.217604280f, -0.216106802f, -0.214608818f, + -0.213110313f, -0.211611331f, -0.210111842f, -0.208611846f, + -0.207111374f, -0.205610409f, -0.204108968f, -0.202607036f, + -0.201104641f, -0.199601755f, -0.198098406f, -0.196594596f, + -0.195090324f, -0.193585590f, -0.192080393f, -0.190574750f, + -0.189068660f, -0.187562123f, -0.186055154f, -0.184547737f, + -0.183039889f, -0.181531608f, -0.180022895f, -0.178513765f, + -0.177004218f, -0.175494254f, -0.173983872f, -0.172473088f, + -0.170961887f, -0.169450298f, -0.167938292f, -0.166425899f, + -0.164913118f, -0.163399950f, -0.161886394f, -0.160372451f, + -0.158858150f, -0.157343462f, -0.155828401f, -0.154312968f, + -0.152797192f, -0.151281044f, -0.149764538f, -0.148247674f, + -0.146730468f, -0.145212919f, -0.143695027f, -0.142176807f, + -0.140658244f, -0.139139339f, -0.137620121f, -0.136100575f, + -0.134580702f, -0.133060530f, -0.131540030f, -0.130019218f, + -0.128498107f, -0.126976699f, -0.125454977f, -0.123932973f, + -0.122410677f, -0.120888084f, -0.119365215f, -0.117842063f, + -0.116318628f, -0.114794925f, -0.113270953f, -0.111746714f, + -0.110222206f, -0.108697444f, -0.107172422f, -0.105647154f, + -0.104121633f, -0.102595866f, -0.101069860f, -0.099543616f, + -0.098017141f, -0.096490428f, -0.094963498f, -0.093436338f, + -0.091908954f, -0.090381362f, -0.088853553f, -0.087325536f, + -0.085797310f, -0.084268890f, -0.082740262f, -0.081211448f, + -0.079682440f, -0.078153245f, -0.076623864f, -0.075094298f, + -0.073564567f, -0.072034650f, -0.070504576f, -0.068974331f, + -0.067443922f, -0.065913349f, -0.064382628f, -0.062851757f, + -0.061320737f, -0.059789572f, -0.058258265f, -0.056726821f, + -0.055195246f, -0.053663537f, -0.052131705f, -0.050599750f, + -0.049067676f, -0.047535483f, -0.046003181f, -0.044470772f, + -0.042938258f, -0.041405641f, -0.039872926f, -0.038340122f, + -0.036807224f, -0.035274237f, -0.033741172f, -0.032208025f, + -0.030674804f, -0.029141508f, -0.027608145f, -0.026074719f, + -0.024541229f, -0.023007682f, -0.021474080f, -0.019940428f, + -0.018406730f, -0.016872987f, -0.015339206f, -0.0138053885f, + -0.012271538f, -0.010737659f, -0.009203754f, -0.007669829f, + -0.0061358847f,-0.004601926f, -0.0030679568f,-0.0015339801f, + + // sine + 0.000000000f, 0.0015339801f,0.0030679568f,0.004601926f, + 0.0061358847f,0.007669829f, 0.009203754f, 0.010737659f, + 0.012271538f, 0.0138053885f,0.015339206f, 0.016872987f, + 0.018406730f, 0.019940428f, 0.021474080f, 0.023007682f, + 0.024541229f, 0.026074719f, 0.027608145f, 0.029141508f, + 0.030674804f, 0.032208025f, 0.033741172f, 0.035274237f, + 0.036807224f, 0.038340122f, 0.039872926f, 0.041405641f, + 0.042938258f, 0.044470772f, 0.046003181f, 0.047535483f, + 0.049067676f, 0.050599750f, 0.052131705f, 0.053663537f, + 0.055195246f, 0.056726821f, 0.058258265f, 0.059789572f, + 0.061320737f, 0.062851757f, 0.064382628f, 0.065913349f, + 0.067443922f, 0.068974331f, 0.070504576f, 0.072034650f, + 0.073564567f, 0.075094298f, 0.076623864f, 0.078153245f, + 0.079682440f, 0.081211448f, 0.082740262f, 0.084268890f, + 0.085797310f, 0.087325536f, 0.088853553f, 0.090381362f, + 0.091908954f, 0.093436338f, 0.094963498f, 0.096490428f, + 0.098017141f, 0.099543616f, 0.101069860f, 0.102595866f, + 0.104121633f, 0.105647154f, 0.107172422f, 0.108697444f, + 0.110222206f, 0.111746714f, 0.113270953f, 0.114794925f, + 0.116318628f, 0.117842063f, 0.119365215f, 0.120888084f, + 0.122410677f, 0.123932973f, 0.125454977f, 0.126976699f, + 0.128498107f, 0.130019218f, 0.131540030f, 0.133060530f, + 0.134580702f, 0.136100575f, 0.137620121f, 0.139139339f, + 0.140658244f, 0.142176807f, 0.143695027f, 0.145212919f, + 0.146730468f, 0.148247674f, 0.149764538f, 0.151281044f, + 0.152797192f, 0.154312968f, 0.155828401f, 0.157343462f, + 0.158858150f, 0.160372451f, 0.161886394f, 0.163399950f, + 0.164913118f, 0.166425899f, 0.167938292f, 0.169450298f, + 0.170961887f, 0.172473088f, 0.173983872f, 0.175494254f, + 0.177004218f, 0.178513765f, 0.180022895f, 0.181531608f, + 0.183039889f, 0.184547737f, 0.186055154f, 0.187562123f, + 0.189068660f, 0.190574750f, 0.192080393f, 0.193585590f, + 0.195090324f, 0.196594596f, 0.198098406f, 0.199601755f, + 0.201104641f, 0.202607036f, 0.204108968f, 0.205610409f, + 0.207111374f, 0.208611846f, 0.210111842f, 0.211611331f, + 0.213110313f, 0.214608818f, 0.216106802f, 0.217604280f, + 0.219101235f, 0.220597684f, 0.222093627f, 0.223589033f, + 0.225083917f, 0.226578265f, 0.228072077f, 0.229565367f, + 0.231058106f, 0.232550308f, 0.234041959f, 0.235533059f, + 0.237023607f, 0.238513589f, 0.240003020f, 0.241491884f, + 0.242980182f, 0.244467899f, 0.245955050f, 0.247441620f, + 0.248927608f, 0.250413001f, 0.251897812f, 0.253382027f, + 0.254865646f, 0.256348670f, 0.257831097f, 0.259312928f, + 0.260794103f, 0.262274712f, 0.263754666f, 0.265234023f, + 0.266712755f, 0.268190861f, 0.269668311f, 0.271145165f, + 0.272621363f, 0.274096906f, 0.275571823f, 0.277046084f, + 0.278519690f, 0.279992640f, 0.281464934f, 0.282936573f, + 0.284407526f, 0.285877824f, 0.287347466f, 0.288816422f, + 0.290284663f, 0.291752249f, 0.293219149f, 0.294685364f, + 0.296150893f, 0.297615707f, 0.299079835f, 0.300543249f, + 0.302005947f, 0.303467959f, 0.304929227f, 0.306389809f, + 0.307849646f, 0.309308767f, 0.310767144f, 0.312224805f, + 0.313681751f, 0.315137923f, 0.316593379f, 0.318048090f, + 0.319502026f, 0.320955247f, 0.322407693f, 0.323859364f, + 0.325310290f, 0.326760441f, 0.328209847f, 0.329658449f, + 0.331106305f, 0.332553357f, 0.333999664f, 0.335445136f, + 0.336889863f, 0.338333756f, 0.339776874f, 0.341219217f, + 0.342660725f, 0.344101429f, 0.345541328f, 0.346980423f, + 0.348418683f, 0.349856138f, 0.351292759f, 0.352728546f, + 0.354163527f, 0.355597675f, 0.357030958f, 0.358463407f, + 0.359895051f, 0.361325800f, 0.362755716f, 0.364184797f, + 0.365612984f, 0.367040336f, 0.368466824f, 0.369892448f, + 0.371317208f, 0.372741073f, 0.374164075f, 0.375586182f, + 0.377007425f, 0.378427744f, 0.379847199f, 0.381265759f, + 0.382683426f, 0.384100199f, 0.385516047f, 0.386931002f, + 0.388345033f, 0.389758170f, 0.391170382f, 0.392581671f, + 0.393992037f, 0.395401478f, 0.396809995f, 0.398217559f, + 0.399624199f, 0.401029885f, 0.402434647f, 0.403838456f, + 0.405241311f, 0.406643212f, 0.408044159f, 0.409444153f, + 0.410843164f, 0.412241220f, 0.413638324f, 0.415034413f, + 0.416429549f, 0.417823702f, 0.419216901f, 0.420609087f, + 0.422000259f, 0.423390478f, 0.424779683f, 0.426167876f, + 0.427555084f, 0.428941280f, 0.430326492f, 0.431710660f, + 0.433093816f, 0.434475958f, 0.435857087f, 0.437237173f, + 0.438616246f, 0.439994276f, 0.441371262f, 0.442747235f, + 0.444122136f, 0.445496023f, 0.446868837f, 0.448240608f, + 0.449611336f, 0.450980991f, 0.452349573f, 0.453717113f, + 0.455083579f, 0.456448972f, 0.457813293f, 0.459176540f, + 0.460538715f, 0.461899787f, 0.463259786f, 0.464618683f, + 0.465976506f, 0.467333198f, 0.468688816f, 0.470043331f, + 0.471396744f, 0.472749025f, 0.474100202f, 0.475450277f, + 0.476799220f, 0.478147060f, 0.479493767f, 0.480839342f, + 0.482183784f, 0.483527064f, 0.484869242f, 0.486210287f, + 0.487550169f, 0.488888890f, 0.490226477f, 0.491562903f, + 0.492898196f, 0.494232297f, 0.495565265f, 0.496897042f, + 0.498227656f, 0.499557108f, 0.500885367f, 0.502212465f, + 0.503538370f, 0.504863083f, 0.506186664f, 0.507508993f, + 0.508830130f, 0.510150075f, 0.511468828f, 0.512786388f, + 0.514102757f, 0.515417874f, 0.516731799f, 0.518044531f, + 0.519356012f, 0.520666242f, 0.521975279f, 0.523283124f, + 0.524589658f, 0.525895000f, 0.527199149f, 0.528501987f, + 0.529803634f, 0.531104028f, 0.532403111f, 0.533701003f, + 0.534997642f, 0.536292970f, 0.537587047f, 0.538879931f, + 0.540171444f, 0.541461766f, 0.542750776f, 0.544038534f, + 0.545324981f, 0.546610177f, 0.547894061f, 0.549176633f, + 0.550457954f, 0.551737964f, 0.553016722f, 0.554294109f, + 0.555570245f, 0.556845009f, 0.558118522f, 0.559390724f, + 0.560661554f, 0.561931133f, 0.563199341f, 0.564466238f, + 0.565731823f, 0.566996038f, 0.568258941f, 0.569520533f, + 0.570780754f, 0.572039604f, 0.573297143f, 0.574553370f, + 0.575808167f, 0.577061653f, 0.578313768f, 0.579564571f, + 0.580813944f, 0.582062006f, 0.583308637f, 0.584553957f, + 0.585797846f, 0.587040365f, 0.588281572f, 0.589521289f, + 0.590759695f, 0.591996670f, 0.593232274f, 0.594466507f, + 0.595699310f, 0.596930683f, 0.598160684f, 0.599389315f, + 0.600616455f, 0.601842225f, 0.603066623f, 0.604289532f, + 0.605511069f, 0.606731117f, 0.607949793f, 0.609167039f, + 0.610382795f, 0.611597180f, 0.612810075f, 0.614021540f, + 0.615231574f, 0.616440177f, 0.617647290f, 0.618852973f, + 0.620057225f, 0.621259987f, 0.622461259f, 0.623661101f, + 0.624859512f, 0.626056373f, 0.627251804f, 0.628445745f, + 0.629638255f, 0.630829215f, 0.632018745f, 0.633206785f, + 0.634393275f, 0.635578334f, 0.636761844f, 0.637943923f, + 0.639124453f, 0.640303493f, 0.641481042f, 0.642657042f, + 0.643831551f, 0.645004511f, 0.646176040f, 0.647345960f, + 0.648514390f, 0.649681330f, 0.650846660f, 0.652010560f, + 0.653172851f, 0.654333591f, 0.655492842f, 0.656650543f, + 0.657806695f, 0.658961296f, 0.660114348f, 0.661265850f, + 0.662415802f, 0.663564146f, 0.664710999f, 0.665856242f, + 0.666999936f, 0.668142021f, 0.669282615f, 0.670421541f, + 0.671558976f, 0.672694743f, 0.673829019f, 0.674961627f, + 0.676092684f, 0.677222192f, 0.678350031f, 0.679476321f, + 0.680601001f, 0.681724072f, 0.682845533f, 0.683965385f, + 0.685083687f, 0.686200321f, 0.687315345f, 0.688428760f, + 0.689540565f, 0.690650702f, 0.691759229f, 0.692866147f, + 0.693971455f, 0.695075095f, 0.696177125f, 0.697277486f, + 0.698376238f, 0.699473321f, 0.700568795f, 0.701662600f, + 0.702754736f, 0.703845263f, 0.704934061f, 0.706021249f, + 0.707106769f, 0.708190620f, 0.709272802f, 0.710353374f, + 0.711432219f, 0.712509394f, 0.713584840f, 0.714658678f, + 0.715730846f, 0.716801286f, 0.717870057f, 0.718937099f, + 0.720002532f, 0.721066177f, 0.722128212f, 0.723188460f, + 0.724247098f, 0.725303948f, 0.726359129f, 0.727412641f, + 0.728464365f, 0.729514420f, 0.730562747f, 0.731609404f, + 0.732654274f, 0.733697414f, 0.734738886f, 0.735778570f, + 0.736816585f, 0.737852812f, 0.738887310f, 0.739920080f, + 0.740951121f, 0.741980433f, 0.743007958f, 0.744033754f, + 0.745057762f, 0.746080101f, 0.747100592f, 0.748119354f, + 0.749136388f, 0.750151634f, 0.751165152f, 0.752176821f, + 0.753186822f, 0.754194975f, 0.755201399f, 0.756205976f, + 0.757208824f, 0.758209884f, 0.759209216f, 0.760206699f, + 0.761202395f, 0.762196302f, 0.763188422f, 0.764178753f, + 0.765167236f, 0.766153991f, 0.767138898f, 0.768122017f, + 0.769103348f, 0.770082831f, 0.771060526f, 0.772036374f, + 0.773010433f, 0.773982704f, 0.774953127f, 0.775921702f, + 0.776888490f, 0.777853429f, 0.778816521f, 0.779777765f, + 0.780737221f, 0.781694829f, 0.782650590f, 0.783604503f, + 0.784556568f, 0.785506845f, 0.786455214f, 0.787401736f, + 0.788346410f, 0.789289236f, 0.790230215f, 0.791169345f, + 0.792106569f, 0.793041945f, 0.793975472f, 0.794907153f, + 0.795836926f, 0.796764791f, 0.797690868f, 0.798614979f, + 0.799537241f, 0.800457656f, 0.801376164f, 0.802292824f, + 0.803207517f, 0.804120362f, 0.805031359f, 0.805940390f, + 0.806847572f, 0.807752848f, 0.808656156f, 0.809557617f, + 0.810457170f, 0.811354876f, 0.812250614f, 0.813144386f, + 0.814036310f, 0.814926326f, 0.815814435f, 0.816700578f, + 0.817584813f, 0.818467140f, 0.819347501f, 0.820225954f, + 0.821102500f, 0.821977139f, 0.822849810f, 0.823720515f, + 0.824589312f, 0.825456142f, 0.826321065f, 0.827184021f, + 0.828045070f, 0.828904092f, 0.829761207f, 0.830616415f, + 0.831469595f, 0.832320869f, 0.833170176f, 0.834017515f, + 0.834862888f, 0.835706294f, 0.836547732f, 0.837387204f, + 0.838224709f, 0.839060247f, 0.839893818f, 0.840725362f, + 0.841554999f, 0.842382610f, 0.843208253f, 0.844031870f, + 0.844853580f, 0.845673263f, 0.846490920f, 0.847306609f, + 0.848120332f, 0.848932028f, 0.849741757f, 0.850549459f, + 0.851355195f, 0.852158904f, 0.852960587f, 0.853760302f, + 0.854557991f, 0.855353653f, 0.856147349f, 0.856938958f, + 0.857728601f, 0.858516216f, 0.859301805f, 0.860085368f, + 0.860866964f, 0.861646473f, 0.862423956f, 0.863199413f, + 0.863972843f, 0.864744246f, 0.865513623f, 0.866280973f, + 0.867046237f, 0.867809474f, 0.868570685f, 0.869329870f, + 0.870086968f, 0.870842040f, 0.871595085f, 0.872346044f, + 0.873094976f, 0.873841822f, 0.874586642f, 0.875329375f, + 0.876070082f, 0.876808703f, 0.877545297f, 0.878279805f, + 0.879012227f, 0.879742622f, 0.880470872f, 0.881197095f, + 0.881921291f, 0.882643342f, 0.883363366f, 0.884081244f, + 0.884797096f, 0.885510862f, 0.886222541f, 0.886932135f, + 0.887639642f, 0.888345063f, 0.889048338f, 0.889749587f, + 0.890448749f, 0.891145766f, 0.891840696f, 0.892533541f, + 0.893224299f, 0.893912971f, 0.894599497f, 0.895283937f, + 0.895966232f, 0.896646500f, 0.897324562f, 0.898000598f, + 0.898674488f, 0.899346232f, 0.900015891f, 0.900683403f, + 0.901348829f, 0.902012169f, 0.902673304f, 0.903332353f, + 0.903989315f, 0.904644072f, 0.905296743f, 0.905947268f, + 0.906595707f, 0.907242000f, 0.907886088f, 0.908528090f, + 0.909168005f, 0.909805715f, 0.910441279f, 0.911074758f, + 0.911706030f, 0.912335157f, 0.912962198f, 0.913587034f, + 0.914209783f, 0.914830327f, 0.915448725f, 0.916064978f, + 0.916679084f, 0.917290986f, 0.917900801f, 0.918508410f, + 0.919113874f, 0.919717133f, 0.920318305f, 0.920917213f, + 0.921514034f, 0.922108650f, 0.922701120f, 0.923291445f, + 0.923879504f, 0.924465477f, 0.925049245f, 0.925630808f, + 0.926210225f, 0.926787496f, 0.927362502f, 0.927935421f, + 0.928506076f, 0.929074585f, 0.929640889f, 0.930205047f, + 0.930766940f, 0.931326687f, 0.931884289f, 0.932439625f, + 0.932992816f, 0.933543801f, 0.934092522f, 0.934639156f, + 0.935183525f, 0.935725689f, 0.936265647f, 0.936803460f, + 0.937339008f, 0.937872350f, 0.938403547f, 0.938932478f, + 0.939459205f, 0.939983726f, 0.940506041f, 0.941026151f, + 0.941544056f, 0.942059755f, 0.942573190f, 0.943084419f, + 0.943593442f, 0.944100261f, 0.944604814f, 0.945107222f, + 0.945607305f, 0.946105242f, 0.946600914f, 0.947094381f, + 0.947585583f, 0.948074579f, 0.948561370f, 0.949045897f, + 0.949528158f, 0.950008273f, 0.950486064f, 0.950961649f, + 0.951435030f, 0.951906145f, 0.952374995f, 0.952841640f, + 0.953306019f, 0.953768194f, 0.954228103f, 0.954685748f, + 0.955141187f, 0.955594361f, 0.956045270f, 0.956493914f, + 0.956940353f, 0.957384527f, 0.957826436f, 0.958266079f, + 0.958703458f, 0.959138632f, 0.959571540f, 0.960002124f, + 0.960430503f, 0.960856616f, 0.961280465f, 0.961702049f, + 0.962121427f, 0.962538481f, 0.962953269f, 0.963365793f, + 0.963776052f, 0.964184046f, 0.964589775f, 0.964993238f, + 0.965394437f, 0.965793371f, 0.966189981f, 0.966584384f, + 0.966976464f, 0.967366278f, 0.967753828f, 0.968139112f, + 0.968522072f, 0.968902826f, 0.969281256f, 0.969657362f, + 0.970031261f, 0.970402837f, 0.970772147f, 0.971139133f, + 0.971503913f, 0.971866310f, 0.972226501f, 0.972584367f, + 0.972939968f, 0.973293245f, 0.973644257f, 0.973992944f, + 0.974339366f, 0.974683523f, 0.975025356f, 0.975364864f, + 0.975702107f, 0.976037085f, 0.976369739f, 0.976700068f, + 0.977028131f, 0.977353871f, 0.977677345f, 0.977998495f, + 0.978317380f, 0.978633940f, 0.978948176f, 0.979260147f, + 0.979569793f, 0.979877114f, 0.980182111f, 0.980484843f, + 0.980785251f, 0.981083393f, 0.981379211f, 0.981672704f, + 0.981963873f, 0.982252717f, 0.982539296f, 0.982823551f, + 0.983105481f, 0.983385086f, 0.983662426f, 0.983937442f, + 0.984210074f, 0.984480441f, 0.984748483f, 0.985014260f, + 0.985277653f, 0.985538721f, 0.985797524f, 0.986053944f, + 0.986308098f, 0.986559927f, 0.986809373f, 0.987056553f, + 0.987301409f, 0.987543941f, 0.987784147f, 0.988022029f, + 0.988257587f, 0.988490820f, 0.988721669f, 0.988950253f, + 0.989176512f, 0.989400446f, 0.989621997f, 0.989841282f, + 0.990058184f, 0.990272820f, 0.990485072f, 0.990695000f, + 0.990902662f, 0.991107941f, 0.991310835f, 0.991511464f, + 0.991709769f, 0.991905689f, 0.992099285f, 0.992290616f, + 0.992479563f, 0.992666125f, 0.992850423f, 0.993032336f, + 0.993211925f, 0.993389189f, 0.993564129f, 0.993736744f, + 0.993906975f, 0.994074881f, 0.994240463f, 0.994403660f, + 0.994564593f, 0.994723141f, 0.994879305f, 0.995033205f, + 0.995184720f, 0.995333910f, 0.995480776f, 0.995625257f, + 0.995767415f, 0.995907247f, 0.996044695f, 0.996179819f, + 0.996312618f, 0.996443033f, 0.996571124f, 0.996696889f, + 0.996820271f, 0.996941328f, 0.997060061f, 0.997176409f, + 0.997290432f, 0.997402132f, 0.997511446f, 0.997618437f, + 0.997723043f, 0.997825325f, 0.997925282f, 0.998022854f, + 0.998118103f, 0.998211026f, 0.998301566f, 0.998389721f, + 0.998475552f, 0.998559058f, 0.998640239f, 0.998719037f, + 0.998795450f, 0.998869538f, 0.998941302f, 0.999010682f, + 0.999077737f, 0.999142408f, 0.999204755f, 0.999264777f, + 0.999322355f, 0.999377668f, 0.999430597f, 0.999481201f, + 0.999529421f, 0.999575317f, 0.999618828f, 0.999660015f, + 0.999698818f, 0.999735296f, 0.999769390f, 0.999801159f, + 0.999830604f, 0.999857664f, 0.999882340f, 0.999904692f, + 0.999924719f, 0.999942362f, 0.999957621f, 0.999970615f, + 0.999981165f, 0.999989390f, 0.999995291f, 0.999998808f, +}; + +s16 gArctanTable[0x401] = { + 0x0000, 0x000A, 0x0014, 0x001F, 0x0029, 0x0033, 0x003D, 0x0047, + 0x0051, 0x005C, 0x0066, 0x0070, 0x007A, 0x0084, 0x008F, 0x0099, + 0x00A3, 0x00AD, 0x00B7, 0x00C2, 0x00CC, 0x00D6, 0x00E0, 0x00EA, + 0x00F4, 0x00FF, 0x0109, 0x0113, 0x011D, 0x0127, 0x0131, 0x013C, + 0x0146, 0x0150, 0x015A, 0x0164, 0x016F, 0x0179, 0x0183, 0x018D, + 0x0197, 0x01A1, 0x01AC, 0x01B6, 0x01C0, 0x01CA, 0x01D4, 0x01DE, + 0x01E9, 0x01F3, 0x01FD, 0x0207, 0x0211, 0x021B, 0x0226, 0x0230, + 0x023A, 0x0244, 0x024E, 0x0258, 0x0262, 0x026D, 0x0277, 0x0281, + 0x028B, 0x0295, 0x029F, 0x02A9, 0x02B4, 0x02BE, 0x02C8, 0x02D2, + 0x02DC, 0x02E6, 0x02F0, 0x02FB, 0x0305, 0x030F, 0x0319, 0x0323, + 0x032D, 0x0337, 0x0341, 0x034C, 0x0356, 0x0360, 0x036A, 0x0374, + 0x037E, 0x0388, 0x0392, 0x039C, 0x03A7, 0x03B1, 0x03BB, 0x03C5, + 0x03CF, 0x03D9, 0x03E3, 0x03ED, 0x03F7, 0x0401, 0x040C, 0x0416, + 0x0420, 0x042A, 0x0434, 0x043E, 0x0448, 0x0452, 0x045C, 0x0466, + 0x0470, 0x047A, 0x0484, 0x048E, 0x0499, 0x04A3, 0x04AD, 0x04B7, + 0x04C1, 0x04CB, 0x04D5, 0x04DF, 0x04E9, 0x04F3, 0x04FD, 0x0507, + 0x0511, 0x051B, 0x0525, 0x052F, 0x0539, 0x0543, 0x054D, 0x0557, + 0x0561, 0x056B, 0x0575, 0x057F, 0x0589, 0x0593, 0x059D, 0x05A7, + 0x05B1, 0x05BB, 0x05C5, 0x05CF, 0x05D9, 0x05E3, 0x05ED, 0x05F7, + 0x0601, 0x060B, 0x0615, 0x061F, 0x0629, 0x0633, 0x063D, 0x0647, + 0x0651, 0x065B, 0x0665, 0x066E, 0x0678, 0x0682, 0x068C, 0x0696, + 0x06A0, 0x06AA, 0x06B4, 0x06BE, 0x06C8, 0x06D2, 0x06DC, 0x06E5, + 0x06EF, 0x06F9, 0x0703, 0x070D, 0x0717, 0x0721, 0x072B, 0x0735, + 0x073E, 0x0748, 0x0752, 0x075C, 0x0766, 0x0770, 0x077A, 0x0783, + 0x078D, 0x0797, 0x07A1, 0x07AB, 0x07B5, 0x07BE, 0x07C8, 0x07D2, + 0x07DC, 0x07E6, 0x07EF, 0x07F9, 0x0803, 0x080D, 0x0817, 0x0820, + 0x082A, 0x0834, 0x083E, 0x0848, 0x0851, 0x085B, 0x0865, 0x086F, + 0x0878, 0x0882, 0x088C, 0x0896, 0x089F, 0x08A9, 0x08B3, 0x08BD, + 0x08C6, 0x08D0, 0x08DA, 0x08E3, 0x08ED, 0x08F7, 0x0901, 0x090A, + 0x0914, 0x091E, 0x0927, 0x0931, 0x093B, 0x0944, 0x094E, 0x0958, + 0x0961, 0x096B, 0x0975, 0x097E, 0x0988, 0x0992, 0x099B, 0x09A5, + 0x09AE, 0x09B8, 0x09C2, 0x09CB, 0x09D5, 0x09DE, 0x09E8, 0x09F2, + 0x09FB, 0x0A05, 0x0A0E, 0x0A18, 0x0A22, 0x0A2B, 0x0A35, 0x0A3E, + 0x0A48, 0x0A51, 0x0A5B, 0x0A64, 0x0A6E, 0x0A77, 0x0A81, 0x0A8B, + 0x0A94, 0x0A9E, 0x0AA7, 0x0AB1, 0x0ABA, 0x0AC4, 0x0ACD, 0x0AD7, + 0x0AE0, 0x0AE9, 0x0AF3, 0x0AFC, 0x0B06, 0x0B0F, 0x0B19, 0x0B22, + 0x0B2C, 0x0B35, 0x0B3F, 0x0B48, 0x0B51, 0x0B5B, 0x0B64, 0x0B6E, + 0x0B77, 0x0B80, 0x0B8A, 0x0B93, 0x0B9D, 0x0BA6, 0x0BAF, 0x0BB9, + 0x0BC2, 0x0BCB, 0x0BD5, 0x0BDE, 0x0BE7, 0x0BF1, 0x0BFA, 0x0C03, + 0x0C0D, 0x0C16, 0x0C1F, 0x0C29, 0x0C32, 0x0C3B, 0x0C45, 0x0C4E, + 0x0C57, 0x0C60, 0x0C6A, 0x0C73, 0x0C7C, 0x0C86, 0x0C8F, 0x0C98, + 0x0CA1, 0x0CAB, 0x0CB4, 0x0CBD, 0x0CC6, 0x0CCF, 0x0CD9, 0x0CE2, + 0x0CEB, 0x0CF4, 0x0CFD, 0x0D07, 0x0D10, 0x0D19, 0x0D22, 0x0D2B, + 0x0D34, 0x0D3E, 0x0D47, 0x0D50, 0x0D59, 0x0D62, 0x0D6B, 0x0D74, + 0x0D7D, 0x0D87, 0x0D90, 0x0D99, 0x0DA2, 0x0DAB, 0x0DB4, 0x0DBD, + 0x0DC6, 0x0DCF, 0x0DD8, 0x0DE1, 0x0DEA, 0x0DF3, 0x0DFC, 0x0E05, + 0x0E0F, 0x0E18, 0x0E21, 0x0E2A, 0x0E33, 0x0E3C, 0x0E45, 0x0E4E, + 0x0E56, 0x0E5F, 0x0E68, 0x0E71, 0x0E7A, 0x0E83, 0x0E8C, 0x0E95, + 0x0E9E, 0x0EA7, 0x0EB0, 0x0EB9, 0x0EC2, 0x0ECB, 0x0ED4, 0x0EDC, + 0x0EE5, 0x0EEE, 0x0EF7, 0x0F00, 0x0F09, 0x0F12, 0x0F1B, 0x0F23, + 0x0F2C, 0x0F35, 0x0F3E, 0x0F47, 0x0F50, 0x0F58, 0x0F61, 0x0F6A, + 0x0F73, 0x0F7C, 0x0F84, 0x0F8D, 0x0F96, 0x0F9F, 0x0FA7, 0x0FB0, + 0x0FB9, 0x0FC2, 0x0FCA, 0x0FD3, 0x0FDC, 0x0FE5, 0x0FED, 0x0FF6, + 0x0FFF, 0x1007, 0x1010, 0x1019, 0x1021, 0x102A, 0x1033, 0x103B, + 0x1044, 0x104D, 0x1055, 0x105E, 0x1067, 0x106F, 0x1078, 0x1080, + 0x1089, 0x1092, 0x109A, 0x10A3, 0x10AB, 0x10B4, 0x10BC, 0x10C5, + 0x10CE, 0x10D6, 0x10DF, 0x10E7, 0x10F0, 0x10F8, 0x1101, 0x1109, + 0x1112, 0x111A, 0x1123, 0x112B, 0x1134, 0x113C, 0x1145, 0x114D, + 0x1156, 0x115E, 0x1166, 0x116F, 0x1177, 0x1180, 0x1188, 0x1191, + 0x1199, 0x11A1, 0x11AA, 0x11B2, 0x11BB, 0x11C3, 0x11CB, 0x11D4, + 0x11DC, 0x11E4, 0x11ED, 0x11F5, 0x11FD, 0x1206, 0x120E, 0x1216, + 0x121F, 0x1227, 0x122F, 0x1237, 0x1240, 0x1248, 0x1250, 0x1259, + 0x1261, 0x1269, 0x1271, 0x127A, 0x1282, 0x128A, 0x1292, 0x129A, + 0x12A3, 0x12AB, 0x12B3, 0x12BB, 0x12C3, 0x12CC, 0x12D4, 0x12DC, + 0x12E4, 0x12EC, 0x12F4, 0x12FC, 0x1305, 0x130D, 0x1315, 0x131D, + 0x1325, 0x132D, 0x1335, 0x133D, 0x1345, 0x134D, 0x1355, 0x135E, + 0x1366, 0x136E, 0x1376, 0x137E, 0x1386, 0x138E, 0x1396, 0x139E, + 0x13A6, 0x13AE, 0x13B6, 0x13BE, 0x13C6, 0x13CE, 0x13D6, 0x13DE, + 0x13E6, 0x13ED, 0x13F5, 0x13FD, 0x1405, 0x140D, 0x1415, 0x141D, + 0x1425, 0x142D, 0x1435, 0x143D, 0x1444, 0x144C, 0x1454, 0x145C, + 0x1464, 0x146C, 0x1473, 0x147B, 0x1483, 0x148B, 0x1493, 0x149B, + 0x14A2, 0x14AA, 0x14B2, 0x14BA, 0x14C1, 0x14C9, 0x14D1, 0x14D9, + 0x14E0, 0x14E8, 0x14F0, 0x14F8, 0x14FF, 0x1507, 0x150F, 0x1516, + 0x151E, 0x1526, 0x152D, 0x1535, 0x153D, 0x1544, 0x154C, 0x1554, + 0x155B, 0x1563, 0x156B, 0x1572, 0x157A, 0x1581, 0x1589, 0x1591, + 0x1598, 0x15A0, 0x15A7, 0x15AF, 0x15B7, 0x15BE, 0x15C6, 0x15CD, + 0x15D5, 0x15DC, 0x15E4, 0x15EB, 0x15F3, 0x15FA, 0x1602, 0x1609, + 0x1611, 0x1618, 0x1620, 0x1627, 0x162F, 0x1636, 0x163E, 0x1645, + 0x164C, 0x1654, 0x165B, 0x1663, 0x166A, 0x1671, 0x1679, 0x1680, + 0x1688, 0x168F, 0x1696, 0x169E, 0x16A5, 0x16AC, 0x16B4, 0x16BB, + 0x16C2, 0x16CA, 0x16D1, 0x16D8, 0x16E0, 0x16E7, 0x16EE, 0x16F6, + 0x16FD, 0x1704, 0x170B, 0x1713, 0x171A, 0x1721, 0x1728, 0x1730, + 0x1737, 0x173E, 0x1745, 0x174C, 0x1754, 0x175B, 0x1762, 0x1769, + 0x1770, 0x1778, 0x177F, 0x1786, 0x178D, 0x1794, 0x179B, 0x17A2, + 0x17AA, 0x17B1, 0x17B8, 0x17BF, 0x17C6, 0x17CD, 0x17D4, 0x17DB, + 0x17E2, 0x17E9, 0x17F0, 0x17F7, 0x17FE, 0x1806, 0x180D, 0x1814, + 0x181B, 0x1822, 0x1829, 0x1830, 0x1837, 0x183E, 0x1845, 0x184C, + 0x1853, 0x185A, 0x1860, 0x1867, 0x186E, 0x1875, 0x187C, 0x1883, + 0x188A, 0x1891, 0x1898, 0x189F, 0x18A6, 0x18AD, 0x18B3, 0x18BA, + 0x18C1, 0x18C8, 0x18CF, 0x18D6, 0x18DD, 0x18E3, 0x18EA, 0x18F1, + 0x18F8, 0x18FF, 0x1906, 0x190C, 0x1913, 0x191A, 0x1921, 0x1928, + 0x192E, 0x1935, 0x193C, 0x1943, 0x1949, 0x1950, 0x1957, 0x195D, + 0x1964, 0x196B, 0x1972, 0x1978, 0x197F, 0x1986, 0x198C, 0x1993, + 0x199A, 0x19A0, 0x19A7, 0x19AE, 0x19B4, 0x19BB, 0x19C2, 0x19C8, + 0x19CF, 0x19D5, 0x19DC, 0x19E3, 0x19E9, 0x19F0, 0x19F6, 0x19FD, + 0x1A04, 0x1A0A, 0x1A11, 0x1A17, 0x1A1E, 0x1A24, 0x1A2B, 0x1A31, + 0x1A38, 0x1A3E, 0x1A45, 0x1A4B, 0x1A52, 0x1A58, 0x1A5F, 0x1A65, + 0x1A6C, 0x1A72, 0x1A79, 0x1A7F, 0x1A86, 0x1A8C, 0x1A93, 0x1A99, + 0x1A9F, 0x1AA6, 0x1AAC, 0x1AB3, 0x1AB9, 0x1AC0, 0x1AC6, 0x1ACC, + 0x1AD3, 0x1AD9, 0x1ADF, 0x1AE6, 0x1AEC, 0x1AF2, 0x1AF9, 0x1AFF, + 0x1B05, 0x1B0C, 0x1B12, 0x1B18, 0x1B1F, 0x1B25, 0x1B2B, 0x1B32, + 0x1B38, 0x1B3E, 0x1B44, 0x1B4B, 0x1B51, 0x1B57, 0x1B5D, 0x1B64, + 0x1B6A, 0x1B70, 0x1B76, 0x1B7D, 0x1B83, 0x1B89, 0x1B8F, 0x1B95, + 0x1B9C, 0x1BA2, 0x1BA8, 0x1BAE, 0x1BB4, 0x1BBA, 0x1BC1, 0x1BC7, + 0x1BCD, 0x1BD3, 0x1BD9, 0x1BDF, 0x1BE5, 0x1BEB, 0x1BF2, 0x1BF8, + 0x1BFE, 0x1C04, 0x1C0A, 0x1C10, 0x1C16, 0x1C1C, 0x1C22, 0x1C28, + 0x1C2E, 0x1C34, 0x1C3A, 0x1C40, 0x1C46, 0x1C4C, 0x1C52, 0x1C58, + 0x1C5E, 0x1C64, 0x1C6A, 0x1C70, 0x1C76, 0x1C7C, 0x1C82, 0x1C88, + 0x1C8E, 0x1C94, 0x1C9A, 0x1CA0, 0x1CA6, 0x1CAC, 0x1CB2, 0x1CB8, + 0x1CBE, 0x1CC3, 0x1CC9, 0x1CCF, 0x1CD5, 0x1CDB, 0x1CE1, 0x1CE7, + 0x1CED, 0x1CF3, 0x1CF8, 0x1CFE, 0x1D04, 0x1D0A, 0x1D10, 0x1D16, + 0x1D1B, 0x1D21, 0x1D27, 0x1D2D, 0x1D33, 0x1D38, 0x1D3E, 0x1D44, + 0x1D4A, 0x1D4F, 0x1D55, 0x1D5B, 0x1D61, 0x1D66, 0x1D6C, 0x1D72, + 0x1D78, 0x1D7D, 0x1D83, 0x1D89, 0x1D8E, 0x1D94, 0x1D9A, 0x1DA0, + 0x1DA5, 0x1DAB, 0x1DB1, 0x1DB6, 0x1DBC, 0x1DC2, 0x1DC7, 0x1DCD, + 0x1DD3, 0x1DD8, 0x1DDE, 0x1DE3, 0x1DE9, 0x1DEF, 0x1DF4, 0x1DFA, + 0x1DFF, 0x1E05, 0x1E0B, 0x1E10, 0x1E16, 0x1E1B, 0x1E21, 0x1E26, + 0x1E2C, 0x1E32, 0x1E37, 0x1E3D, 0x1E42, 0x1E48, 0x1E4D, 0x1E53, + 0x1E58, 0x1E5E, 0x1E63, 0x1E69, 0x1E6E, 0x1E74, 0x1E79, 0x1E7F, + 0x1E84, 0x1E8A, 0x1E8F, 0x1E94, 0x1E9A, 0x1E9F, 0x1EA5, 0x1EAA, + 0x1EB0, 0x1EB5, 0x1EBA, 0x1EC0, 0x1EC5, 0x1ECB, 0x1ED0, 0x1ED5, + 0x1EDB, 0x1EE0, 0x1EE6, 0x1EEB, 0x1EF0, 0x1EF6, 0x1EFB, 0x1F00, + 0x1F06, 0x1F0B, 0x1F10, 0x1F16, 0x1F1B, 0x1F20, 0x1F26, 0x1F2B, + 0x1F30, 0x1F36, 0x1F3B, 0x1F40, 0x1F45, 0x1F4B, 0x1F50, 0x1F55, + 0x1F5A, 0x1F60, 0x1F65, 0x1F6A, 0x1F6F, 0x1F75, 0x1F7A, 0x1F7F, + 0x1F84, 0x1F8A, 0x1F8F, 0x1F94, 0x1F99, 0x1F9E, 0x1FA4, 0x1FA9, + 0x1FAE, 0x1FB3, 0x1FB8, 0x1FBD, 0x1FC3, 0x1FC8, 0x1FCD, 0x1FD2, + 0x1FD7, 0x1FDC, 0x1FE1, 0x1FE6, 0x1FEC, 0x1FF1, 0x1FF6, 0x1FFB, + 0x2000 +}; + + +// Variables for a spline curve animation (used for the flight path in the grand star cutscene) +Vec4s *gSplineKeyframe; +float gSplineKeyframeFraction; +int gSplineState; + +// These functions have bogus return values. +// Disable the compiler warning. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreturn-local-addr" + +/// Copy vector 'src' to 'dest' +void *vec3f_copy(Vec3f dest, Vec3f src) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Set vector 'dest' to (x, y, z) +void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z) { + dest[0] = x; + dest[1] = y; + dest[2] = z; + return &dest; //! warning: function returns address of local variable +} + +/// Add vector 'a' to 'dest' +void *vec3f_add(Vec3f dest, Vec3f a) { + dest[0] += a[0]; + dest[1] += a[1]; + dest[2] += a[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Make 'dest' the sum of vectors a and b. +void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Copy vector src to dest +void *vec3s_copy(Vec3s dest, Vec3s src) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Set vector 'dest' to (x, y, z) +void *vec3s_set(Vec3s dest, s16 x, s16 y, s16 z) { + dest[0] = x; + dest[1] = y; + dest[2] = z; + return &dest; //! warning: function returns address of local variable +} + +/// Add vector a to 'dest' +void *vec3s_add(Vec3s dest, Vec3s a) { + dest[0] += a[0]; + dest[1] += a[1]; + dest[2] += a[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Make 'dest' the sum of vectors a and b. +void *vec3s_sum(Vec3s dest, Vec3s a, Vec3s b) { + dest[0] = a[0] + b[0]; + dest[1] = a[1] + b[1]; + dest[2] = a[2] + b[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Subtract vector a from 'dest' +void *vec3s_sub(Vec3s dest, Vec3s a) { + dest[0] -= a[0]; + dest[1] -= a[1]; + dest[2] -= a[2]; + return &dest; //! warning: function returns address of local variable +} + +/// Convert short vector a to float vector 'dest' +void *vec3s_to_vec3f(Vec3f dest, Vec3s a) { + dest[0] = a[0]; + dest[1] = a[1]; + dest[2] = a[2]; + return &dest; //! warning: function returns address of local variable +} + +/** + * Convert float vector a to a short vector 'dest' by rounding the components + * to the nearest integer. + */ +void *vec3f_to_vec3s(Vec3s dest, Vec3f a) { + // add/subtract 0.5 in order to round to the nearest s32 instead of truncating + dest[0] = a[0] + ((a[0] > 0) ? 0.5f : -0.5f); + dest[1] = a[1] + ((a[1] > 0) ? 0.5f : -0.5f); + dest[2] = a[2] + ((a[2] > 0) ? 0.5f : -0.5f); + return &dest; //! warning: function returns address of local variable +} + +/** + * Set 'dest' the normal vector of a triangle with vertices a, b and c. + * It is similar to vec3f_cross, but it calculates the vectors (c-b) and (b-a) + * at the same time. + */ +void *find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c) { + dest[0] = (b[1] - a[1]) * (c[2] - b[2]) - (c[1] - b[1]) * (b[2] - a[2]); + dest[1] = (b[2] - a[2]) * (c[0] - b[0]) - (c[2] - b[2]) * (b[0] - a[0]); + dest[2] = (b[0] - a[0]) * (c[1] - b[1]) - (c[0] - b[0]) * (b[1] - a[1]); + return &dest; //! warning: function returns address of local variable +} + +/// Make vector 'dest' the cross product of vectors a and b. +void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b) { + dest[0] = a[1] * b[2] - b[1] * a[2]; + dest[1] = a[2] * b[0] - b[2] * a[0]; + dest[2] = a[0] * b[1] - b[0] * a[1]; + return &dest; //! warning: function returns address of local variable +} + +/// Scale vector 'dest' so it has length 1 +void *vec3f_normalize(Vec3f dest) { + //! Possible division by zero + f32 invsqrt = 1.0f / sqrtf(dest[0] * dest[0] + dest[1] * dest[1] + dest[2] * dest[2]); + + dest[0] *= invsqrt; + dest[1] *= invsqrt; + dest[2] *= invsqrt; + return &dest; //! warning: function returns address of local variable +} + +#pragma GCC diagnostic pop + +/// Copy matrix 'src' to 'dest' +void mtxf_copy(Mat4 dest, Mat4 src) { + register s32 i; + register u32 *d = (u32 *) dest; + register u32 *s = (u32 *) src; + + for (i = 0; i < 16; i++) { + *d++ = *s++; + } +} + +/** + * Set mtx to the identity matrix + */ +void mtxf_identity(Mat4 mtx) { + register s32 i; + register f32 *dest; + // These loops must be one line to match on -O2 + + // initialize everything except the first and last cells to 0 + for (dest = (f32 *) mtx + 1, i = 0; i < 14; dest++, i++) *dest = 0; + + // initialize the diagonal cells to 1 + for (dest = (f32 *) mtx, i = 0; i < 4; dest += 5, i++) *dest = 1; +} + +/** + * Set dest to a translation matrix of vector b + */ +void mtxf_translate(Mat4 dest, Vec3f b) { + mtxf_identity(dest); + dest[3][0] = b[0]; + dest[3][1] = b[1]; + dest[3][2] = b[2]; +} + +/** + * Set mtx to a look-at matrix for the camera. The resulting transformation + * transforms the world as if there exists a camera at position 'from' pointed + * at the position 'to'. The up-vector is assumed to be (0, 1, 0), but the 'roll' + * angle allows a bank rotation of the camera. + */ +void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll) { + register f32 invLength; + f32 dx; + f32 dz; + f32 xColY; + f32 yColY; + f32 zColY; + f32 xColZ; + f32 yColZ; + f32 zColZ; + f32 xColX; + f32 yColX; + f32 zColX; + + dx = to[0] - from[0]; + dz = to[2] - from[2]; + + invLength = -1.0 / sqrtf(dx * dx + dz * dz); + dx *= invLength; + dz *= invLength; + + yColY = coss(roll); + xColY = sins(roll) * dz; + zColY = -sins(roll) * dx; + + xColZ = to[0] - from[0]; + yColZ = to[1] - from[1]; + zColZ = to[2] - from[2]; + + invLength = -1.0 / sqrtf(xColZ * xColZ + yColZ * yColZ + zColZ * zColZ); + xColZ *= invLength; + yColZ *= invLength; + zColZ *= invLength; + + xColX = yColY * zColZ - zColY * yColZ; + yColX = zColY * xColZ - xColY * zColZ; + zColX = xColY * yColZ - yColY * xColZ; + + invLength = 1.0 / sqrtf(xColX * xColX + yColX * yColX + zColX * zColX); + + xColX *= invLength; + yColX *= invLength; + zColX *= invLength; + + xColY = yColZ * zColX - zColZ * yColX; + yColY = zColZ * xColX - xColZ * zColX; + zColY = xColZ * yColX - yColZ * xColX; + + invLength = 1.0 / sqrtf(xColY * xColY + yColY * yColY + zColY * zColY); + xColY *= invLength; + yColY *= invLength; + zColY *= invLength; + + mtx[0][0] = xColX; + mtx[1][0] = yColX; + mtx[2][0] = zColX; + mtx[3][0] = -(from[0] * xColX + from[1] * yColX + from[2] * zColX); + + mtx[0][1] = xColY; + mtx[1][1] = yColY; + mtx[2][1] = zColY; + mtx[3][1] = -(from[0] * xColY + from[1] * yColY + from[2] * zColY); + + mtx[0][2] = xColZ; + mtx[1][2] = yColZ; + mtx[2][2] = zColZ; + mtx[3][2] = -(from[0] * xColZ + from[1] * yColZ + from[2] * zColZ); + + mtx[0][3] = 0; + mtx[1][3] = 0; + mtx[2][3] = 0; + mtx[3][3] = 1; +} + +/** + * Build a matrix that rotates around the z axis, then the x axis, then the y + * axis, and then translates. + */ +void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f translate, Vec3s rotate) { + register f32 sx = sins(rotate[0]); + register f32 cx = coss(rotate[0]); + + register f32 sy = sins(rotate[1]); + register f32 cy = coss(rotate[1]); + + register f32 sz = sins(rotate[2]); + register f32 cz = coss(rotate[2]); + + dest[0][0] = cy * cz + sx * sy * sz; + dest[1][0] = -cy * sz + sx * sy * cz; + dest[2][0] = cx * sy; + dest[3][0] = translate[0]; + + dest[0][1] = cx * sz; + dest[1][1] = cx * cz; + dest[2][1] = -sx; + dest[3][1] = translate[1]; + + dest[0][2] = -sy * cz + sx * cy * sz; + dest[1][2] = sy * sz + sx * cy * cz; + dest[2][2] = cx * cy; + dest[3][2] = translate[2]; + + dest[0][3] = dest[1][3] = dest[2][3] = 0.0f; + dest[3][3] = 1.0f; +} + +/** + * Build a matrix that rotates around the x axis, then the y axis, then the z + * axis, and then translates. + */ +void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f b, Vec3s c) { + register f32 sx = sins(c[0]); + register f32 cx = coss(c[0]); + + register f32 sy = sins(c[1]); + register f32 cy = coss(c[1]); + + register f32 sz = sins(c[2]); + register f32 cz = coss(c[2]); + + dest[0][0] = cy * cz; + dest[0][1] = cy * sz; + dest[0][2] = -sy; + dest[0][3] = 0; + + dest[1][0] = sx * sy * cz - cx * sz; + dest[1][1] = sx * sy * sz + cx * cz; + dest[1][2] = sx * cy; + dest[1][3] = 0; + + dest[2][0] = cx * sy * cz + sx * sz; + dest[2][1] = cx * sy * sz - sx * cz; + dest[2][2] = cx * cy; + dest[2][3] = 0; + + dest[3][0] = b[0]; + dest[3][1] = b[1]; + dest[3][2] = b[2]; + dest[3][3] = 1; +} + +/** + * Set 'dest' to a transformation matrix that turns an object to face the camera. + * 'mtx' is the look-at matrix from the camera + * 'position' is the position of the object in the world + * 'angle' rotates the object while still facing the camera. + */ +void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle) { + dest[0][0] = coss(angle); + dest[0][1] = sins(angle); + dest[0][2] = 0; + dest[0][3] = 0; + + dest[1][0] = -dest[0][1]; + dest[1][1] = dest[0][0]; + dest[1][2] = 0; + dest[1][3] = 0; + + dest[2][0] = 0; + dest[2][1] = 0; + dest[2][2] = 1; + dest[2][3] = 0; + + dest[3][0] = + mtx[0][0] * position[0] + mtx[1][0] * position[1] + mtx[2][0] * position[2] + mtx[3][0]; + dest[3][1] = + mtx[0][1] * position[0] + mtx[1][1] * position[1] + mtx[2][1] * position[2] + mtx[3][1]; + dest[3][2] = + mtx[0][2] * position[0] + mtx[1][2] * position[1] + mtx[2][2] * position[2] + mtx[3][2]; + dest[3][3] = 1; +} + +/** + * Set 'dest' to a transformation matrix that aligns an object with the terrain + * based on the normal. Used for enemies. + * 'upDir' is the terrain normal + * 'yaw' is the angle which it should face + * 'pos' is the object's position in the world + */ +void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw) { + Vec3f lateralDir; + Vec3f leftDir; + Vec3f forwardDir; + + vec3f_set(lateralDir, sins(yaw), 0, coss(yaw)); + vec3f_normalize(upDir); + + vec3f_cross(leftDir, upDir, lateralDir); + vec3f_normalize(leftDir); + + vec3f_cross(forwardDir, leftDir, upDir); + vec3f_normalize(forwardDir); + + dest[0][0] = leftDir[0]; + dest[0][1] = leftDir[1]; + dest[0][2] = leftDir[2]; + dest[3][0] = pos[0]; + + dest[1][0] = upDir[0]; + dest[1][1] = upDir[1]; + dest[1][2] = upDir[2]; + dest[3][1] = pos[1]; + + dest[2][0] = forwardDir[0]; + dest[2][1] = forwardDir[1]; + dest[2][2] = forwardDir[2]; + dest[3][2] = pos[2]; + + dest[0][3] = 0.0f; + dest[1][3] = 0.0f; + dest[2][3] = 0.0f; + dest[3][3] = 1.0f; +} + +/** + * Set 'mtx' to a transformation matrix that aligns an object with the terrain + * based on 3 height samples in an equilateral triangle around the object. + * Used for Mario when crawling or sliding. + * 'yaw' is the angle which it should face + * 'pos' is the object's position in the world + * 'radius' is the distance from each triangle vertex to the center + */ +void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius) { + struct Surface *sp74; + Vec3f point0; + Vec3f point1; + Vec3f point2; + Vec3f forward; + Vec3f xColumn; + Vec3f yColumn; + Vec3f zColumn; + f32 avgY; + f32 minY = -radius * 3; + + point0[0] = pos[0] + radius * sins(yaw + 0x2AAA); + point0[2] = pos[2] + radius * coss(yaw + 0x2AAA); + point1[0] = pos[0] + radius * sins(yaw + 0x8000); + point1[2] = pos[2] + radius * coss(yaw + 0x8000); + point2[0] = pos[0] + radius * sins(yaw + 0xD555); + point2[2] = pos[2] + radius * coss(yaw + 0xD555); + + point0[1] = find_floor(point0[0], pos[1] + 150, point0[2], &sp74); + point1[1] = find_floor(point1[0], pos[1] + 150, point1[2], &sp74); + point2[1] = find_floor(point2[0], pos[1] + 150, point2[2], &sp74); + + if (point0[1] - pos[1] < minY) { + point0[1] = pos[1]; + } + + if (point1[1] - pos[1] < minY) { + point1[1] = pos[1]; + } + + if (point2[1] - pos[1] < minY) { + point2[1] = pos[1]; + } + + avgY = (point0[1] + point1[1] + point2[1]) / 3; + + vec3f_set(forward, sins(yaw), 0, coss(yaw)); + find_vector_perpendicular_to_plane(yColumn, point0, point1, point2); + vec3f_normalize(yColumn); + vec3f_cross(xColumn, yColumn, forward); + vec3f_normalize(xColumn); + vec3f_cross(zColumn, xColumn, yColumn); + vec3f_normalize(zColumn); + + mtx[0][0] = xColumn[0]; + mtx[0][1] = xColumn[1]; + mtx[0][2] = xColumn[2]; + mtx[3][0] = pos[0]; + + mtx[1][0] = yColumn[0]; + mtx[1][1] = yColumn[1]; + mtx[1][2] = yColumn[2]; + mtx[3][1] = (avgY < pos[1]) ? pos[1] : avgY; + + mtx[2][0] = zColumn[0]; + mtx[2][1] = zColumn[1]; + mtx[2][2] = zColumn[2]; + mtx[3][2] = pos[2]; + + mtx[0][3] = 0; + mtx[1][3] = 0; + mtx[2][3] = 0; + mtx[3][3] = 1; +} + +/** + * Sets matrix 'dest' to the matrix product b * a assuming they are both + * transformation matrices with a w-component of 1. Since the bottom row + * is assumed to equal [0, 0, 0, 1], it saves some multiplications and + * addition. + * The resulting matrix represents first applying transformation b and + * then a. + */ +void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b) { + Mat4 temp; + register f32 entry0; + register f32 entry1; + register f32 entry2; + + // column 0 + entry0 = a[0][0]; + entry1 = a[0][1]; + entry2 = a[0][2]; + temp[0][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0]; + temp[0][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1]; + temp[0][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2]; + + // column 1 + entry0 = a[1][0]; + entry1 = a[1][1]; + entry2 = a[1][2]; + temp[1][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0]; + temp[1][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1]; + temp[1][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2]; + + // column 2 + entry0 = a[2][0]; + entry1 = a[2][1]; + entry2 = a[2][2]; + temp[2][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0]; + temp[2][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1]; + temp[2][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2]; + + // column 3 + entry0 = a[3][0]; + entry1 = a[3][1]; + entry2 = a[3][2]; + temp[3][0] = entry0 * b[0][0] + entry1 * b[1][0] + entry2 * b[2][0] + b[3][0]; + temp[3][1] = entry0 * b[0][1] + entry1 * b[1][1] + entry2 * b[2][1] + b[3][1]; + temp[3][2] = entry0 * b[0][2] + entry1 * b[1][2] + entry2 * b[2][2] + b[3][2]; + + temp[0][3] = temp[1][3] = temp[2][3] = 0; + temp[3][3] = 1; + + mtxf_copy(dest, temp); +} + +/** + * Set matrix 'dest' to 'mtx' scaled by vector s + */ +void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, Vec3f s) { + register s32 i; + + for (i = 0; i < 4; i++) { + dest[0][i] = mtx[0][i] * s[0]; + dest[1][i] = mtx[1][i] * s[1]; + dest[2][i] = mtx[2][i] * s[2]; + dest[3][i] = mtx[3][i]; + } +} + +/** + * Multiply a vector with a transformation matrix, which applies the transformation + * to the point. Note that the bottom row is assumed to be [0, 0, 0, 1], which is + * true for transformation matrices if the translation has a w component of 1. + */ +void mtxf_mul_vec3s(Mat4 mtx, Vec3s b) { + register f32 x = b[0]; + register f32 y = b[1]; + register f32 z = b[2]; + + b[0] = x * mtx[0][0] + y * mtx[1][0] + z * mtx[2][0] + mtx[3][0]; + b[1] = x * mtx[0][1] + y * mtx[1][1] + z * mtx[2][1] + mtx[3][1]; + b[2] = x * mtx[0][2] + y * mtx[1][2] + z * mtx[2][2] + mtx[3][2]; +} + +/** + * Convert float matrix 'src' to fixed point matrix 'dest'. + * The float matrix may not contain entries larger than 65536 or the console + * crashes. The fixed point matrix has entries with a 16-bit integer part, so + * the floating point numbers are multiplied by 2^16 before being cast to a s32 + * integer. If this doesn't fit, the N64 and iQue consoles will throw an + * exception. On Wii and Wii U Virtual Console the value will simply be clamped + * and no crashes occur. + */ +void mtxf_to_mtx(Mtx *dest, Mat4 src) { +#ifdef AVOID_UB + // Avoid type-casting which is technically UB by calling the equivalent + // guMtxF2L function. This helps little-endian systems, as well. + guMtxF2L(src, dest); +#else + s32 asFixedPoint; + register s32 i; + register s16 *a3 = (s16 *) dest; // all integer parts stored in first 16 bytes + register s16 *t0 = (s16 *) dest + 16; // all fraction parts stored in last 16 bytes + register f32 *t1 = (f32 *) src; + + for (i = 0; i < 16; i++) { + asFixedPoint = *t1++ * (1 << 16); //! float-to-integer conversion responsible for PU crashes + *a3++ = GET_HIGH_S16_OF_32(asFixedPoint); // integer part + *t0++ = GET_LOW_S16_OF_32(asFixedPoint); // fraction part + } +#endif +} + +/** + * Set 'mtx' to a transformation matrix that rotates around the z axis. + */ +void mtxf_rotate_xy(Mtx *mtx, s16 angle) { + Mat4 temp; + + mtxf_identity(temp); + temp[0][0] = coss(angle); + temp[0][1] = sins(angle); + temp[1][0] = -temp[0][1]; + temp[1][1] = temp[0][0]; + mtxf_to_mtx(mtx, temp); +} + +/** + * Extract a position given an object's transformation matrix and a camera matrix. + * This is used for determining the world position of the held object: since objMtx + * inherits the transformation from both the camera and Mario, it calculates this + * by taking the camera matrix and inverting its transformation by first rotating + * objMtx back from screen orientation to world orientation, and then subtracting + * the camera position. + */ +void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx) { + f32 camX = camMtx[3][0] * camMtx[0][0] + camMtx[3][1] * camMtx[0][1] + camMtx[3][2] * camMtx[0][2]; + f32 camY = camMtx[3][0] * camMtx[1][0] + camMtx[3][1] * camMtx[1][1] + camMtx[3][2] * camMtx[1][2]; + f32 camZ = camMtx[3][0] * camMtx[2][0] + camMtx[3][1] * camMtx[2][1] + camMtx[3][2] * camMtx[2][2]; + + dest[0] = + objMtx[3][0] * camMtx[0][0] + objMtx[3][1] * camMtx[0][1] + objMtx[3][2] * camMtx[0][2] - camX; + dest[1] = + objMtx[3][0] * camMtx[1][0] + objMtx[3][1] * camMtx[1][1] + objMtx[3][2] * camMtx[1][2] - camY; + dest[2] = + objMtx[3][0] * camMtx[2][0] + objMtx[3][1] * camMtx[2][1] + objMtx[3][2] * camMtx[2][2] - camZ; +} + +/** + * Take the vector starting at 'from' pointed at 'to' an retrieve the length + * of that vector, as well as the yaw and pitch angles. + * Basically it converts the direction to spherical coordinates. + */ +void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw) { + register f32 x = to[0] - from[0]; + register f32 y = to[1] - from[1]; + register f32 z = to[2] - from[2]; + + *dist = sqrtf(x * x + y * y + z * z); + *pitch = atan2s(sqrtf(x * x + z * z), y); + *yaw = atan2s(z, x); +} + +/** + * Construct the 'to' point which is distance 'dist' away from the 'from' position, + * and has the angles pitch and yaw. + */ +void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s16 pitch, s16 yaw) { + to[0] = from[0] + dist * coss(pitch) * sins(yaw); + to[1] = from[1] + dist * sins(pitch); + to[2] = from[2] + dist * coss(pitch) * coss(yaw); +} + +/** + * Return the value 'current' after it tries to approach target, going up at + * most 'inc' and going down at most 'dec'. + */ +s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) { + //! If target is close to the max or min s32, then it's possible to overflow + // past it without stopping. + + if (current < target) { + current += inc; + if (current > target) { + current = target; + } + } else { + current -= dec; + if (current < target) { + current = target; + } + } + return current; +} + +/** + * Return the value 'current' after it tries to approach target, going up at + * most 'inc' and going down at most 'dec'. + */ +f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec) { + if (current < target) { + current += inc; + if (current > target) { + current = target; + } + } else { + current -= dec; + if (current < target) { + current = target; + } + } + return current; +} + +/** + * Helper function for atan2s. Does a look up of the arctangent of y/x assuming + * the resulting angle is in range [0, 0x2000] (1/8 of a circle). + */ +static u16 atan2_lookup(f32 y, f32 x) { + u16 ret; + + if (x == 0) { + ret = gArctanTable[0]; + } else { + ret = gArctanTable[(s32)(y / x * 1024 + 0.5f)]; + } + return ret; +} + +/** + * Compute the angle from (0, 0) to (x, y) as a s16. Given that terrain is in + * the xz-plane, this is commonly called with (z, x) to get a yaw angle. + */ +s16 atan2s(f32 y, f32 x) { + u16 ret; + + if (x >= 0) { + if (y >= 0) { + if (y >= x) { + ret = atan2_lookup(x, y); + } else { + ret = 0x4000 - atan2_lookup(y, x); + } + } else { + y = -y; + if (y < x) { + ret = 0x4000 + atan2_lookup(y, x); + } else { + ret = 0x8000 - atan2_lookup(x, y); + } + } + } else { + x = -x; + if (y < 0) { + y = -y; + if (y >= x) { + ret = 0x8000 + atan2_lookup(x, y); + } else { + ret = 0xC000 - atan2_lookup(y, x); + } + } else { + if (y < x) { + ret = 0xC000 + atan2_lookup(y, x); + } else { + ret = -atan2_lookup(x, y); + } + } + } + return ret; +} + +/** + * Compute the atan2 in radians by calling atan2s and converting the result. + */ +f32 atan2f(f32 y, f32 x) { + return (f32) atan2s(y, x) * M_PI / 0x8000; +} + +#define CURVE_BEGIN_1 1 +#define CURVE_BEGIN_2 2 +#define CURVE_MIDDLE 3 +#define CURVE_END_1 4 +#define CURVE_END_2 5 + +/** + * Set 'result' to a 4-vector with weights corresponding to interpolation + * value t in [0, 1] and gSplineState. Given the current control point P, these + * weights are for P[0], P[1], P[2] and P[3] to obtain an interpolated point. + * The weights naturally sum to 1, and they are also always in range [0, 1] so + * the interpolated point will never overshoot. The curve is guaranteed to go + * through the first and last point, but not through intermediate points. + * + * gSplineState ensures that the curve is clamped: the first two points + * and last two points have different weight formulas. These are the weights + * just before gSplineState transitions: + * 1: [1, 0, 0, 0] + * 1->2: [0, 3/12, 7/12, 2/12] + * 2->3: [0, 1/6, 4/6, 1/6] + * 3->3: [0, 1/6, 4/6, 1/6] (repeats) + * 3->4: [0, 1/6, 4/6, 1/6] + * 4->5: [0, 2/12, 7/12, 3/12] + * 5: [0, 0, 0, 1] + * + * I suspect that the weight formulas will give a 3rd degree B-spline with the + * common uniform clamped knot vector, e.g. for n points: + * [0, 0, 0, 0, 1, 2, ... n-1, n, n, n, n] + * TODO: verify the classification of the spline / figure out how polynomials were computed + */ +void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c) { + f32 tinv = 1 - t; + f32 tinv2 = tinv * tinv; + f32 tinv3 = tinv2 * tinv; + f32 t2 = t * t; + f32 t3 = t2 * t; + + switch (gSplineState) { + case CURVE_BEGIN_1: + result[0] = tinv3; + result[1] = t3 * 1.75f - t2 * 4.5f + t * 3.0f; + result[2] = -t3 * (11 / 12.0f) + t2 * 1.5f; + result[3] = t3 * (1 / 6.0f); + break; + case CURVE_BEGIN_2: + result[0] = tinv3 * 0.25f; + result[1] = t3 * (7 / 12.0f) - t2 * 1.25f + t * 0.25f + (7 / 12.0f); + result[2] = -t3 * 0.5f + t2 * 0.5f + t * 0.5f + (1 / 6.0f); + result[3] = t3 * (1 / 6.0f); + break; + case CURVE_MIDDLE: + result[0] = tinv3 * (1 / 6.0f); + result[1] = t3 * 0.5f - t2 + (4 / 6.0f); + result[2] = -t3 * 0.5f + t2 * 0.5f + t * 0.5f + (1 / 6.0f); + result[3] = t3 * (1 / 6.0f); + break; + case CURVE_END_1: + result[0] = tinv3 * (1 / 6.0f); + result[1] = -tinv3 * 0.5f + tinv2 * 0.5f + tinv * 0.5f + (1 / 6.0f); + result[2] = tinv3 * (7 / 12.0f) - tinv2 * 1.25f + tinv * 0.25f + (7 / 12.0f); + result[3] = t3 * 0.25f; + break; + case CURVE_END_2: + result[0] = tinv3 * (1 / 6.0f); + result[1] = -tinv3 * (11 / 12.0f) + tinv2 * 1.5f; + result[2] = tinv3 * 1.75f - tinv2 * 4.5f + tinv * 3.0f; + result[3] = t3; + break; + } +} + +/** + * Initialize a spline animation. + * 'keyFrames' should be an array of (s, x, y, z) vectors + * s: the speed of the keyframe in 1000/frames, e.g. s=100 means the keyframe lasts 10 frames + * (x, y, z): point in 3D space on the curve + * The array should end with three entries with s=0 (infinite keyframe duration). + * That's because the spline has a 3rd degree polynomial, so it looks 3 points ahead. + */ +void anim_spline_init(Vec4s *keyFrames) { + gSplineKeyframe = keyFrames; + gSplineKeyframeFraction = 0; + gSplineState = 1; +} + +/** + * Poll the next point from a spline animation. + * anim_spline_init should be called before polling for vectors. + * Returns TRUE when the last point is reached, FALSE otherwise. + */ +s32 anim_spline_poll(Vec3f result) { + Vec4f weights; + s32 i; + s32 hasEnded = FALSE; + + vec3f_copy(result, gVec3fZero); + spline_get_weights(weights, gSplineKeyframeFraction, gSplineState); + for (i = 0; i < 4; i++) { + result[0] += weights[i] * gSplineKeyframe[i][1]; + result[1] += weights[i] * gSplineKeyframe[i][2]; + result[2] += weights[i] * gSplineKeyframe[i][3]; + } + + if ((gSplineKeyframeFraction += gSplineKeyframe[0][0] / 1000.0f) >= 1) { + gSplineKeyframe++; + gSplineKeyframeFraction--; + switch (gSplineState) { + case CURVE_END_2: + hasEnded = TRUE; + break; + case CURVE_MIDDLE: + if (gSplineKeyframe[2][0] == 0) { + gSplineState = CURVE_END_1; + } + break; + default: + gSplineState++; + break; + } + } + + return hasEnded; +} diff --git a/src/engine/math_util.h b/src/engine/math_util.h new file mode 100644 index 0000000..43f938e --- /dev/null +++ b/src/engine/math_util.h @@ -0,0 +1,75 @@ +#ifndef MATH_UTIL_H +#define MATH_UTIL_H + +#include "../include/PR/ultratypes.h" +#include "../include/PR/gbi.h" + +#include "../include/types.h" + +/* + * The sine and cosine tables overlap, but "#define gCosineTable (gSineTable + + * 0x400)" doesn't give expected codegen; gSineTable and gCosineTable need to + * be different symbols for code to match. Most likely the tables were placed + * adjacent to each other, and gSineTable cut short, such that reads overflow + * into gCosineTable. + * + * These kinds of out of bounds reads are undefined behavior, and break on + * e.g. GCC (which doesn't place the tables next to each other, and probably + * exploits array sizes for range analysis-based optimizations as well). + * Thus, for non-IDO compilers we use the standard-compliant version. + */ +extern f32 gSineTable[]; +#ifdef AVOID_UB +#define gCosineTable (gSineTable + 0x400) +#else +extern f32 gCosineTable[]; +#endif + +#define sins(x) gSineTable[(u16) (x) >> 4] +#define coss(x) gCosineTable[(u16) (x) >> 4] + +#define min(a, b) ((a) <= (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +#define sqr(x) ((x) * (x)) + +void *vec3f_copy(Vec3f dest, Vec3f src); +void *vec3f_set(Vec3f dest, f32 x, f32 y, f32 z); +void *vec3f_add(Vec3f dest, Vec3f a); +void *vec3f_sum(Vec3f dest, Vec3f a, Vec3f b); +void *vec3s_copy(Vec3s dest, Vec3s src); +void *vec3s_set(Vec3s dest, s16 x, s16 y, s16 z); +void *vec3s_add(Vec3s dest, Vec3s a); +void *vec3s_sum(Vec3s dest, Vec3s a, Vec3s b); +void *vec3s_sub(Vec3s dest, Vec3s a); +void *vec3s_to_vec3f(Vec3f dest, Vec3s a); +void *vec3f_to_vec3s(Vec3s dest, Vec3f a); +void *find_vector_perpendicular_to_plane(Vec3f dest, Vec3f a, Vec3f b, Vec3f c); +void *vec3f_cross(Vec3f dest, Vec3f a, Vec3f b); +void *vec3f_normalize(Vec3f dest); +void mtxf_copy(Mat4 dest, Mat4 src); +void mtxf_identity(Mat4 mtx); +void mtxf_translate(Mat4 dest, Vec3f b); +void mtxf_lookat(Mat4 mtx, Vec3f from, Vec3f to, s16 roll); +void mtxf_rotate_zxy_and_translate(Mat4 dest, Vec3f translate, Vec3s rotate); +void mtxf_rotate_xyz_and_translate(Mat4 dest, Vec3f b, Vec3s c); +void mtxf_billboard(Mat4 dest, Mat4 mtx, Vec3f position, s16 angle); +void mtxf_align_terrain_normal(Mat4 dest, Vec3f upDir, Vec3f pos, s16 yaw); +void mtxf_align_terrain_triangle(Mat4 mtx, Vec3f pos, s16 yaw, f32 radius); +void mtxf_mul(Mat4 dest, Mat4 a, Mat4 b); +void mtxf_scale_vec3f(Mat4 dest, Mat4 mtx, Vec3f s); +void mtxf_mul_vec3s(Mat4 mtx, Vec3s b); +void mtxf_to_mtx(Mtx *dest, Mat4 src); +void mtxf_rotate_xy(Mtx *mtx, s16 angle); +void get_pos_from_transform_mtx(Vec3f dest, Mat4 objMtx, Mat4 camMtx); +void vec3f_get_dist_and_angle(Vec3f from, Vec3f to, f32 *dist, s16 *pitch, s16 *yaw); +void vec3f_set_dist_and_angle(Vec3f from, Vec3f to, f32 dist, s16 pitch, s16 yaw); +s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec); +f32 approach_f32(f32 current, f32 target, f32 inc, f32 dec); +s16 atan2s(f32 y, f32 x); +f32 atan2f(f32 a, f32 b); +void spline_get_weights(Vec4f result, f32 t, UNUSED s32 c); +void anim_spline_init(Vec4s *keyFrames); +s32 anim_spline_poll(Vec3f result); + +#endif // MATH_UTIL_H diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c new file mode 100644 index 0000000..bc3ac02 --- /dev/null +++ b/src/engine/surface_collision.c @@ -0,0 +1,423 @@ +// CUSTOM/PATCH + +#include "../shim.h" +#include "surface_collision.h" +#include "../include/surface_terrains.h" + +struct SurfaceNode *s_surfaceList; + +/** + * Iterate through the list of ceilings and find the first ceiling over a given point. + */ +static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) { + register struct Surface *surf; + register s32 x1, z1, x2, z2, x3, z3; + struct Surface *ceil = NULL; + + ceil = NULL; + + // Stay in this loop until out of ceilings. + while (surfaceNode != NULL) { + surf = surfaceNode->surface; + surfaceNode = surfaceNode->next; + + + // Do the check normally done in add_surface_to_cell + if( surf->normal.y >= -0.01f ) continue; + + + x1 = surf->vertex1[0]; + z1 = surf->vertex1[2]; + z2 = surf->vertex2[2]; + x2 = surf->vertex2[0]; + + // Checking if point is in bounds of the triangle laterally. + if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) > 0) { + continue; + } + + // Slight optimization by checking these later. + x3 = surf->vertex3[0]; + z3 = surf->vertex3[2]; + if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) > 0) { + continue; + } + if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) > 0) { + continue; + } + +// // Determine if checking for the camera or not. +// if (gCheckingSurfaceCollisionsForCamera != 0) { +// if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) { +// continue; +// } +// } +// // Ignore camera only surfaces. +// else if (surf->type == SURFACE_CAMERA_BOUNDARY) { +// continue; +// } + + { + f32 nx = surf->normal.x; + f32 ny = surf->normal.y; + f32 nz = surf->normal.z; + f32 oo = surf->originOffset; + f32 height; + + // If a wall, ignore it. Likely a remnant, should never occur. + if (ny == 0.0f) { + continue; + } + + // Find the ceil height at the specific point. + height = -(x * nx + nz * z + oo) / ny; + + // Checks for ceiling interaction with a 78 unit buffer. + //! (Exposed Ceilings) Because any point above a ceiling counts + // as interacting with a ceiling, ceilings far below can cause + // "invisible walls" that are really just exposed ceilings. + if (y - (height - -78.0f) > 0.0f) { + continue; + } + + if( height < *pheight ) + { + *pheight = height; + ceil = surf; + } + } + } + + //! (Surface Cucking) Since only the first ceil is returned and not the lowest, + // lower ceilings can be "cucked" by higher ceilings. + return ceil; +} + +/** + * Iterate through the list of floors and find the first floor under a given point. + */ +static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32 x, s32 y, s32 z, f32 *pheight) { + register struct Surface *surf; + register s32 x1, z1, x2, z2, x3, z3; + f32 nx, ny, nz; + f32 oo; + f32 height; + struct Surface *floor = NULL; + + // Iterate through the list of floors until there are no more floors. + while (surfaceNode != NULL) { + surf = surfaceNode->surface; + surfaceNode = surfaceNode->next; + + + // Do the check normally done in add_surface_to_cell + if( surf->normal.y <= 0.01f ) continue; + + + x1 = surf->vertex1[0]; + z1 = surf->vertex1[2]; + x2 = surf->vertex2[0]; + z2 = surf->vertex2[2]; + + // Check that the point is within the triangle bounds. + if ((z1 - z) * (x2 - x1) - (x1 - x) * (z2 - z1) < 0) { + continue; + } + + // To slightly save on computation time, set this later. + x3 = surf->vertex3[0]; + z3 = surf->vertex3[2]; + + if ((z2 - z) * (x3 - x2) - (x2 - x) * (z3 - z2) < 0) { + continue; + } + if ((z3 - z) * (x1 - x3) - (x3 - x) * (z1 - z3) < 0) { + continue; + } + +// // Determine if we are checking for the camera or not. +// if (gCheckingSurfaceCollisionsForCamera != 0) { +// if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) { +// continue; +// } +// } +// // If we are not checking for the camera, ignore camera only floors. +// else if (surf->type == SURFACE_CAMERA_BOUNDARY) { +// continue; +// } + + nx = surf->normal.x; + ny = surf->normal.y; + nz = surf->normal.z; + oo = surf->originOffset; + + // If a wall, ignore it. Likely a remnant, should never occur. + if (ny == 0.0f) { + continue; + } + + // Find the height of the floor at a given location. + height = -(x * nx + nz * z + oo) / ny; + // Checks for floor interaction with a 78 unit buffer. + if (y - (height + -78.0f) < 0.0f) { + continue; + } + + if( height > *pheight ) + { + *pheight = height; + floor = surf; + } + } + + //! (Surface Cucking) Since only the first floor is returned and not the highest, + // higher floors can be "cucked" by lower floors. + return floor; +} + +static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode, + struct WallCollisionData *data) { + register struct Surface *surf; + register f32 offset; + register f32 radius = data->radius; + register f32 x = data->x; + register f32 y = data->y + data->offsetY; + register f32 z = data->z; + register f32 px, pz; + register f32 w1, w2, w3; + register f32 y1, y2, y3; + s32 numCols = 0; + + // Max collision radius = 200 + if (radius > 200.0f) { + radius = 200.0f; + } + + // Stay in this loop until out of walls. + while (surfaceNode != NULL) { + surf = surfaceNode->surface; + surfaceNode = surfaceNode->next; + + + // Do the check normally done in add_surface_to_cell + if( surf->normal.y < -0.01f || surf->normal.y > 0.01f ) continue; + if( surf->normal.x < -0.707f || surf->normal.x > 0.707f ) { + surf->flags |= SURFACE_FLAG_X_PROJECTION; + } + + + // Exclude a large number of walls immediately to optimize. + if (y < surf->lowerY || y > surf->upperY) { + continue; + } + + offset = surf->normal.x * x + surf->normal.y * y + surf->normal.z * z + surf->originOffset; + + if (offset < -radius || offset > radius) { + continue; + } + + px = x; + pz = z; + + //! (Quantum Tunneling) Due to issues with the vertices walls choose and + // the fact they are floating point, certain floating point positions + // along the seam of two walls may collide with neither wall or both walls. + if (surf->flags & SURFACE_FLAG_X_PROJECTION) { + w1 = -surf->vertex1[2]; w2 = -surf->vertex2[2]; w3 = -surf->vertex3[2]; + y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1]; + + if (surf->normal.x > 0.0f) { + if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) > 0.0f) { + continue; + } + if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) > 0.0f) { + continue; + } + if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) > 0.0f) { + continue; + } + } else { + if ((y1 - y) * (w2 - w1) - (w1 - -pz) * (y2 - y1) < 0.0f) { + continue; + } + if ((y2 - y) * (w3 - w2) - (w2 - -pz) * (y3 - y2) < 0.0f) { + continue; + } + if ((y3 - y) * (w1 - w3) - (w3 - -pz) * (y1 - y3) < 0.0f) { + continue; + } + } + } else { + w1 = surf->vertex1[0]; w2 = surf->vertex2[0]; w3 = surf->vertex3[0]; + y1 = surf->vertex1[1]; y2 = surf->vertex2[1]; y3 = surf->vertex3[1]; + + if (surf->normal.z > 0.0f) { + if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) > 0.0f) { + continue; + } + if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) > 0.0f) { + continue; + } + if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) > 0.0f) { + continue; + } + } else { + if ((y1 - y) * (w2 - w1) - (w1 - px) * (y2 - y1) < 0.0f) { + continue; + } + if ((y2 - y) * (w3 - w2) - (w2 - px) * (y3 - y2) < 0.0f) { + continue; + } + if ((y3 - y) * (w1 - w3) - (w3 - px) * (y1 - y3) < 0.0f) { + continue; + } + } + } + + // Determine if checking for the camera or not. +// if (gCheckingSurfaceCollisionsForCamera) { +// if (surf->flags & SURFACE_FLAG_NO_CAM_COLLISION) { +// continue; +// } +// } else { +// // Ignore camera only surfaces. +// if (surf->type == SURFACE_CAMERA_BOUNDARY) { +// continue; +// } + +// // If an object can pass through a vanish cap wall, pass through. +// if (surf->type == SURFACE_VANISH_CAP_WALLS) { +// // If an object can pass through a vanish cap wall, pass through. +// if (gCurrentObject != NULL +// && (gCurrentObject->activeFlags & ACTIVE_FLAG_MOVE_THROUGH_GRATE)) { +// continue; +// } + +// // If Mario has a vanish cap, pass through the vanish cap wall. +// if (gCurrentObject != NULL && gCurrentObject == gMarioObject +// && (gMarioState->flags & MARIO_VANISH_CAP)) { +// continue; +// } +// } +// } + + //! (Wall Overlaps) Because this doesn't update the x and z local variables, + // multiple walls can push mario more than is required. + data->x += surf->normal.x * (radius - offset); + data->z += surf->normal.z * (radius - offset); + + //! (Unreferenced Walls) Since this only returns the first four walls, + // this can lead to wall interaction being missed. Typically unreferenced walls + // come from only using one wall, however. + if (data->numWalls < 4) { + data->walls[data->numWalls++] = surf; + } + + numCols++; + } + + return numCols; +} + + + + + +s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius) +{ + struct WallCollisionData collision; + s32 numCollisions = 0; + + collision.offsetY = offsetY; + collision.radius = radius; + + collision.x = *xPtr; + collision.y = *yPtr; + collision.z = *zPtr; + + collision.numWalls = 0; + + numCollisions = find_wall_collisions(&collision); + + *xPtr = collision.x; + *yPtr = collision.y; + *zPtr = collision.z; + + return numCollisions; +} + +s32 find_wall_collisions(struct WallCollisionData *colData) +{ + s32 numCollisions = 0; + s16 x = colData->x; + s16 z = colData->z; + + colData->numWalls = 0; + + if (x <= -LEVEL_BOUNDARY_MAX || x >= LEVEL_BOUNDARY_MAX) { + return numCollisions; + } + if (z <= -LEVEL_BOUNDARY_MAX || z >= LEVEL_BOUNDARY_MAX) { + return numCollisions; + } + + numCollisions += find_wall_collisions_from_list(s_surfaceList, colData); + return numCollisions; +} + +f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil) +{ + f32 height = CELL_HEIGHT_LIMIT; + *pceil = find_ceil_from_list( s_surfaceList, posX, posY, posZ, &height ); + return height; +} + +struct FloorGeometry sFloorGeo; + +f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometry **floorGeo) +{ + struct Surface *floor; + f32 floorHeight = find_floor(xPos, yPos, zPos, &floor); + + *floorGeo = NULL; + + if (floor != NULL) { + sFloorGeo.normalX = floor->normal.x; + sFloorGeo.normalY = floor->normal.y; + sFloorGeo.normalZ = floor->normal.z; + sFloorGeo.originOffset = floor->originOffset; + + *floorGeo = &sFloorGeo; + } + return floorHeight; +} + +f32 find_floor_height(f32 x, f32 y, f32 z) +{ + f32 height = FLOOR_LOWER_LIMIT; + find_floor_from_list( s_surfaceList, x, y, z, &height ); + return height; +} + +f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor) +{ + f32 height = FLOOR_LOWER_LIMIT; + *pfloor = find_floor_from_list( s_surfaceList, xPos, yPos, zPos, &height ); + return height; +} + +f32 find_water_level(f32 x, f32 z) +{ + return -10000.0f; +} + +f32 find_poison_gas_level(f32 x, f32 z) +{ + return -10000.0f; +} + +void hack_load_surface_list(struct SurfaceNode *surfaceNode) +{ + s_surfaceList = surfaceNode; +} \ No newline at end of file diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h new file mode 100644 index 0000000..85780e6 --- /dev/null +++ b/src/engine/surface_collision.h @@ -0,0 +1,44 @@ +#ifndef SURFACE_COLLISION_H +#define SURFACE_COLLISION_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +#define LEVEL_BOUNDARY_MAX 0x2000 +#define CELL_SIZE 0x400 + +#define CELL_HEIGHT_LIMIT 20000.f +#define FLOOR_LOWER_LIMIT -11000.f + +struct WallCollisionData +{ + /*0x00*/ f32 x, y, z; + /*0x0C*/ f32 offsetY; + /*0x10*/ f32 radius; + /*0x14*/ s16 unk14; + /*0x16*/ s16 numWalls; + /*0x18*/ struct Surface *walls[4]; +}; + +struct FloorGeometry +{ + f32 unused[4]; // possibly position data? + f32 normalX; + f32 normalY; + f32 normalZ; + f32 originOffset; +}; + +s32 f32_find_wall_collision(f32 *xPtr, f32 *yPtr, f32 *zPtr, f32 offsetY, f32 radius); +s32 find_wall_collisions(struct WallCollisionData *colData); +f32 find_ceil(f32 posX, f32 posY, f32 posZ, struct Surface **pceil); +f32 find_floor_height_and_data(f32 xPos, f32 yPos, f32 zPos, struct FloorGeometry **floorGeo); +f32 find_floor_height(f32 x, f32 y, f32 z); +f32 find_floor(f32 xPos, f32 yPos, f32 zPos, struct Surface **pfloor); +f32 find_water_level(f32 x, f32 z); +f32 find_poison_gas_level(f32 x, f32 z); + +void hack_load_surface_list(struct SurfaceNode *surfaceNode); + +#endif // SURFACE_COLLISION_H diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c new file mode 100644 index 0000000..1918dad --- /dev/null +++ b/src/engine/surface_load.c @@ -0,0 +1,378 @@ +#include +#include +#include "../include/surface_terrains.h" +#include "../include/surface_terrains.h" +#include "../shim.h" +#include "surface_load.h" +#include "surface_collision.h" + +struct Surface **s_surface_list = NULL; +size_t s_surface_list_count = 0; +struct SurfaceNode *s_surface_node_list = NULL; + +static void add_surface_ex(struct Surface *surface, s32 dynamic) +{ + s_surface_list_count++; + + if( s_surface_list == NULL ) + s_surface_list = malloc( s_surface_list_count * sizeof( struct Surface * )); + else + s_surface_list = realloc( s_surface_list, s_surface_list_count * sizeof( struct Surface * )); + + s_surface_list[s_surface_list_count - 1] = surface; +} + +static struct Surface *alloc_surface(void) { + struct Surface *surface = malloc(sizeof(struct Surface)); // PATCH - just use malloc instead of pool +// struct Surface *surface = &sSurfacePool[gSurfacesAllocated]; +// gSurfacesAllocated++; + + //! A bounds check! If there's more surfaces than the 2300 allowed, + // we, um... + // Perhaps originally just debug feedback? +// if (gSurfacesAllocated >= sSurfacePoolSize) { +// } + + surface->type = 0; + surface->force = 0; + surface->flags = 0; + surface->room = 0; + surface->object = NULL; + + return surface; +} + +/** + * Returns whether a surface has exertion/moves Mario + * based on the surface type. + */ +static s32 surface_has_force(s16 surfaceType) { + s32 hasForce = FALSE; + + switch (surfaceType) { + case SURFACE_0004: // Unused + case SURFACE_FLOWING_WATER: + case SURFACE_DEEP_MOVING_QUICKSAND: + case SURFACE_SHALLOW_MOVING_QUICKSAND: + case SURFACE_MOVING_QUICKSAND: + case SURFACE_HORIZONTAL_WIND: + case SURFACE_INSTANT_MOVING_QUICKSAND: + hasForce = TRUE; + break; + + default: + break; + } + return hasForce; +} + +/** + * Returns whether a surface should have the + * SURFACE_FLAG_NO_CAM_COLLISION flag. + */ +static s32 surf_has_no_cam_collision(s16 surfaceType) { + s32 flags = 0; + + switch (surfaceType) { + case SURFACE_NO_CAM_COLLISION: + case SURFACE_NO_CAM_COLLISION_77: // Unused + case SURFACE_NO_CAM_COL_VERY_SLIPPERY: + case SURFACE_SWITCH: + flags = SURFACE_FLAG_NO_CAM_COLLISION; + break; + + default: + break; + } + + return flags; +} + + +static struct Surface *read_surface_data_ex( s32 x1, s32 y1, s32 z1, s32 x2, s32 y2, s32 z2, s32 x3, s32 y3, s32 z3) { + struct Surface *surface; + s32 maxY, minY; + f32 nx, ny, nz; + f32 mag; + + // (v2 - v1) x (v3 - v2) + nx = (y2 - y1) * (z3 - z2) - (z2 - z1) * (y3 - y2); + ny = (z2 - z1) * (x3 - x2) - (x2 - x1) * (z3 - z2); + nz = (x2 - x1) * (y3 - y2) - (y2 - y1) * (x3 - x2); + mag = sqrtf(nx * nx + ny * ny + nz * nz); + + // Could have used min_3 and max_3 for this... + minY = y1; + if (y2 < minY) { + minY = y2; + } + if (y3 < minY) { + minY = y3; + } + + maxY = y1; + if (y2 > maxY) { + maxY = y2; + } + if (y3 > maxY) { + maxY = y3; + } + + // Checking to make sure no DIV/0 + if (mag < 0.0001) { + return NULL; + } + mag = (f32)(1.0 / mag); + nx *= mag; + ny *= mag; + nz *= mag; + + surface = alloc_surface(); + + surface->vertex1[0] = x1; + surface->vertex2[0] = x2; + surface->vertex3[0] = x3; + + surface->vertex1[1] = y1; + surface->vertex2[1] = y2; + surface->vertex3[1] = y3; + + surface->vertex1[2] = z1; + surface->vertex2[2] = z2; + surface->vertex3[2] = z3; + + surface->normal.x = nx; + surface->normal.y = ny; + surface->normal.z = nz; + + surface->originOffset = -(nx * x1 + ny * y1 + nz * z1); + + surface->lowerY = minY - 5; + surface->upperY = maxY + 5; + + return surface; +} + +/** + * Initializes a Surface struct using the given vertex data + * @param vertexData The raw data containing vertex positions + * @param vertexIndices Helper which tells positions in vertexData to start reading vertices + */ +static struct Surface *read_surface_data(s16 *vertexData, s16 **vertexIndices) { + register s32 x1, y1, z1; + register s32 x2, y2, z2; + register s32 x3, y3, z3; + s16 offset1, offset2, offset3; + + offset1 = 3 * (*vertexIndices)[0]; + offset2 = 3 * (*vertexIndices)[1]; + offset3 = 3 * (*vertexIndices)[2]; + + x1 = *(vertexData + offset1 + 0); + y1 = *(vertexData + offset1 + 1); + z1 = *(vertexData + offset1 + 2); + + x2 = *(vertexData + offset2 + 0); + y2 = *(vertexData + offset2 + 1); + z2 = *(vertexData + offset2 + 2); + + x3 = *(vertexData + offset3 + 0); + y3 = *(vertexData + offset3 + 1); + z3 = *(vertexData + offset3 + 2); + + return read_surface_data_ex( x1, y1, z1, x2, y2, z2, x3, y3, z3 ); +} + +/** + * Read the data for vertices for reference by triangles. + */ +static s16 *read_vertex_data(s16 **data) { + s32 numVertices; + UNUSED s16 unused1[3]; + UNUSED s16 unused2[3]; + s16 *vertexData; + + numVertices = *(*data); + (*data)++; + + vertexData = *data; + *data += 3 * numVertices; + + return vertexData; +} + +/** + * Load in the surfaces for a given surface type. This includes setting the flags, + * exertion, and room. + */ +static void load_static_surfaces(s16 **data, s16 *vertexData, s16 surfaceType, s8 **surfaceRooms) { + s32 i; + s32 numSurfaces; + struct Surface *surface; + s8 room = 0; + s16 hasForce = surface_has_force(surfaceType); + s16 flags = surf_has_no_cam_collision(surfaceType); + + numSurfaces = *(*data); + *data += 1; + + for (i = 0; i < numSurfaces; i++) { + if (*surfaceRooms != NULL) { + room = *(*surfaceRooms); + *surfaceRooms += 1; + } + + surface = read_surface_data(vertexData, data); + if (surface != NULL) { + surface->room = room; + surface->type = surfaceType; + surface->flags = (s8) flags; + + if (hasForce) { + surface->force = *(*data + 3); + } else { + surface->force = 0; + } + + add_surface_ex(surface, FALSE); + } + + *data += 3; + if (hasForce) { + *data += 1; + } + } +} + +static void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects) { + s16 terrainLoadType; + s16 *vertexData; + UNUSED s32 unused; + + // Initialize the data for this. +// gEnvironmentRegions = NULL; +// unused8038BE90 = 0; +// gSurfaceNodesAllocated = 0; +// gSurfacesAllocated = 0; + +// clear_static_surfaces(); + + // A while loop iterating through each section of the level data. Sections of data + // are prefixed by a terrain "type." This type is reused for surfaces as the surface + // type. + while (TRUE) { + terrainLoadType = *data; + data++; + + if (TERRAIN_LOAD_IS_SURFACE_TYPE_LOW(terrainLoadType)) { + load_static_surfaces(&data, vertexData, terrainLoadType, &surfaceRooms); + } else if (terrainLoadType == TERRAIN_LOAD_VERTICES) { + vertexData = read_vertex_data(&data); +// } else if (terrainLoadType == TERRAIN_LOAD_OBJECTS) { +// spawn_special_objects(index, &data); +// } else if (terrainLoadType == TERRAIN_LOAD_ENVIRONMENT) { +// load_environmental_regions(&data); + } else if (terrainLoadType == TERRAIN_LOAD_CONTINUE) { +// continue; + break; // PATCH + } else if (terrainLoadType == TERRAIN_LOAD_END) { + break; + } else if (TERRAIN_LOAD_IS_SURFACE_TYPE_HIGH(terrainLoadType)) { + load_static_surfaces(&data, vertexData, terrainLoadType, &surfaceRooms); + continue; + } + } + +// if (macroObjects != NULL && *macroObjects != -1) { +// // If the first macro object presetID is within the range [0, 29]. +// // Generally an early spawning method, every object is in BBH (the first level). +// if (0 <= *macroObjects && *macroObjects < 30) { +// spawn_macro_objects_hardcoded(index, macroObjects); +// } +// // A more general version that can spawn more objects. +// else { +// spawn_macro_objects(index, macroObjects); +// } +// } + +// gNumStaticSurfaceNodes = gSurfaceNodesAllocated; +// gNumStaticSurfaces = gSurfacesAllocated; +} + + + + +// Unused, loads from raw sm64 collision data +// struct Surface **surface_load_from_collision_data( const s16 *data, size_t *numSurfaces ) +// { +// s8 *rooms = NULL; +// s16 *macroObjects = NULL; +// load_area_terrain( 0, (s16*)data, rooms, macroObjects ); +// *numSurfaces = s_surface_list_count; +// return s_surface_list; +// } + + +void surface_load_for_libsm64( const struct SM64Surface *surfaceArray, size_t numSurfaces ) +{ + while( s_surface_node_list ) + { + struct SurfaceNode *node = s_surface_node_list; + s_surface_node_list = node->next; + free( node ); + } + + if( s_surface_list ) + { + for( int i = 0; i < s_surface_list_count; ++i ) + free( s_surface_list[i] ); + + free( s_surface_list ); + + s_surface_list = NULL; + s_surface_list_count = 0; + } + + for( int i = 0; i < numSurfaces; ++i ) + { + struct Surface *surface = read_surface_data_ex( + surfaceArray[i].vertices[0][0], surfaceArray[i].vertices[0][1], surfaceArray[i].vertices[0][2], + surfaceArray[i].vertices[1][0], surfaceArray[i].vertices[1][1], surfaceArray[i].vertices[1][2], + surfaceArray[i].vertices[2][0], surfaceArray[i].vertices[2][1], surfaceArray[i].vertices[2][2] + ); + + if (surface != NULL) { + s16 hasForce = surface_has_force(surfaceArray[i].type); + s16 flags = surf_has_no_cam_collision(surfaceArray[i].type); + + surface->room = 0; + surface->type = surfaceArray[i].type; + surface->flags = (s8) flags; + + if (hasForce) { + surface->force = surfaceArray[i].force; + } else { + surface->force = 0; + } + + add_surface_ex(surface, FALSE); + } + } + + struct Surface **ptr = s_surface_list; + struct SurfaceNode *node = malloc( sizeof( struct SurfaceNode )); + node->surface = *ptr; + s_surface_node_list = node; + + for( int i = 1; i < s_surface_list_count; ++i ) + { + ptr++; + struct SurfaceNode *next = malloc( sizeof( struct SurfaceNode )); + next->surface = *ptr; + next->next = NULL; + node->next = next; + node = next; + } + + hack_load_surface_list( s_surface_node_list ); +} \ No newline at end of file diff --git a/src/engine/surface_load.h b/src/engine/surface_load.h new file mode 100644 index 0000000..9d0ed7d --- /dev/null +++ b/src/engine/surface_load.h @@ -0,0 +1,8 @@ +#pragma once + +#include "../libsm64.h" +#include "../include/types.h" + +struct Surface **surface_load_from_collision_data( const s16 *data, size_t *numSurfaces ); + +void surface_load_for_libsm64( const struct SM64Surface *surfaceArray, size_t numSurfaces ); \ No newline at end of file diff --git a/src/game/area.h b/src/game/area.h new file mode 100644 index 0000000..983b8b6 --- /dev/null +++ b/src/game/area.h @@ -0,0 +1,157 @@ +#ifndef AREA_H +#define AREA_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" +#include "camera.h" +//#include "engine/graph_node.h" + +struct WarpNode +{ + /*00*/ u8 id; + /*01*/ u8 destLevel; + /*02*/ u8 destArea; + /*03*/ u8 destNode; +}; + +struct ObjectWarpNode +{ + /*0x00*/ struct WarpNode node; + /*0x04*/ struct Object *object; + /*0x08*/ struct ObjectWarpNode *next; +}; + +// From Surface 0x1B to 0x1E +#define INSTANT_WARP_INDEX_START 0x00 // Equal and greater than Surface 0x1B +#define INSTANT_WARP_INDEX_STOP 0x04 // Less than Surface 0x1F + +struct InstantWarp +{ + /*0x00*/ u8 id; // 0 = 0x1B / 1 = 0x1C / 2 = 0x1D / 3 = 0x1E + /*0x01*/ u8 area; + /*0x02*/ Vec3s displacement; +}; + +struct SpawnInfo +{ + /*0x00*/ Vec3s startPos; + /*0x06*/ Vec3s startAngle; + /*0x0C*/ s8 areaIndex; + /*0x0D*/ s8 activeAreaIndex; + /*0x10*/ u32 behaviorArg; + /*0x14*/ void *behaviorScript; + /*0x18*/ struct GraphNode *unk18; + /*0x1C*/ struct SpawnInfo *next; +}; + +struct UnusedArea28 +{ + /*0x00*/ s16 unk00; + /*0x02*/ s16 unk02; + /*0x04*/ s16 unk04; + /*0x06*/ s16 unk06; + /*0x08*/ s16 unk08; +}; + +struct Whirlpool +{ + /*0x00*/ Vec3s pos; + /*0x03*/ s16 strength; +}; + +struct Area +{ + /*0x00*/ s8 index; + /*0x01*/ s8 flags; // Only has 1 flag: 0x01 = Is this the active area? + /*0x02*/ u16 terrainType; // default terrain of the level (set from level script cmd 0x31) + /*0x04*/ struct GraphNodeRoot *unk04; // geometry layout data + /*0x08*/ s16 *terrainData; // collision data (set from level script cmd 0x2E) + /*0x0C*/ s8 *surfaceRooms; // (set from level script cmd 0x2F) + /*0x10*/ s16 *macroObjects; // Macro Objects Ptr (set from level script cmd 0x39) + /*0x14*/ struct ObjectWarpNode *warpNodes; + /*0x18*/ struct WarpNode *paintingWarpNodes; + /*0x1C*/ struct InstantWarp *instantWarps; + /*0x20*/ struct SpawnInfo *objectSpawnInfos; + /*0x24*/ struct Camera *camera; + /*0x28*/ struct UnusedArea28 *unused28; // Filled by level script 0x3A, but is unused. + /*0x2C*/ struct Whirlpool *whirlpools[2]; + /*0x34*/ u8 dialog[2]; // Level start dialog number (set by level script cmd 0x30) + /*0x36*/ u16 musicParam; + /*0x38*/ u16 musicParam2; +}; + +// All the transition data to be used in screen_transition.c +struct WarpTransitionData +{ + /*0x00*/ u8 red; + /*0x01*/ u8 green; + /*0x02*/ u8 blue; + + /*0x04*/ s16 startTexRadius; + /*0x06*/ s16 endTexRadius; + /*0x08*/ s16 startTexX; + /*0x0A*/ s16 startTexY; + /*0x0C*/ s16 endTexX; + /*0x0E*/ s16 endTexY; + + /*0x10*/ s16 texTimer; // always 0, does seems to affect transition when disabled +}; + +#define WARP_TRANSITION_FADE_FROM_COLOR 0x00 +#define WARP_TRANSITION_FADE_INTO_COLOR 0x01 +#define WARP_TRANSITION_FADE_FROM_STAR 0x08 +#define WARP_TRANSITION_FADE_INTO_STAR 0x09 +#define WARP_TRANSITION_FADE_FROM_CIRCLE 0x0A +#define WARP_TRANSITION_FADE_INTO_CIRCLE 0x0B +#define WARP_TRANSITION_FADE_FROM_MARIO 0x10 +#define WARP_TRANSITION_FADE_INTO_MARIO 0x11 +#define WARP_TRANSITION_FADE_FROM_BOWSER 0x12 +#define WARP_TRANSITION_FADE_INTO_BOWSER 0x13 + +struct WarpTransition +{ + /*0x00*/ u8 isActive; // Is the transition active. (either TRUE or FALSE) + /*0x01*/ u8 type; // Determines the type of transition to use (circle, star, etc.) + /*0x02*/ u8 time; // Amount of time to complete the transition (in frames) + /*0x03*/ u8 pauseRendering; // Should the game stop rendering. (either TRUE or FALSE) + /*0x04*/ struct WarpTransitionData data; +}; + +// extern struct GraphNode **gLoadedGraphNodes; +// extern struct SpawnInfo gPlayerSpawnInfos[]; +// extern struct GraphNode *D_8033A160[]; +// extern struct Area gAreaData[]; +// extern struct WarpTransition gWarpTransition; +// extern s16 gCurrCourseNum; +// extern s16 gCurrActNum; +// extern s16 gCurrAreaIndex; +// extern s16 gSavedCourseNum; +// extern s16 gPauseScreenMode; +// extern s16 gSaveOptSelectIndex; +// +// extern struct SpawnInfo *gMarioSpawnInfo; +// +// extern struct Area *gAreas; +// extern struct Area *gCurrentArea; +// +// extern s16 gCurrSaveFileNum; +// extern s16 gCurrLevelNum; + +// void override_viewport_and_clip(Vp *a, Vp *b, u8 c, u8 d, u8 e); +// void print_intro_text(void); +// u32 get_mario_spawn_type(struct Object *o); +// struct ObjectWarpNode *area_get_warp_node(u8 id); +// void clear_areas(void); +// void clear_area_graph_nodes(void); +// void load_area(s32 index); +// void unload_area(void); +// void load_mario_area(void); +// void unload_mario_area(void); +// void change_area(s32 index); +// void area_update_objects(void); +// void play_transition(s16 transType, s16 time, u8 red, u8 green, u8 blue); +// void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 blue, s16 delay); +// void render_game(void); + +#endif // AREA_H diff --git a/src/game/camera.h b/src/game/camera.h new file mode 100644 index 0000000..3628f56 --- /dev/null +++ b/src/game/camera.h @@ -0,0 +1,776 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" +#include "area.h" +//#include "engine/geo_layout.h" +//#include "engine/graph_node.h" + +//#include "level_table.h" + +/** + * @file camera.h + * Constants, defines, and structs used by the camera system. + * @see camera.c + */ + +#define ABS(x) ((x) > 0.f ? (x) : -(x)) +#define ABS2(x) ((x) >= 0.f ? (x) : -(x)) + +/** + * Converts an angle in degrees to sm64's s16 angle units. For example, DEGREES(90) == 0x4000 + * This should be used mainly to make camera code clearer at first glance. + */ +#define DEGREES(x) ((x) * 0x10000 / 360) + +#define LEVEL_AREA_INDEX(levelNum, areaNum) (((levelNum) << 4) + (areaNum)) + +/** + * Helper macro for defining which areas of a level should zoom out the camera when the game is paused. + * Because a mask is used by two levels, the pattern will repeat when more than 4 areas are used by a level. + */ +#define ZOOMOUT_AREA_MASK(level1Area1, level1Area2, level1Area3, level1Area4, \ + level2Area1, level2Area2, level2Area3, level2Area4) \ + ((level2Area4) << 7 | \ + (level2Area3) << 6 | \ + (level2Area2) << 5 | \ + (level2Area1) << 4 | \ + (level1Area4) << 3 | \ + (level1Area3) << 2 | \ + (level1Area2) << 1 | \ + (level1Area1) << 0) + + +#define AREA_BBH LEVEL_AREA_INDEX(LEVEL_BBH, 1) +#define AREA_CCM_OUTSIDE LEVEL_AREA_INDEX(LEVEL_CCM, 1) +#define AREA_CCM_SLIDE LEVEL_AREA_INDEX(LEVEL_CCM, 2) +#define AREA_CASTLE_LOBBY LEVEL_AREA_INDEX(LEVEL_CASTLE, 1) +#define AREA_CASTLE_TIPPY LEVEL_AREA_INDEX(LEVEL_CASTLE, 2) +#define AREA_CASTLE_BASEMENT LEVEL_AREA_INDEX(LEVEL_CASTLE, 3) +#define AREA_HMC LEVEL_AREA_INDEX(LEVEL_HMC, 1) +#define AREA_SSL_OUTSIDE LEVEL_AREA_INDEX(LEVEL_SSL, 1) +#define AREA_SSL_PYRAMID LEVEL_AREA_INDEX(LEVEL_SSL, 2) +#define AREA_SSL_EYEROK LEVEL_AREA_INDEX(LEVEL_SSL, 3) +#define AREA_BOB LEVEL_AREA_INDEX(LEVEL_BOB, 1) +#define AREA_SL_OUTSIDE LEVEL_AREA_INDEX(LEVEL_SL, 1) +#define AREA_SL_IGLOO LEVEL_AREA_INDEX(LEVEL_SL, 2) +#define AREA_WDW_MAIN LEVEL_AREA_INDEX(LEVEL_WDW, 1) +#define AREA_WDW_TOWN LEVEL_AREA_INDEX(LEVEL_WDW, 2) +#define AREA_JRB_MAIN LEVEL_AREA_INDEX(LEVEL_JRB, 1) +#define AREA_JRB_SHIP LEVEL_AREA_INDEX(LEVEL_JRB, 2) +#define AREA_THI_HUGE LEVEL_AREA_INDEX(LEVEL_THI, 1) +#define AREA_THI_TINY LEVEL_AREA_INDEX(LEVEL_THI, 2) +#define AREA_THI_WIGGLER LEVEL_AREA_INDEX(LEVEL_THI, 3) +#define AREA_TTC LEVEL_AREA_INDEX(LEVEL_TTC, 1) +#define AREA_RR LEVEL_AREA_INDEX(LEVEL_RR, 1) +#define AREA_CASTLE_GROUNDS LEVEL_AREA_INDEX(LEVEL_CASTLE_GROUNDS, 1) +#define AREA_BITDW LEVEL_AREA_INDEX(LEVEL_BITDW, 1) +#define AREA_VCUTM LEVEL_AREA_INDEX(LEVEL_VCUTM, 1) +#define AREA_BITFS LEVEL_AREA_INDEX(LEVEL_BITFS, 1) +#define AREA_SA LEVEL_AREA_INDEX(LEVEL_SA, 1) +#define AREA_BITS LEVEL_AREA_INDEX(LEVEL_BITS, 1) +#define AREA_LLL_OUTSIDE LEVEL_AREA_INDEX(LEVEL_LLL, 1) +#define AREA_LLL_VOLCANO LEVEL_AREA_INDEX(LEVEL_LLL, 2) +#define AREA_DDD_WHIRLPOOL LEVEL_AREA_INDEX(LEVEL_DDD, 1) +#define AREA_DDD_SUB LEVEL_AREA_INDEX(LEVEL_DDD, 2) +#define AREA_WF LEVEL_AREA_INDEX(LEVEL_WF, 1) +#define AREA_ENDING LEVEL_AREA_INDEX(LEVEL_ENDING, 1) +#define AREA_COURTYARD LEVEL_AREA_INDEX(LEVEL_CASTLE_COURTYARD, 1) +#define AREA_PSS LEVEL_AREA_INDEX(LEVEL_PSS, 1) +#define AREA_COTMC LEVEL_AREA_INDEX(LEVEL_COTMC, 1) +#define AREA_TOTWC LEVEL_AREA_INDEX(LEVEL_TOTWC, 1) +#define AREA_BOWSER_1 LEVEL_AREA_INDEX(LEVEL_BOWSER_1, 1) +#define AREA_WMOTR LEVEL_AREA_INDEX(LEVEL_WMOTR, 1) +#define AREA_BOWSER_2 LEVEL_AREA_INDEX(LEVEL_BOWSER_2, 1) +#define AREA_BOWSER_3 LEVEL_AREA_INDEX(LEVEL_BOWSER_3, 1) +#define AREA_TTM_OUTSIDE LEVEL_AREA_INDEX(LEVEL_TTM, 1) + +#define CAM_MODE_MARIO_ACTIVE 0x01 +#define CAM_MODE_LAKITU_WAS_ZOOMED_OUT 0x02 +#define CAM_MODE_MARIO_SELECTED 0x04 + +#define CAM_SELECTION_MARIO 1 +#define CAM_SELECTION_FIXED 2 + +#define CAM_ANGLE_MARIO 1 +#define CAM_ANGLE_LAKITU 2 + +#define CAMERA_MODE_NONE 0x00 +#define CAMERA_MODE_RADIAL 0x01 +#define CAMERA_MODE_OUTWARD_RADIAL 0x02 +#define CAMERA_MODE_BEHIND_MARIO 0x03 +#define CAMERA_MODE_CLOSE 0x04 // Inside Castle / Big Boo's Haunt +#define CAMERA_MODE_C_UP 0x06 +#define CAMERA_MODE_WATER_SURFACE 0x08 +#define CAMERA_MODE_SLIDE_HOOT 0x09 +#define CAMERA_MODE_INSIDE_CANNON 0x0A +#define CAMERA_MODE_BOSS_FIGHT 0x0B +#define CAMERA_MODE_PARALLEL_TRACKING 0x0C +#define CAMERA_MODE_FIXED 0x0D +#define CAMERA_MODE_8_DIRECTIONS 0x0E // AKA Parallel Camera, Bowser Courses & Rainbow Ride +#define CAMERA_MODE_FREE_ROAM 0x10 +#define CAMERA_MODE_SPIRAL_STAIRS 0x11 + +#define CAM_MOVE_RETURN_TO_MIDDLE 0x0001 +#define CAM_MOVE_ZOOMED_OUT 0x0002 +#define CAM_MOVE_ROTATE_RIGHT 0x0004 +#define CAM_MOVE_ROTATE_LEFT 0x0008 +#define CAM_MOVE_ENTERED_ROTATE_SURFACE 0x0010 +#define CAM_MOVE_METAL_BELOW_WATER 0x0020 +#define CAM_MOVE_FIX_IN_PLACE 0x0040 +#define CAM_MOVE_UNKNOWN_8 0x0080 +#define CAM_MOVING_INTO_MODE 0x0100 +#define CAM_MOVE_STARTED_EXITING_C_UP 0x0200 +#define CAM_MOVE_UNKNOWN_11 0x0400 +#define CAM_MOVE_INIT_CAMERA 0x0800 +#define CAM_MOVE_ALREADY_ZOOMED_OUT 0x1000 +#define CAM_MOVE_C_UP_MODE 0x2000 +#define CAM_MOVE_SUBMERGED 0x4000 +#define CAM_MOVE_PAUSE_SCREEN 0x8000 + +#define CAM_MOVE_ROTATE /**/ (CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ROTATE_LEFT | CAM_MOVE_RETURN_TO_MIDDLE) +/// These flags force the camera to move a certain way +#define CAM_MOVE_RESTRICT /**/ (CAM_MOVE_ENTERED_ROTATE_SURFACE | CAM_MOVE_METAL_BELOW_WATER | CAM_MOVE_FIX_IN_PLACE | CAM_MOVE_UNKNOWN_8) + +#define CAM_SOUND_C_UP_PLAYED 0x01 +#define CAM_SOUND_MARIO_ACTIVE 0x02 +#define CAM_SOUND_NORMAL_ACTIVE 0x04 +#define CAM_SOUND_UNUSED_SELECT_MARIO 0x08 +#define CAM_SOUND_UNUSED_SELECT_FIXED 0x10 +#define CAM_SOUND_FIXED_ACTIVE 0x20 + +#define CAM_FLAG_SMOOTH_MOVEMENT 0x0001 +#define CAM_FLAG_BLOCK_SMOOTH_MOVEMENT 0x0002 +#define CAM_FLAG_FRAME_AFTER_CAM_INIT 0x0004 +#define CAM_FLAG_CHANGED_PARTRACK_INDEX 0x0008 +#define CAM_FLAG_CCM_SLIDE_SHORTCUT 0x0010 +#define CAM_FLAG_CAM_NEAR_WALL 0x0020 +#define CAM_FLAG_SLEEPING 0x0040 +#define CAM_FLAG_UNUSED_7 0x0080 +#define CAM_FLAG_UNUSED_8 0x0100 +#define CAM_FLAG_COLLIDED_WITH_WALL 0x0200 +#define CAM_FLAG_START_TRANSITION 0x0400 +#define CAM_FLAG_TRANSITION_OUT_OF_C_UP 0x0800 +#define CAM_FLAG_BLOCK_AREA_PROCESSING 0x1000 +#define CAM_FLAG_UNUSED_13 0x2000 +#define CAM_FLAG_UNUSED_CUTSCENE_ACTIVE 0x4000 +#define CAM_FLAG_BEHIND_MARIO_POST_DOOR 0x8000 + +#define CAM_STATUS_NONE 0 +#define CAM_STATUS_MARIO 1 << 0 +#define CAM_STATUS_LAKITU 1 << 1 +#define CAM_STATUS_FIXED 1 << 2 +#define CAM_STATUS_C_DOWN 1 << 3 +#define CAM_STATUS_C_UP 1 << 4 + +#define CAM_STATUS_MODE_GROUP (CAM_STATUS_MARIO | CAM_STATUS_LAKITU | CAM_STATUS_FIXED) +#define CAM_STATUS_C_MODE_GROUP (CAM_STATUS_C_DOWN | CAM_STATUS_C_UP) + +#define SHAKE_ATTACK 1 +#define SHAKE_GROUND_POUND 2 +#define SHAKE_SMALL_DAMAGE 3 +#define SHAKE_MED_DAMAGE 4 +#define SHAKE_LARGE_DAMAGE 5 +#define SHAKE_HIT_FROM_BELOW 8 +#define SHAKE_FALL_DAMAGE 9 +#define SHAKE_SHOCK 10 + +#define SHAKE_ENV_EXPLOSION 1 +#define SHAKE_ENV_BOWSER_THROW_BOUNCE 2 +#define SHAKE_ENV_BOWSER_JUMP 3 +#define SHAKE_ENV_UNUSED_5 5 +#define SHAKE_ENV_UNUSED_6 6 +#define SHAKE_ENV_UNUSED_7 7 +#define SHAKE_ENV_PYRAMID_EXPLODE 8 +#define SHAKE_ENV_JRB_SHIP_DRAIN 9 +#define SHAKE_ENV_FALLING_BITS_PLAT 10 + +#define SHAKE_FOV_SMALL 1 +#define SHAKE_FOV_UNUSED 2 +#define SHAKE_FOV_MEDIUM 3 +#define SHAKE_FOV_LARGE 4 + +#define SHAKE_POS_SMALL 1 +#define SHAKE_POS_MEDIUM 2 +#define SHAKE_POS_LARGE 3 +#define SHAKE_POS_BOWLING_BALL 4 + +#define CUTSCENE_DOOR_PULL 130 +#define CUTSCENE_DOOR_PUSH 131 +#define CUTSCENE_ENTER_CANNON 133 +#define CUTSCENE_ENTER_PAINTING 134 +#define CUTSCENE_DEATH_EXIT 135 +#define CUTSCENE_DOOR_WARP 139 +#define CUTSCENE_DOOR_PULL_MODE 140 +#define CUTSCENE_DOOR_PUSH_MODE 141 +#define CUTSCENE_INTRO_PEACH 142 +#define CUTSCENE_DANCE_ROTATE 143 +#define CUTSCENE_ENTER_BOWSER_ARENA 144 +#define CUTSCENE_0F_UNUSED 145 // Never activated, stub cutscene functions +#define CUTSCENE_UNUSED_EXIT 147 // Never activated +#define CUTSCENE_SLIDING_DOORS_OPEN 149 +#define CUTSCENE_PREPARE_CANNON 150 +#define CUTSCENE_UNLOCK_KEY_DOOR 151 +#define CUTSCENE_STANDING_DEATH 152 +#define CUTSCENE_DEATH_ON_STOMACH 153 +#define CUTSCENE_DEATH_ON_BACK 154 +#define CUTSCENE_QUICKSAND_DEATH 155 +#define CUTSCENE_SUFFOCATION_DEATH 156 +#define CUTSCENE_EXIT_BOWSER_SUCC 157 +#define CUTSCENE_EXIT_BOWSER_DEATH 158 // Never activated +#define CUTSCENE_WATER_DEATH 159 // Not in cutscene switch +#define CUTSCENE_EXIT_PAINTING_SUCC 160 +#define CUTSCENE_CAP_SWITCH_PRESS 161 +#define CUTSCENE_DIALOG 162 +#define CUTSCENE_RACE_DIALOG 163 +#define CUTSCENE_ENTER_PYRAMID_TOP 164 +#define CUTSCENE_DANCE_FLY_AWAY 165 +#define CUTSCENE_DANCE_CLOSEUP 166 +#define CUTSCENE_KEY_DANCE 167 +#define CUTSCENE_SSL_PYRAMID_EXPLODE 168 // Never activated +#define CUTSCENE_EXIT_SPECIAL_SUCC 169 +#define CUTSCENE_NONPAINTING_DEATH 170 +#define CUTSCENE_READ_MESSAGE 171 +#define CUTSCENE_ENDING 172 +#define CUTSCENE_STAR_SPAWN 173 +#define CUTSCENE_GRAND_STAR 174 +#define CUTSCENE_DANCE_DEFAULT 175 +#define CUTSCENE_RED_COIN_STAR_SPAWN 176 +#define CUTSCENE_END_WAVING 177 +#define CUTSCENE_CREDITS 178 +#define CUTSCENE_EXIT_WATERFALL 179 +#define CUTSCENE_EXIT_FALL_WMOTR 180 +#define CUTSCENE_ENTER_POOL 181 + +/** + * Stop the cutscene. + */ +#define CUTSCENE_STOP 0x8000 +/** + * Play the current cutscene shot indefinitely (until canceled). + */ +#define CUTSCENE_LOOP 0x7FFF + +#define HAND_CAM_SHAKE_OFF 0 +#define HAND_CAM_SHAKE_CUTSCENE 1 +#define HAND_CAM_SHAKE_UNUSED 2 +#define HAND_CAM_SHAKE_HANG_OWL 3 +#define HAND_CAM_SHAKE_HIGH 4 +#define HAND_CAM_SHAKE_STAR_DANCE 5 +#define HAND_CAM_SHAKE_LOW 6 + +#define DOOR_DEFAULT 0 +#define DOOR_LEAVING_SPECIAL 1 +#define DOOR_ENTER_LOBBY 2 + +// Might rename these to reflect what they are used for instead "SET_45" etc. +#define CAM_FOV_SET_45 1 +#define CAM_FOV_DEFAULT 2 +#define CAM_FOV_APP_45 4 +#define CAM_FOV_SET_30 5 +#define CAM_FOV_APP_20 6 +#define CAM_FOV_BBH 7 +#define CAM_FOV_APP_80 9 +#define CAM_FOV_APP_30 10 +#define CAM_FOV_APP_60 11 +#define CAM_FOV_ZOOM_30 12 +#define CAM_FOV_SET_29 13 + +#define CAM_EVENT_CANNON 1 +#define CAM_EVENT_SHOT_FROM_CANNON 2 +#define CAM_EVENT_UNUSED_3 3 +#define CAM_EVENT_BOWSER_INIT 4 +#define CAM_EVENT_DOOR_WARP 5 +#define CAM_EVENT_DOOR 6 +#define CAM_EVENT_BOWSER_JUMP 7 +#define CAM_EVENT_BOWSER_THROW_BOUNCE 8 +#define CAM_EVENT_START_INTRO 9 +#define CAM_EVENT_START_GRAND_STAR 10 +#define CAM_EVENT_START_ENDING 11 +#define CAM_EVENT_START_END_WAVING 12 +#define CAM_EVENT_START_CREDITS 13 + +/** + * A copy of player information that is relevant to the camera. + */ +struct PlayerCameraState +{ + /** + * Mario's action on this frame. + */ + /*0x00*/ u32 action; + /*0x04*/ Vec3f pos; + /*0x10*/ Vec3s faceAngle; + /*0x16*/ Vec3s headRotation; + /*0x1C*/ s16 unused; + /** + * Set to nonzero when an event, such as entering a door, starting the credits, or throwing bowser, + * has happened on this frame. + */ + /*0x1E*/ s16 cameraEvent; + /*0x20*/ struct Object *usedObj; +}; + +/** + * Struct containing info that is used when transition_next_state() is called. Stores the intermediate + * distances and angular displacements from lakitu's goal position and focus. + */ +struct TransitionInfo +{ + /*0x00*/ s16 posPitch; + /*0x02*/ s16 posYaw; + /*0x04*/ f32 posDist; + /*0x08*/ s16 focPitch; + /*0x0A*/ s16 focYaw; + /*0x0C*/ f32 focDist; + /*0x10*/ s32 framesLeft; + /*0x14*/ Vec3f marioPos; + /*0x20*/ u8 pad; // for the structs to align, there has to be an extra unused variable here. type is unknown. +}; + +/** + * A point that's used in a spline, controls the direction to move the camera in + * during the shake effect. + */ +struct HandheldShakePoint +{ + /*0x00*/ s8 index; // only set to -1 + /*0x04 (aligned)*/ u32 pad; + /*0x08*/ Vec3s point; +}; // size = 0x10 + +struct Camera; + +/** + * A function that is called by CameraTriggers and cutscene shots. + * These are concurrent: multiple CameraEvents can occur on the same frame. + */ +typedef BAD_RETURN(s32) (*CameraEvent)(struct Camera *c); +/** + * The same type as a CameraEvent, but because these are generally longer, and happen in sequential + * order, they're are called "shots," a term taken from cinematography. + * + * To further tell the difference: CutsceneShots usually call multiple CameraEvents at once, but only + * one CutsceneShot is ever called on a given frame. + */ +typedef CameraEvent CutsceneShot; + +/** + * Defines a bounding box which activates an event while Mario is inside + */ +struct CameraTrigger +{ + /** + * The area this should be checked in, or -1 if it should run in every area of the level. + * + * Triggers with area set to -1 are run by default, they don't care if Mario is inside their bounds. + * However, they are only active if Mario is not already inside an area-specific trigger's + * boundaries. + */ + s8 area; + /// A function that gets called while Mario is in the trigger bounds + CameraEvent event; + // The (x,y,z) position of the center of the bounding box + s16 centerX; + s16 centerY; + s16 centerZ; + // The max displacement in x, y, and z from the center for a point to be considered inside the + // bounding box + s16 boundsX; + s16 boundsY; + s16 boundsZ; + /// This angle rotates Mario's offset from the box's origin, before it is checked for being inside. + s16 boundsYaw; +}; + +/** + * A camera shot that is active for a number of frames. + * Together, a sequence of shots makes up a cutscene. + */ +struct Cutscene +{ + /// The function that gets called. + CutsceneShot shot; + /// How long the shot lasts. + s16 duration; +}; + +/** + * Info for the camera's field of view and the FOV shake effect. + */ +struct CameraFOVStatus +{ + /// The current function being used to set the camera's field of view (before any fov shake is applied). + /*0x00*/ u8 fovFunc; + /// The current field of view in degrees + /*0x04*/ f32 fov; + + // Fields used by shake_camera_fov() + + /// The amount to change the current fov by in the fov shake effect. + /*0x08*/ f32 fovOffset; + /// A bool set in fov_default() but unused otherwise + /*0x0C*/ u32 unusedIsSleeping; + /// The range in degrees to shake fov + /*0x10*/ f32 shakeAmplitude; + /// Used to calculate fovOffset, the phase through the shake's period. + /*0x14*/ s16 shakePhase; + /// How much to progress through the shake period + /*0x16*/ s16 shakeSpeed; + /// How much to decrease shakeAmplitude each frame. + /*0x18*/ s16 decay; +}; + +/** + * Information for a control point in a spline segment. + */ +struct CutsceneSplinePoint +{ + /* The index of this point in the spline. Ignored except for -1, which ends the spline. + An index of -1 should come four points after the start of the last segment. */ + s8 index; + /* Roughly controls the number of frames it takes to progress through the spline segment. + See move_point_along_spline() in camera.c */ + u8 speed; + Vec3s point; +}; + +/** + * Struct containing the nearest floor and ceiling to the player, as well as the previous floor and + * ceiling. It also stores their distances from the player's position. + */ +struct PlayerGeometry +{ + /*0x00*/ struct Surface *currFloor; + /*0x04*/ f32 currFloorHeight; + /*0x08*/ s16 currFloorType; + /*0x0C*/ struct Surface *currCeil; + /*0x10*/ s16 currCeilType; + /*0x14*/ f32 currCeilHeight; + /*0x18*/ struct Surface *prevFloor; + /*0x1C*/ f32 prevFloorHeight; + /*0x20*/ s16 prevFloorType; + /*0x24*/ struct Surface *prevCeil; + /*0x28*/ f32 prevCeilHeight; + /*0x2C*/ s16 prevCeilType; + /// Unused, but recalculated every frame + /*0x30*/ f32 waterHeight; +}; + +/** + * Point used in transitioning between camera modes and C-Up. + */ +struct LinearTransitionPoint +{ + Vec3f focus; + Vec3f pos; + f32 dist; + s16 pitch; + s16 yaw; +}; + +/** + * Info about transitioning between camera modes. + */ +struct ModeTransitionInfo +{ + s16 newMode; + s16 lastMode; + s16 max; + s16 frame; + struct LinearTransitionPoint transitionStart; + struct LinearTransitionPoint transitionEnd; +}; + +/** + * A point in a path used by update_parallel_tracking_camera + */ +struct ParallelTrackingPoint +{ + /// Whether this point is the start of a path + s16 startOfPath; + /// Point used to define a line segment to follow + Vec3f pos; + /// The distance Mario can move along the line before the camera should move + f32 distThresh; + /// The percentage that the camera should move from the line to Mario + f32 zoom; +}; + +/** + * Stores the camera's info + */ +struct CameraStoredInfo +{ + /*0x00*/ Vec3f pos; + /*0x0C*/ Vec3f focus; + /*0x18*/ f32 panDist; + /*0x1C*/ f32 cannonYOffset; +}; + +/** + * Struct used to store cutscene info, like the camera's target position/focus. + * + * See the sCutsceneVars[] array in camera.c for more details. + */ +struct CutsceneVariable +{ + /// Perhaps an index + s32 unused1; + Vec3f point; + Vec3f unusedPoint; + Vec3s angle; + /// Perhaps a boolean or an extra angle + s16 unused2; +}; + +/** + * The main camera struct. Gets updated by the active camera mode and the current level/area. In + * update_lakitu, its pos and focus are used to calculate lakitu's next position and focus, which are + * then used to render the game. + */ +struct Camera +{ + /*0x00*/ u8 mode; // What type of mode the camera uses (see defines above) + /*0x01*/ u8 defMode; + /** + * Determines what direction Mario moves in when the analog stick is moved. + * + * @warning This is NOT the camera's xz-rotation in world space. This is the angle calculated from the + * camera's focus TO the camera's position, instead of the other way around like it should + * be. It's effectively the opposite of the camera's actual yaw. Use + * vec3f_get_dist_and_angle() if you need the camera's yaw. + */ + /*0x02*/ s16 yaw; + /*0x04*/ Vec3f focus; + /*0x10*/ Vec3f pos; + /*0x1C*/ Vec3f unusedVec1; + /// The x coordinate of the "center" of the area. The camera will rotate around this point. + /// For example, this is what makes the camera rotate around the hill in BoB + /*0x28*/ f32 areaCenX; + /// The z coordinate of the "center" of the area. The camera will rotate around this point. + /// For example, this is what makes the camera rotate around the hill in BoB + /*0x2C*/ f32 areaCenZ; + /*0x30*/ u8 cutscene; + /*0x31*/ u8 filler31[0x8]; + /*0x3A*/ s16 nextYaw; + /*0x3C*/ u8 filler3C[0x28]; + /*0x64*/ u8 doorStatus; + /// The y coordinate of the "center" of the area. Unlike areaCenX and areaCenZ, this is only used + /// when paused. See zoom_out_if_paused_and_outside + /*0x68*/ f32 areaCenY; +}; + +/** + * A struct containing info pertaining to lakitu, such as his position and focus, and what + * camera-related effects are happening to him, like camera shakes. + * + * This struct's pos and focus are what is actually used to render the game. + * + * @see update_lakitu() + */ +struct LakituState +{ + /** + * Lakitu's position, which (when CAM_FLAG_SMOOTH_MOVEMENT is set), approaches his goalPos every frame. + */ + /*0x00*/ Vec3f curFocus; + /** + * Lakitu's focus, which (when CAM_FLAG_SMOOTH_MOVEMENT is set), approaches his goalFocus every frame. + */ + /*0x0C*/ Vec3f curPos; + /** + * The focus point that lakitu turns towards every frame. + * If CAM_FLAG_SMOOTH_MOVEMENT is unset, this is the same as curFocus. + */ + /*0x18*/ Vec3f goalFocus; + /** + * The point that lakitu flies towards every frame. + * If CAM_FLAG_SMOOTH_MOVEMENT is unset, this is the same as curPos. + */ + /*0x24*/ Vec3f goalPos; + + /*0x30*/ u8 filler30[12]; // extra unused Vec3f? + + /// Copy of the active camera mode + /*0x3C*/ u8 mode; + /// Copy of the default camera mode + /*0x3D*/ u8 defMode; + + /*0x3E*/ u8 filler3E[10]; + + /*0x48*/ f32 focusDistance; // unused + /*0x4C*/ s16 oldPitch; // unused + /*0x4E*/ s16 oldYaw; // unused + /*0x50*/ s16 oldRoll; // unused + + /// The angular offsets added to lakitu's pitch, yaw, and roll + /*0x52*/ Vec3s shakeMagnitude; + + // shake pitch, yaw, and roll phase: The progression through the camera shake (a cosine wave). + // shake pitch, yaw, and roll vel: The speed of the camera shake. + // shake pitch, yaw, and roll decay: The shake's deceleration. + /*0x58*/ s16 shakePitchPhase; + /*0x5A*/ s16 shakePitchVel; + /*0x5C*/ s16 shakePitchDecay; + + /*0x60*/ Vec3f unusedVec1; + /*0x6C*/ Vec3s unusedVec2; + /*0x72*/ u8 filler72[8]; + + /// Used to rotate the screen when rendering. + /*0x7A*/ s16 roll; + /// Copy of the camera's yaw. + /*0x7C*/ s16 yaw; + /// Copy of the camera's next yaw. + /*0x7E*/ s16 nextYaw; + /// The actual focus point the game uses to render. + /*0x80*/ Vec3f focus; + /// The actual position the game is rendered from. + /*0x8C*/ Vec3f pos; + + // Shake variables: See above description + /*0x98*/ s16 shakeRollPhase; + /*0x9A*/ s16 shakeRollVel; + /*0x9C*/ s16 shakeRollDecay; + /*0x9E*/ s16 shakeYawPhase; + /*0xA0*/ s16 shakeYawVel; + /*0xA2*/ s16 shakeYawDecay; + + // focH,Vspeed: how fast lakitu turns towards his goalFocus. + /// By default HSpeed is 0.8, so lakitu turns 80% of the horz distance to his goal each frame. + /*0xA4*/ f32 focHSpeed; + /// By default VSpeed is 0.3, so lakitu turns 30% of the vert distance to his goal each frame. + /*0xA8*/ f32 focVSpeed; + + // posH,Vspeed: How fast lakitu flies towards his goalPos. + /// By default they are 0.3, so lakitu will fly 30% of the way towards his goal each frame. + /*0xAC*/ f32 posHSpeed; + /*0xB0*/ f32 posVSpeed; + + /// The roll offset applied during part of the key dance cutscene + /*0xB4*/ s16 keyDanceRoll; + /// Mario's action from the previous frame. Only used to determine if Mario just finished a dive. + /*0xB8*/ u32 lastFrameAction; + /*0xBC*/ s16 unused; +}; + + +// // bss order hack to not affect BSS order. if possible, remove me, but it will be hard to match otherwise +// #ifndef INCLUDED_FROM_CAMERA_C +// // BSS +// extern s16 sSelectionFlags; +// extern s16 sCameraSoundFlags; +// extern u16 sCButtonsPressed; +// extern struct PlayerCameraState gPlayerCameraState[2]; +// extern struct LakituState gLakituState; +// extern s16 gCameraMovementFlags; +// extern s32 gObjCutsceneDone; +// extern struct Camera *gCamera; +// #endif +// +// extern struct Object *gCutsceneFocus; +// extern struct Object *gSecondCameraFocus; +// extern u8 gRecentCutscene; +// +// // TODO: sort all of this extremely messy shit out after the split +// +// void set_camera_shake_from_hit(s16 shake); +// void set_environmental_camera_shake(s16 shake); +// void set_camera_shake_from_point(s16 shake, f32 posX, f32 posY, f32 posZ); +// void move_mario_head_c_up(UNUSED struct Camera *c); +// void transition_next_state(UNUSED struct Camera *c, s16 frames); +// void set_camera_mode(struct Camera *c, s16 mode, s16 frames); +// void update_camera(struct Camera *c); +// void reset_camera(struct Camera *c); +// void init_camera(struct Camera *c); +// void select_mario_cam_mode(void); +// Gfx *geo_camera_main(s32 callContext, struct GraphNode *g, void *context); +// void stub_camera_2(UNUSED struct Camera *c); +// void stub_camera_3(UNUSED struct Camera *c); +// void vec3f_sub(Vec3f dst, Vec3f src); +// void object_pos_to_vec3f(Vec3f dst, struct Object *o); +// void vec3f_to_object_pos(struct Object *o, Vec3f src); +// s32 move_point_along_spline(Vec3f p, struct CutsceneSplinePoint spline[], s16 *splineSegment, f32 *progress); +// s32 cam_select_alt_mode(s32 angle); +// s32 set_cam_angle(s32 mode); +// void set_handheld_shake(u8 mode); +// void shake_camera_handheld(Vec3f pos, Vec3f focus); +// s32 find_c_buttons_pressed(u16 currentState, u16 buttonsPressed, u16 buttonsDown); +// s32 update_camera_hud_status(struct Camera *c); +// s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius); +// s32 clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch); +// s32 is_within_100_units_of_mario(f32 posX, f32 posY, f32 posZ); +// s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale); +// s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier); +// f32 approach_f32_asymptotic(f32 current, f32 target, f32 multiplier); +// s32 approach_s16_asymptotic_bool(s16 *current, s16 target, s16 divisor); +// s32 approach_s16_asymptotic(s16 current, s16 target, s16 divisor); +// void approach_vec3f_asymptotic(Vec3f current, Vec3f target, f32 xMul, f32 yMul, f32 zMul); +// void set_or_approach_vec3f_asymptotic(Vec3f dst, Vec3f goal, f32 xMul, f32 yMul, f32 zMul); +// s32 camera_approach_s16_symmetric_bool(s16 *current, s16 target, s16 increment); +// s32 set_or_approach_s16_symmetric(s16 *current, s16 target, s16 increment); +// s32 camera_approach_f32_symmetric_bool(f32 *current, f32 target, f32 increment); +// f32 camera_approach_f32_symmetric(f32 value, f32 target, f32 increment); +// void random_vec3s(Vec3s dst, s16 xRange, s16 yRange, s16 zRange); +// s32 clamp_positions_and_find_yaw(Vec3f pos, Vec3f origin, f32 xMax, f32 xMin, f32 zMax, f32 zMin); +// s32 is_range_behind_surface(Vec3f from, Vec3f to, struct Surface *surf, s16 range, s16 surfType); +// void scale_along_line(Vec3f dest, Vec3f from, Vec3f to, f32 scale); +// s16 calculate_pitch(Vec3f from, Vec3f to); +// s16 calculate_yaw(Vec3f from, Vec3f to); +// void calculate_angles(Vec3f from, Vec3f to, s16 *pitch, s16 *yaw); +// f32 calc_abs_dist(Vec3f a, Vec3f b); +// f32 calc_hor_dist(Vec3f a, Vec3f b); +// void rotate_in_xz(Vec3f dst, Vec3f src, s16 yaw); +// void rotate_in_yz(Vec3f dst, Vec3f src, s16 pitch); +// void set_camera_pitch_shake(s16 mag, s16 decay, s16 inc); +// void set_camera_yaw_shake(s16 mag, s16 decay, s16 inc); +// void set_camera_roll_shake(s16 mag, s16 decay, s16 inc); +// void set_pitch_shake_from_point(s16 mag, s16 decay, s16 inc, f32 maxDist, f32 posX, f32 posY, f32 posZ); +// void shake_camera_pitch(Vec3f pos, Vec3f focus); +// void shake_camera_yaw(Vec3f pos, Vec3f focus); +// void shake_camera_roll(s16 *roll); +// s32 offset_yaw_outward_radial(struct Camera *c, s16 areaYaw); +// void play_camera_buzz_if_cdown(void); +// void play_camera_buzz_if_cbutton(void); +// void play_camera_buzz_if_c_sideways(void); +// void play_sound_cbutton_up(void); +// void play_sound_cbutton_down(void); +// void play_sound_cbutton_side(void); +// void play_sound_button_change_blocked(void); +// void play_sound_rbutton_changed(void); +// void play_sound_if_cam_switched_to_lakitu_or_mario(void); +// s32 radial_camera_input(struct Camera *c, UNUSED f32 unused); +// s32 trigger_cutscene_dialog(s32 trigger); +// void handle_c_button_movement(struct Camera *c); +// void start_cutscene(struct Camera *c, u8 cutscene); +// u8 get_cutscene_from_mario_status(struct Camera *c); +// void warp_camera(f32 displacementX, f32 displacementY, f32 displacementZ); +// void approach_camera_height(struct Camera *c, f32 goal, f32 inc); +// void offset_rotated(Vec3f dst, Vec3f from, Vec3f to, Vec3s rotation); +// s16 next_lakitu_state(Vec3f newPos, Vec3f newFoc, Vec3f curPos, Vec3f curFoc, Vec3f oldPos, Vec3f oldFoc, s16 yaw); +// void set_fixed_cam_axis_sa_lobby(UNUSED s16 preset); +// s16 camera_course_processing(struct Camera *c); +// void resolve_geometry_collisions(Vec3f pos, UNUSED Vec3f lastGood); +// s32 rotate_camera_around_walls(struct Camera *c, Vec3f cPos, s16 *avoidYaw, s16 yawRange); +// void find_mario_floor_and_ceil(struct PlayerGeometry *pg); +// u8 start_object_cutscene_without_focus(u8 cutscene); +// s16 cutscene_object_with_dialog(u8 cutscene, struct Object *o, s16 dialogID); +// s16 cutscene_object_without_dialog(u8 cutscene, struct Object *o); +// s16 cutscene_object(u8 cutscene, struct Object *o); +// void play_cutscene(struct Camera *c); +// s32 cutscene_event(CameraEvent event, struct Camera * c, s16 start, s16 end); +// s32 cutscene_spawn_obj(u32 obj, s16 frame); +// void set_fov_shake(s16 amplitude, s16 decay, s16 shakeSpeed); +// +// void set_fov_function(u8 func); +// void cutscene_set_fov_shake_preset(u8 preset); +// void set_fov_shake_from_point_preset(u8 preset, f32 posX, f32 posY, f32 posZ); +// void obj_rotate_towards_point(struct Object *o, Vec3f point, s16 pitchOff, s16 yawOff, s16 pitchDiv, s16 yawDiv); +// +// Gfx *geo_camera_fov(s32 callContext, struct GraphNode *g, UNUSED void *context); +// + +#endif // CAMERA_H diff --git a/src/game/interaction.c b/src/game/interaction.c new file mode 100644 index 0000000..ffa5f38 --- /dev/null +++ b/src/game/interaction.c @@ -0,0 +1,928 @@ +// HEAVILY EDITED === Specific interaction functions removed + +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "area.h" +//#include "actors/common1.h" +//#include "audio/external.h" +//#include "behavior_actions.h" +//#include "behavior_data.h" +#include "camera.h" +//#include "course_table.h" +//#include "dialog_ids.h" +#include "../engine/math_util.h" +#include "../engine/surface_collision.h" +//#include "game_init.h" +#include "interaction.h" +#include "level_update.h" +#include "mario.h" +#include "mario_step.h" +#include "memory.h" +//#include "obj_behaviors.h" +//#include "object_helpers.h" +#include "save_file.h" +//#include "seq_ids.h" +#include "../include/sm64.h" +//#include "sound_init.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + +#define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01 +#define INT_PUNCH (1 << 1) // 0x02 +#define INT_KICK (1 << 2) // 0x04 +#define INT_TRIP (1 << 3) // 0x08 +#define INT_SLIDE_KICK (1 << 4) // 0x10 +#define INT_FAST_ATTACK_OR_SHELL (1 << 5) // 0x20 +#define INT_HIT_FROM_ABOVE (1 << 6) // 0x40 +#define INT_HIT_FROM_BELOW (1 << 7) // 0x80 + +#define INT_ATTACK_NOT_FROM_BELOW \ + (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \ + | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE) + +#define INT_ANY_ATTACK \ + (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \ + | INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE | INT_HIT_FROM_BELOW) + +#define INT_ATTACK_NOT_WEAK_FROM_ABOVE \ + (INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_HIT_FROM_BELOW) + +u8 sDelayInvincTimer; +s16 sInvulnerable; + +struct InteractionHandler { + u32 interactType; + u32 (*handler)(struct MarioState *, u32, struct Object *); +}; + +static struct InteractionHandler sInteractionHandlers[] = { }; + +static u32 sForwardKnockbackActions[][3] = { + { ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB }, + { ACT_FORWARD_AIR_KB, ACT_FORWARD_AIR_KB, ACT_HARD_FORWARD_AIR_KB }, + { ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB }, +}; + +static u32 sBackwardKnockbackActions[][3] = { + { ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB }, + { ACT_BACKWARD_AIR_KB, ACT_BACKWARD_AIR_KB, ACT_HARD_BACKWARD_AIR_KB }, + { ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB }, +}; + +static u8 sDisplayingDoorText = FALSE; +static u8 sJustTeleported = FALSE; +static u8 sPssSlideStarted = FALSE; + +/** + * Returns the type of cap Mario is wearing. + */ +u32 get_mario_cap_flag(struct Object *capObject) { + + return MARIO_NORMAL_CAP; + +// const BehaviorScript *script = virtual_to_segmented(0x13, capObject->behavior); +// if (script == bhvNormalCap) { +// return MARIO_NORMAL_CAP; +// } else if (script == bhvMetalCap) { +// return MARIO_METAL_CAP; +// } else if (script == bhvWingCap) { +// return MARIO_WING_CAP; +// } else if (script == bhvVanishCap) { +// return MARIO_VANISH_CAP; +// } + + return 0; +} + +/** + * Returns true if the passed in object has a moving angle yaw + * in the angular range given towards Mario. + */ +u32 object_facing_mario(struct MarioState *m, struct Object *o, s16 angleRange) { + f32 dx = m->pos[0] - o->oPosX; + f32 dz = m->pos[2] - o->oPosZ; + + s16 angleToMario = atan2s(dz, dx); + s16 dAngle = angleToMario - o->oMoveAngleYaw; + + if (-angleRange <= dAngle && dAngle <= angleRange) { + return TRUE; + } + + return FALSE; +} + +s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o) { + f32 dx = o->oPosX - m->pos[0]; + f32 dz = o->oPosZ - m->pos[2]; + + return atan2s(dz, dx); +} + +/** + * Determines Mario's interaction with a given object depending on their proximity, + * action, speed, and position. + */ +u32 determine_interaction(struct MarioState *m, struct Object *o) { + u32 interaction = 0; + u32 action = m->action; + + if (action & ACT_FLAG_ATTACKING) { + if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING || action == ACT_JUMP_KICK) { + s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1]; + + if (m->flags & MARIO_PUNCHING) { + // 120 degrees total, or 60 each way + if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) { + interaction = INT_PUNCH; + } + } + if (m->flags & MARIO_KICKING) { + // 120 degrees total, or 60 each way + if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) { + interaction = INT_KICK; + } + } + if (m->flags & MARIO_TRIPPING) { + // 180 degrees total, or 90 each way + if (-0x4000 <= dYawToObject && dYawToObject <= 0x4000) { + interaction = INT_TRIP; + } + } + } else if (action == ACT_GROUND_POUND || action == ACT_TWIRLING) { + if (m->vel[1] < 0.0f) { + interaction = INT_GROUND_POUND_OR_TWIRL; + } + } else if (action == ACT_GROUND_POUND_LAND || action == ACT_TWIRL_LAND) { + // Neither ground pounding nor twirling change Mario's vertical speed on landing., + // so the speed check is nearly always true (perhaps not if you land while going upwards?) + // Additionally, actionState it set on each first thing in their action, so this is + // only true prior to the very first frame (i.e. active 1 frame prior to it run). + if (m->vel[1] < 0.0f && m->actionState == 0) { + interaction = INT_GROUND_POUND_OR_TWIRL; + } + } else if (action == ACT_SLIDE_KICK || action == ACT_SLIDE_KICK_SLIDE) { + interaction = INT_SLIDE_KICK; + } else if (action & ACT_FLAG_RIDING_SHELL) { + interaction = INT_FAST_ATTACK_OR_SHELL; + } else if (m->forwardVel <= -26.0f || 26.0f <= m->forwardVel) { + interaction = INT_FAST_ATTACK_OR_SHELL; + } + } + + // Prior to this, the interaction type could be overwritten. This requires, however, + // that the interaction not be set prior. This specifically overrides turning a ground + // pound into just a bounce. + if (interaction == 0 && (action & ACT_FLAG_AIR)) { + if (m->vel[1] < 0.0f) { + if (m->pos[1] > o->oPosY) { + interaction = INT_HIT_FROM_ABOVE; + } + } else { + if (m->pos[1] < o->oPosY) { + interaction = INT_HIT_FROM_BELOW; + } + } + } + + return interaction; +} + +/** + * Sets the interaction types for INT_STATUS_INTERACTED, INT_STATUS_WAS_ATTACKED + */ +u32 attack_object(struct Object *o, s32 interaction) { + u32 attackType = 0; + + switch (interaction) { + case INT_GROUND_POUND_OR_TWIRL: + attackType = ATTACK_GROUND_POUND_OR_TWIRL; + break; + case INT_PUNCH: + attackType = ATTACK_PUNCH; + break; + case INT_KICK: + case INT_TRIP: + attackType = ATTACK_KICK_OR_TRIP; + break; + case INT_SLIDE_KICK: + case INT_FAST_ATTACK_OR_SHELL: + attackType = ATTACK_FAST_ATTACK; + break; + case INT_HIT_FROM_ABOVE: + attackType = ATTACK_FROM_ABOVE; + break; + case INT_HIT_FROM_BELOW: + attackType = ATTACK_FROM_BELOW; + break; + } + + o->oInteractStatus = attackType + (INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED); + return attackType; +} + +void mario_stop_riding_object(struct MarioState *m) { + if (m->riddenObj != NULL) { + m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING; + stop_shell_music(); + m->riddenObj = NULL; + } +} + +void mario_grab_used_object(struct MarioState *m) { + if (m->heldObj == NULL) { + m->heldObj = m->usedObj; +// obj_set_held_state(m->heldObj, bhvCarrySomething3); + } +} + +void mario_drop_held_object(struct MarioState *m) { + if (m->heldObj != NULL) { + // if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { + // stop_shell_music(); + // } + + // obj_set_held_state(m->heldObj, bhvCarrySomething4); + + // ! When dropping an object instead of throwing it, it will be put at Mario's + // y-positon instead of the HOLP's y-position. This fact is often exploited when + // cloning objects. + m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0]; + m->heldObj->oPosY = m->pos[1]; + m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2]; + + m->heldObj->oMoveAngleYaw = m->faceAngle[1]; + + m->heldObj = NULL; + } +} + +void mario_throw_held_object(struct MarioState *m) { + if (m->heldObj != NULL) { + // if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { + // stop_shell_music(); + // } + + // obj_set_held_state(m->heldObj, bhvCarrySomething5); + + m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]); + m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1]; + m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]); + + m->heldObj->oMoveAngleYaw = m->faceAngle[1]; + + m->heldObj = NULL; + } +} + +void mario_stop_riding_and_holding(struct MarioState *m) { + mario_drop_held_object(m); + mario_stop_riding_object(m); + + if (m->action == ACT_RIDING_HOOT) { + m->usedObj->oInteractStatus = 0; + m->usedObj->oHootMarioReleaseTime = gGlobalTimer; + } +} + +u32 does_mario_have_normal_cap_on_head(struct MarioState *m) { + return (m->flags & (MARIO_CAPS | MARIO_CAP_ON_HEAD)) == (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); +} + +void mario_blow_off_cap(struct MarioState *m, f32 capSpeed) { +// struct Object *capObject; + +// if (does_mario_have_normal_cap_on_head(m)) { +// save_file_set_cap_pos(m->pos[0], m->pos[1], m->pos[2]); + +// m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); + +// capObject = spawn_object(m->marioObj, MODEL_MARIOS_CAP, bhvNormalCap); + +// capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f; +// capObject->oForwardVel = capSpeed; +// capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400); + +// if (m->forwardVel < 0.0f) { +// capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000); +// } +// } +} + +u32 mario_lose_cap_to_enemy(u32 arg) { + u32 wasWearingCap = FALSE; + + if (does_mario_have_normal_cap_on_head(gMarioState)) { + save_file_set_flags(arg == 1 ? SAVE_FLAG_CAP_ON_KLEPTO : SAVE_FLAG_CAP_ON_UKIKI); + gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); + wasWearingCap = TRUE; + } + + return wasWearingCap; +} + +void mario_retrieve_cap(void) { + mario_drop_held_object(gMarioState); + save_file_clear_flags(SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI); + gMarioState->flags &= ~MARIO_CAP_ON_HEAD; + gMarioState->flags |= MARIO_NORMAL_CAP | MARIO_CAP_IN_HAND; +} + +u32 able_to_grab_object(struct MarioState *m, UNUSED struct Object *o) { + u32 action = m->action; + + if (action == ACT_DIVE_SLIDE || action == ACT_DIVE) { + if (!(o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { + return TRUE; + } + } else if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING) { + if (m->actionArg < 2) { + return TRUE; + } + } + + return FALSE; +} + +struct Object *mario_get_collided_object(struct MarioState *m, u32 interactType) { + s32 i; + struct Object *object; + + for (i = 0; i < m->marioObj->numCollidedObjs; i++) { + object = m->marioObj->collidedObjs[i]; + + if (object->oInteractType == interactType) { + return object; + } + } + + return NULL; +} + +u32 mario_check_object_grab(struct MarioState *m) { + u32 result = FALSE; + const BehaviorScript *script; + + if (m->input & INPUT_INTERACT_OBJ_GRABBABLE) { + script = virtual_to_segmented(0x13, m->interactObj->behavior); + +// if (script == bhvBowser) { +// s16 facingDYaw = m->faceAngle[1] - m->interactObj->oMoveAngleYaw; +// if (facingDYaw >= -0x5555 && facingDYaw <= 0x5555) { +// m->faceAngle[1] = m->interactObj->oMoveAngleYaw; +// m->usedObj = m->interactObj; +// result = set_mario_action(m, ACT_PICKING_UP_BOWSER, 0); +// } +// } else { + s16 facingDYaw = mario_obj_angle_to_object(m, m->interactObj) - m->faceAngle[1]; + if (facingDYaw >= -0x2AAA && facingDYaw <= 0x2AAA) { + m->usedObj = m->interactObj; + + if (!(m->action & ACT_FLAG_AIR)) { + set_mario_action( + m, (m->action & ACT_FLAG_DIVING) ? ACT_DIVE_PICKING_UP : ACT_PICKING_UP, 0); + } + + result = TRUE; + } +// } + } + + return result; +} + +u32 bully_knock_back_mario(struct MarioState *mario) { + struct BullyCollisionData marioData; + struct BullyCollisionData bullyData; + s16 newMarioYaw; + s16 newBullyYaw; + s16 marioDYaw; + UNUSED s16 bullyDYaw; + + u32 bonkAction = 0; + + struct Object *bully = mario->interactObj; + + //! Conversion ratios multiply to more than 1 (could allow unbounded speed + // with bonk cancel - but this isn't important for regular bully battery) + f32 bullyToMarioRatio = bully->hitboxRadius * 3 / 53; + f32 marioToBullyRatio = 53.0f / bully->hitboxRadius; + + init_bully_collision_data(&marioData, mario->pos[0], mario->pos[2], mario->forwardVel, + mario->faceAngle[1], bullyToMarioRatio, 52.0f); + + init_bully_collision_data(&bullyData, bully->oPosX, bully->oPosZ, bully->oForwardVel, + bully->oMoveAngleYaw, marioToBullyRatio, bully->hitboxRadius + 2.0f); + + if (mario->forwardVel != 0.0f) { + transfer_bully_speed(&marioData, &bullyData); + } else { + transfer_bully_speed(&bullyData, &marioData); + } + + newMarioYaw = atan2s(marioData.velZ, marioData.velX); + newBullyYaw = atan2s(bullyData.velZ, bullyData.velX); + + marioDYaw = newMarioYaw - mario->faceAngle[1]; + bullyDYaw = newBullyYaw - bully->oMoveAngleYaw; + + mario->faceAngle[1] = newMarioYaw; + mario->forwardVel = sqrtf(marioData.velX * marioData.velX + marioData.velZ * marioData.velZ); + mario->pos[0] = marioData.posX; + mario->pos[2] = marioData.posZ; + + bully->oMoveAngleYaw = newBullyYaw; + bully->oForwardVel = sqrtf(bullyData.velX * bullyData.velX + bullyData.velZ * bullyData.velZ); + bully->oPosX = bullyData.posX; + bully->oPosZ = bullyData.posZ; + + if (marioDYaw < -0x4000 || marioDYaw > 0x4000) { + mario->faceAngle[1] += 0x8000; + mario->forwardVel *= -1.0f; + + if (mario->action & ACT_FLAG_AIR) { + bonkAction = ACT_BACKWARD_AIR_KB; + } else { + bonkAction = ACT_SOFT_BACKWARD_GROUND_KB; + } + } else { + if (mario->action & ACT_FLAG_AIR) { + bonkAction = ACT_FORWARD_AIR_KB; + } else { + bonkAction = ACT_SOFT_FORWARD_GROUND_KB; + } + } + + return bonkAction; +} + +void bounce_off_object(struct MarioState *m, struct Object *o, f32 velY) { + m->pos[1] = o->oPosY + o->hitboxHeight; + m->vel[1] = velY; + + m->flags &= ~MARIO_UNKNOWN_08; + + play_sound(SOUND_ACTION_BOUNCE_OFF_OBJECT, m->marioObj->header.gfx.cameraToObject); +} + +void hit_object_from_below(struct MarioState *m, UNUSED struct Object *o) { + m->vel[1] = 0.0f; + set_camera_shake_from_hit(SHAKE_HIT_FROM_BELOW); +} + +static u32 unused_determine_knockback_action(struct MarioState *m) { + u32 bonkAction; + s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj); + s16 facingDYaw = angleToObject - m->faceAngle[1]; + + if (m->forwardVel < 16.0f) { + m->forwardVel = 16.0f; + } + + m->faceAngle[1] = angleToObject; + + if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) { + m->forwardVel *= -1.0f; + if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { + bonkAction = ACT_BACKWARD_AIR_KB; + } else { + bonkAction = ACT_SOFT_BACKWARD_GROUND_KB; + } + } else { + m->faceAngle[1] += 0x8000; + if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { + bonkAction = ACT_FORWARD_AIR_KB; + } else { + bonkAction = ACT_SOFT_FORWARD_GROUND_KB; + } + } + + return bonkAction; +} + +u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) { + u32 bonkAction; + + s16 terrainIndex = 0; // 1 = air, 2 = water, 0 = default + s16 strengthIndex = 0; + + s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj); + s16 facingDYaw = angleToObject - m->faceAngle[1]; + s16 remainingHealth = m->health - 0x40 * m->hurtCounter; + + if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) { + terrainIndex = 2; + } else if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) { + terrainIndex = 1; + } + + if (remainingHealth < 0x100) { + strengthIndex = 2; + } else if (m->interactObj->oDamageOrCoinValue >= 4) { + strengthIndex = 2; + } else if (m->interactObj->oDamageOrCoinValue >= 2) { + strengthIndex = 1; + } + + m->faceAngle[1] = angleToObject; + + if (terrainIndex == 2) { + if (m->forwardVel < 28.0f) { + mario_set_forward_vel(m, 28.0f); + } + + if (m->pos[1] >= m->interactObj->oPosY) { + if (m->vel[1] < 20.0f) { + m->vel[1] = 20.0f; + } + } else { + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + } + } else { + if (m->forwardVel < 16.0f) { + mario_set_forward_vel(m, 16.0f); + } + } + + if (-0x4000 <= facingDYaw && facingDYaw <= 0x4000) { + m->forwardVel *= -1.0f; + bonkAction = sBackwardKnockbackActions[terrainIndex][strengthIndex]; + } else { + m->faceAngle[1] += 0x8000; + bonkAction = sForwardKnockbackActions[terrainIndex][strengthIndex]; + } + + return bonkAction; +} + +void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 padding) { + f32 minDistance = o->hitboxRadius + m->marioObj->hitboxRadius + padding; + + f32 offsetX = m->pos[0] - o->oPosX; + f32 offsetZ = m->pos[2] - o->oPosZ; + f32 distance = sqrtf(offsetX * offsetX + offsetZ * offsetZ); + + if (distance < minDistance) { + struct Surface *floor; + s16 pushAngle; + f32 newMarioX; + f32 newMarioZ; + + if (distance == 0.0f) { + pushAngle = m->faceAngle[1]; + } else { + pushAngle = atan2s(offsetZ, offsetX); + } + + newMarioX = o->oPosX + minDistance * sins(pushAngle); + newMarioZ = o->oPosZ + minDistance * coss(pushAngle); + + f32_find_wall_collision(&newMarioX, &m->pos[1], &newMarioZ, 60.0f, 50.0f); + + find_floor(newMarioX, m->pos[1], newMarioZ, &floor); + if (floor != NULL) { + //! Doesn't update Mario's referenced floor (allows oob death when + // an object pushes you into a steep slope while in a ground action) + m->pos[0] = newMarioX; + m->pos[2] = newMarioZ; + } + } +} + +void bounce_back_from_attack(struct MarioState *m, u32 interaction) { + if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP)) { + if (m->action == ACT_PUNCHING) { + m->action = ACT_MOVE_PUNCHING; + } + + if (m->action & ACT_FLAG_AIR) { + mario_set_forward_vel(m, -16.0f); + } else { + mario_set_forward_vel(m, -48.0f); + } + + set_camera_shake_from_hit(SHAKE_ATTACK); + m->particleFlags |= PARTICLE_TRIANGLE; + } + + if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP | INT_FAST_ATTACK_OR_SHELL)) { + play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); + } +} + +u32 should_push_or_pull_door(struct MarioState *m, struct Object *o) { + f32 dx = o->oPosX - m->pos[0]; + f32 dz = o->oPosZ - m->pos[2]; + + s16 dYaw = o->oMoveAngleYaw - atan2s(dz, dx); + + return (dYaw >= -0x4000 && dYaw <= 0x4000) ? 0x00000001 : 0x00000002; +} + +u32 take_damage_from_interact_object(struct MarioState *m) { + s32 shake; + s32 damage = m->interactObj->oDamageOrCoinValue; + + if (damage >= 4) { + shake = SHAKE_LARGE_DAMAGE; + } else if (damage >= 2) { + shake = SHAKE_MED_DAMAGE; + } else { + shake = SHAKE_SMALL_DAMAGE; + } + + if (!(m->flags & MARIO_CAP_ON_HEAD)) { + damage += (damage + 1) / 2; + } + + if (m->flags & MARIO_METAL_CAP) { + damage = 0; + } + + m->hurtCounter += 4 * damage; + +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + set_camera_shake_from_hit(shake); + + return damage; +} + +u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) { + u32 damage; + + if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) + && !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) { + o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO; + m->interactObj = o; + + damage = take_damage_from_interact_object(m); + + if (o->oInteractionSubtype & INT_SUBTYPE_BIG_KNOCKBACK) { + m->forwardVel = 40.0f; + } + + if (o->oDamageOrCoinValue > 0) { + play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); + } + + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue), + damage); + } + + return FALSE; +} + +void reset_mario_pitch(struct MarioState *m) { + if (m->action == ACT_WATER_JUMP || m->action == ACT_SHOT_FROM_CANNON || m->action == ACT_FLYING) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + m->faceAngle[0] = 0; + } +} + + +u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struct Object *o) { + if ((!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !sInvulnerable) + && (o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) { + if (object_facing_mario(m, o, 0x2AAA)) { + mario_stop_riding_and_holding(m); + o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_GRABBED_MARIO; + + m->faceAngle[1] = o->oMoveAngleYaw; + m->interactObj = o; + m->usedObj = o; + + update_mario_sound_and_camera(m); + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + return set_mario_action(m, ACT_GRABBED, 0); + } + } + + push_mario_out_of_object(m, o, -5.0f); + return FALSE; +} + + +u32 mario_can_talk(struct MarioState *m, u32 arg) { + s16 val6; + + if ((m->action & ACT_FLAG_IDLE) != 0x00000000) { + return TRUE; + } + + if (m->action == ACT_WALKING) { + if (arg) { + return TRUE; + } + + val6 = m->marioObj->header.gfx.animInfo.animID; + + if (val6 == 0x0080 || val6 == 0x007F || val6 == 0x006C) { + return TRUE; + } + } + + return FALSE; +} + +#ifdef VERSION_JP +#define READ_MASK (INPUT_B_PRESSED) +#else +#define READ_MASK (INPUT_B_PRESSED | INPUT_A_PRESSED) +#endif + +#ifdef VERSION_JP +#define SIGN_RANGE 0x38E3 +#else +#define SIGN_RANGE 0x4000 +#endif + +u32 check_read_sign(struct MarioState *m, struct Object *o) { + if ((m->input & READ_MASK) && mario_can_talk(m, 0) && object_facing_mario(m, o, SIGN_RANGE)) { + s16 facingDYaw = (s16)(o->oMoveAngleYaw + 0x8000) - m->faceAngle[1]; + if (facingDYaw >= -SIGN_RANGE && facingDYaw <= SIGN_RANGE) { + f32 targetX = o->oPosX + 105.0f * sins(o->oMoveAngleYaw); + f32 targetZ = o->oPosZ + 105.0f * coss(o->oMoveAngleYaw); + + m->marioObj->oMarioReadingSignDYaw = facingDYaw; + m->marioObj->oMarioReadingSignDPosX = targetX - m->pos[0]; + m->marioObj->oMarioReadingSignDPosZ = targetZ - m->pos[2]; + + m->interactObj = o; + m->usedObj = o; + return set_mario_action(m, ACT_READING_SIGN, 0); + } + } + + return FALSE; +} + +u32 check_npc_talk(struct MarioState *m, struct Object *o) { + if ((m->input & READ_MASK) && mario_can_talk(m, 1)) { + s16 facingDYaw = mario_obj_angle_to_object(m, o) - m->faceAngle[1]; + if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) { + o->oInteractStatus = INT_STATUS_INTERACTED; + + m->interactObj = o; + m->usedObj = o; + + push_mario_out_of_object(m, o, -10.0f); + return set_mario_action(m, ACT_WAITING_FOR_DIALOG, 0); + } + } + + push_mario_out_of_object(m, o, -10.0f); + return FALSE; +} + +void check_kick_or_punch_wall(struct MarioState *m) { + if (m->flags & (MARIO_PUNCHING | MARIO_KICKING | MARIO_TRIPPING)) { + Vec3f detector; + detector[0] = m->pos[0] + 50.0f * sins(m->faceAngle[1]); + detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]); + detector[1] = m->pos[1]; + + if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) { + if (m->action != ACT_MOVE_PUNCHING || m->forwardVel >= 0.0f) { + if (m->action == ACT_PUNCHING) { + m->action = ACT_MOVE_PUNCHING; + } + + mario_set_forward_vel(m, -48.0f); + play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_TRIANGLE; + } else if (m->action & ACT_FLAG_AIR) { + mario_set_forward_vel(m, -16.0f); + play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_TRIANGLE; + } + } + } +} + +void mario_process_interactions(struct MarioState *m) { + sDelayInvincTimer = FALSE; + sInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0; + + if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) { + s32 i; + for (i = 0; i < 31; i++) { + u32 interactType = sInteractionHandlers[i].interactType; + if (m->collidedObjInteractTypes & interactType) { + struct Object *object = mario_get_collided_object(m, interactType); + + m->collidedObjInteractTypes &= ~interactType; + + if (!(object->oInteractStatus & INT_STATUS_INTERACTED)) { + if (sInteractionHandlers[i].handler(m, interactType, object)) { + break; + } + } + } + } + } + + if (m->invincTimer > 0 && !sDelayInvincTimer) { + m->invincTimer -= 1; + } + + //! If the kick/punch flags are set and an object collision changes Mario's + // action, he will get the kick/punch wall speed anyway. + check_kick_or_punch_wall(m); + m->flags &= ~MARIO_PUNCHING & ~MARIO_KICKING & ~MARIO_TRIPPING; + + if (!(m->marioObj->collidedObjInteractTypes & (INTERACT_WARP_DOOR | INTERACT_DOOR))) { + sDisplayingDoorText = FALSE; + } + if (!(m->marioObj->collidedObjInteractTypes & INTERACT_WARP)) { + sJustTeleported = FALSE; + } +} + +void check_death_barrier(struct MarioState *m) { + if (m->pos[1] < m->floorHeight + 2048.0f) { + if (level_trigger_warp(m, WARP_OP_WARP_FLOOR) == 20 && !(m->flags & MARIO_UNKNOWN_18)) { + play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); + } + } +} + +void check_lava_boost(struct MarioState *m) { + if (!(m->action & ACT_FLAG_RIDING_SHELL) && m->pos[1] < m->floorHeight + 10.0f) { + if (!(m->flags & MARIO_METAL_CAP)) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18; + } + + update_mario_sound_and_camera(m); + drop_and_set_mario_action(m, ACT_LAVA_BOOST, 0); + } +} + +void pss_begin_slide(UNUSED struct MarioState *m) { + if (!(gHudDisplay.flags & HUD_DISPLAY_FLAG_TIMER)) { + level_control_timer(TIMER_CONTROL_SHOW); + level_control_timer(TIMER_CONTROL_START); + sPssSlideStarted = TRUE; + } +} + +void pss_end_slide(struct MarioState *m) { + //! This flag isn't set on death or level entry, allowing double star spawn + if (sPssSlideStarted) { + u16 slideTime = level_control_timer(TIMER_CONTROL_STOP); + if (slideTime < 630) { + m->marioObj->oBehParams = (1 << 24); + spawn_default_star(-6358.0f, -4300.0f, 4700.0f); + } + sPssSlideStarted = FALSE; + } +} + +void mario_handle_special_floors(struct MarioState *m) { + if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) { + return; + } + + if (m->floor != NULL) { + s32 floorType = m->floor->type; + + switch (floorType) { + case SURFACE_DEATH_PLANE: + case SURFACE_VERTICAL_WIND: + check_death_barrier(m); + break; + + case SURFACE_WARP: + level_trigger_warp(m, WARP_OP_WARP_FLOOR); + break; + + case SURFACE_TIMER_START: + pss_begin_slide(m); + break; + + case SURFACE_TIMER_END: + pss_end_slide(m); + break; + } + + if (!(m->action & ACT_FLAG_AIR) && !(m->action & ACT_FLAG_SWIMMING)) { + switch (floorType) { + case SURFACE_BURNING: + check_lava_boost(m); + break; + } + } + } +} diff --git a/src/game/interaction.h b/src/game/interaction.h new file mode 100644 index 0000000..b913ccc --- /dev/null +++ b/src/game/interaction.h @@ -0,0 +1,117 @@ +#ifndef INTERACTION_H +#define INTERACTION_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +#define INTERACT_HOOT /* 0x00000001 */ (1 << 0) +#define INTERACT_GRABBABLE /* 0x00000002 */ (1 << 1) +#define INTERACT_DOOR /* 0x00000004 */ (1 << 2) +#define INTERACT_DAMAGE /* 0x00000008 */ (1 << 3) +#define INTERACT_COIN /* 0x00000010 */ (1 << 4) +#define INTERACT_CAP /* 0x00000020 */ (1 << 5) +#define INTERACT_POLE /* 0x00000040 */ (1 << 6) +#define INTERACT_KOOPA /* 0x00000080 */ (1 << 7) +#define INTERACT_UNKNOWN_08 /* 0x00000100 */ (1 << 8) +#define INTERACT_BREAKABLE /* 0x00000200 */ (1 << 9) +#define INTERACT_STRONG_WIND /* 0x00000400 */ (1 << 10) +#define INTERACT_WARP_DOOR /* 0x00000800 */ (1 << 11) +#define INTERACT_STAR_OR_KEY /* 0x00001000 */ (1 << 12) +#define INTERACT_WARP /* 0x00002000 */ (1 << 13) +#define INTERACT_CANNON_BASE /* 0x00004000 */ (1 << 14) +#define INTERACT_BOUNCE_TOP /* 0x00008000 */ (1 << 15) +#define INTERACT_WATER_RING /* 0x00010000 */ (1 << 16) +#define INTERACT_BULLY /* 0x00020000 */ (1 << 17) +#define INTERACT_FLAME /* 0x00040000 */ (1 << 18) +#define INTERACT_KOOPA_SHELL /* 0x00080000 */ (1 << 19) +#define INTERACT_BOUNCE_TOP2 /* 0x00100000 */ (1 << 20) +#define INTERACT_MR_BLIZZARD /* 0x00200000 */ (1 << 21) +#define INTERACT_HIT_FROM_BELOW /* 0x00400000 */ (1 << 22) +#define INTERACT_TEXT /* 0x00800000 */ (1 << 23) +#define INTERACT_TORNADO /* 0x01000000 */ (1 << 24) +#define INTERACT_WHIRLPOOL /* 0x02000000 */ (1 << 25) +#define INTERACT_CLAM_OR_BUBBA /* 0x04000000 */ (1 << 26) +#define INTERACT_BBH_ENTRANCE /* 0x08000000 */ (1 << 27) +#define INTERACT_SNUFIT_BULLET /* 0x10000000 */ (1 << 28) +#define INTERACT_SHOCK /* 0x20000000 */ (1 << 29) +#define INTERACT_IGLOO_BARRIER /* 0x40000000 */ (1 << 30) +#define INTERACT_UNKNOWN_31 /* 0x80000000 */ (1 << 31) + + +// INTERACT_WARP +#define INT_SUBTYPE_FADING_WARP 0x00000001 + +// Damaging interactions +#define INT_SUBTYPE_DELAY_INVINCIBILITY 0x00000002 +#define INT_SUBTYPE_BIG_KNOCKBACK 0x00000008 /* Used by Bowser, sets Mario's forward velocity to 40 on hit */ + +// INTERACT_GRABBABLE +#define INT_SUBTYPE_GRABS_MARIO 0x00000004 /* Also makes the object heavy */ +#define INT_SUBTYPE_HOLDABLE_NPC 0x00000010 /* Allows the object to be gently dropped, and sets vertical speed to 0 when dropped with no forwards velocity */ +#define INT_SUBTYPE_DROP_IMMEDIATELY 0x00000040 /* This gets set by grabbable NPCs that talk to Mario to make him drop them after the dialog is finished */ +#define INT_SUBTYPE_KICKABLE 0x00000100 +#define INT_SUBTYPE_NOT_GRABBABLE 0x00000200 /* Used by Heavy-Ho to allow it to throw Mario, without Mario being able to pick it up */ + +// INTERACT_DOOR +#define INT_SUBTYPE_STAR_DOOR 0x00000020 + +//INTERACT_BOUNCE_TOP +#define INT_SUBTYPE_TWIRL_BOUNCE 0x00000080 + +// INTERACT_STAR_OR_KEY +#define INT_SUBTYPE_NO_EXIT 0x00000400 +#define INT_SUBTYPE_GRAND_STAR 0x00000800 + +// INTERACT_TEXT +#define INT_SUBTYPE_SIGN 0x00001000 +#define INT_SUBTYPE_NPC 0x00004000 + +// INTERACT_CLAM_OR_BUBBA +#define INT_SUBTYPE_EATS_MARIO 0x00002000 + + +#define ATTACK_PUNCH 1 +#define ATTACK_KICK_OR_TRIP 2 +#define ATTACK_FROM_ABOVE 3 +#define ATTACK_GROUND_POUND_OR_TWIRL 4 +#define ATTACK_FAST_ATTACK 5 +#define ATTACK_FROM_BELOW 6 + +#define INT_STATUS_ATTACK_MASK 0x000000FF + +#define INT_STATUS_HOOT_GRABBED_BY_MARIO (1 << 0) /* 0x00000001 */ +#define INT_STATUS_MARIO_UNK1 (1 << 1) /* 0x00000002 */ +#define INT_STATUS_MARIO_UNK2 (1 << 2) /* 0x00000004 */ +#define INT_STATUS_MARIO_DROP_OBJECT (1 << 3) /* 0x00000008 */ +#define INT_STATUS_HIT_BY_SHOCKWAVE (1 << 4) /* 0x00000010 */ +#define INT_STATUS_MARIO_UNK5 (1 << 5) /* 0x00000020 */ +#define INT_STATUS_MARIO_UNK6 (1 << 6) /* 0x00000040 */ +#define INT_STATUS_MARIO_UNK7 (1 << 7) /* 0x00000080 */ +#define INT_STATUS_GRABBED_MARIO (1 << 11) /* 0x00000800 */ +#define INT_STATUS_ATTACKED_MARIO (1 << 13) /* 0x00002000 */ +#define INT_STATUS_WAS_ATTACKED (1 << 14) /* 0x00004000 */ +#define INT_STATUS_INTERACTED (1 << 15) /* 0x00008000 */ +#define INT_STATUS_TRAP_TURN (1 << 20) /* 0x00100000 */ +#define INT_STATUS_HIT_MINE (1 << 21) /* 0x00200000 */ +#define INT_STATUS_STOP_RIDING (1 << 22) /* 0x00400000 */ +#define INT_STATUS_TOUCHED_BOB_OMB (1 << 23) /* 0x00800000 */ + + +s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o); +void mario_stop_riding_object(struct MarioState *m); +void mario_grab_used_object(struct MarioState *m); +void mario_drop_held_object(struct MarioState *m); +void mario_throw_held_object(struct MarioState *m); +void mario_stop_riding_and_holding(struct MarioState *m); +u32 does_mario_have_normal_cap_on_head(struct MarioState *m); +void mario_blow_off_cap(struct MarioState *m, f32 capSpeed); +u32 mario_lose_cap_to_enemy(u32 arg); +void mario_retrieve_cap(void); +struct Object *mario_get_collided_object(struct MarioState *m, u32 interactType); +u32 mario_check_object_grab(struct MarioState *m); +u32 get_door_save_file_flag(struct Object *door); +void mario_process_interactions(struct MarioState *m); +void mario_handle_special_floors(struct MarioState *m); + +#endif // INTERACTION_H diff --git a/src/game/level_update.h b/src/game/level_update.h new file mode 100644 index 0000000..9d17aa0 --- /dev/null +++ b/src/game/level_update.h @@ -0,0 +1,132 @@ +#ifndef LEVEL_UPDATE_H +#define LEVEL_UPDATE_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + + +#define TIMER_CONTROL_SHOW 0 +#define TIMER_CONTROL_START 1 +#define TIMER_CONTROL_STOP 2 +#define TIMER_CONTROL_HIDE 3 + +#define WARP_OP_NONE 0x00 +#define WARP_OP_UNKNOWN_01 0x01 +#define WARP_OP_UNKNOWN_02 0x02 +#define WARP_OP_WARP_DOOR 0x03 +#define WARP_OP_WARP_OBJECT 0x04 +#define WARP_OP_TELEPORT 0x05 +#define WARP_OP_STAR_EXIT 0x11 +#define WARP_OP_DEATH 0x12 +#define WARP_OP_WARP_FLOOR 0x13 +#define WARP_OP_GAME_OVER 0x14 +#define WARP_OP_CREDITS_END 0x15 +#define WARP_OP_DEMO_NEXT 0x16 +#define WARP_OP_CREDITS_START 0x17 +#define WARP_OP_CREDITS_NEXT 0x18 +#define WARP_OP_DEMO_END 0x19 + +#define WARP_OP_TRIGGERS_LEVEL_SELECT 0x10 + +#define MARIO_SPAWN_DOOR_WARP 0x01 +#define MARIO_SPAWN_UNKNOWN_02 0x02 +#define MARIO_SPAWN_UNKNOWN_03 0x03 +#define MARIO_SPAWN_TELEPORT 0x04 +#define MARIO_SPAWN_INSTANT_ACTIVE 0x10 +#define MARIO_SPAWN_SWIMMING 0x11 +#define MARIO_SPAWN_AIRBORNE 0x12 +#define MARIO_SPAWN_HARD_AIR_KNOCKBACK 0x13 +#define MARIO_SPAWN_SPIN_AIRBORNE_CIRCLE 0x14 +#define MARIO_SPAWN_DEATH 0x15 +#define MARIO_SPAWN_SPIN_AIRBORNE 0x16 +#define MARIO_SPAWN_FLYING 0x17 +#define MARIO_SPAWN_PAINTING_STAR_COLLECT 0x20 +#define MARIO_SPAWN_PAINTING_DEATH 0x21 +#define MARIO_SPAWN_AIRBORNE_STAR_COLLECT 0x22 +#define MARIO_SPAWN_AIRBORNE_DEATH 0x23 +#define MARIO_SPAWN_LAUNCH_STAR_COLLECT 0x24 +#define MARIO_SPAWN_LAUNCH_DEATH 0x25 +#define MARIO_SPAWN_UNKNOWN_27 0x27 + + +// struct CreditsEntry +// { +// /*0x00*/ u8 levelNum; +// /*0x01*/ u8 areaIndex; +// /*0x02*/ u8 unk02; +// /*0x03*/ s8 marioAngle; +// /*0x04*/ Vec3s marioPos; +// /*0x0C*/ const char **unk0C; +// }; +// +// extern struct CreditsEntry *gCurrCreditsEntry; +// +// extern struct MarioState gMarioStates[]; +// extern struct MarioState *gMarioState; +// +// extern s16 sCurrPlayMode; +// extern u16 D_80339ECA; +// extern s16 sTransitionTimer; +// extern void (*sTransitionUpdate)(s16 *); +// extern u8 unused3[4]; +// +// struct WarpDest { +// u8 type; +// u8 levelNum; +// u8 areaIdx; +// u8 nodeId; +// u32 arg; +// }; +// +// extern struct WarpDest sWarpDest; +// +// extern s16 D_80339EE0; +// extern s16 sDelayedWarpOp; +// extern s16 sDelayedWarpTimer; +// extern s16 sSourceWarpNodeId; +// extern s32 sDelayedWarpArg; +// extern u8 unused4[2]; +// extern s8 sTimerRunning; + +struct HudDisplay { + /*0x00*/ s16 lives; + /*0x02*/ s16 coins; + /*0x04*/ s16 stars; + /*0x06*/ s16 wedges; + /*0x08*/ s16 keys; + /*0x0A*/ s16 flags; + /*0x0C*/ u16 timer; +}; + +// extern struct HudDisplay gHudDisplay; +// extern s8 gNeverEnteredCastle; + +enum HUDDisplayFlag { + HUD_DISPLAY_FLAG_LIVES = 0x0001, + HUD_DISPLAY_FLAG_COIN_COUNT = 0x0002, + HUD_DISPLAY_FLAG_STAR_COUNT = 0x0004, + HUD_DISPLAY_FLAG_CAMERA_AND_POWER = 0x0008, + HUD_DISPLAY_FLAG_KEYS = 0x0010, + HUD_DISPLAY_FLAG_UNKNOWN_0020 = 0x0020, + HUD_DISPLAY_FLAG_TIMER = 0x0040, + HUD_DISPLAY_FLAG_EMPHASIZE_POWER = 0x8000, + + HUD_DISPLAY_NONE = 0x0000, + HUD_DISPLAY_DEFAULT = HUD_DISPLAY_FLAG_LIVES | HUD_DISPLAY_FLAG_COIN_COUNT | HUD_DISPLAY_FLAG_STAR_COUNT | HUD_DISPLAY_FLAG_CAMERA_AND_POWER | HUD_DISPLAY_FLAG_KEYS | HUD_DISPLAY_FLAG_UNKNOWN_0020 +}; +// +// +// u16 level_control_timer(s32 timerOp); +// void fade_into_special_warp(u32 arg, u32 color); +// void load_level_init_text(u32 arg); +// s16 level_trigger_warp(struct MarioState *m, s32 warpOp); +// void level_set_transition(s16 length, void (*updateFunction)(s16 *)); +// +// s32 lvl_init_or_update(s16 initOrUpdate, UNUSED s32 unused); +// s32 lvl_init_from_save_file(UNUSED s16 arg0, s32 levelNum); +// s32 lvl_set_current_level(UNUSED s16 arg0, s32 levelNum); +// s32 lvl_play_the_end_screen_sound(UNUSED s16 arg0, UNUSED s32 arg1); +// void basic_update(UNUSED s16 *arg); + +#endif // LEVEL_UPDATE_H diff --git a/src/game/mario.c b/src/game/mario.c new file mode 100644 index 0000000..615347d --- /dev/null +++ b/src/game/mario.c @@ -0,0 +1,1903 @@ +#include +#include + +#include "../shim.h" +#include "../include/PR/os_cont.h" +#include "../include/mario_geo_switch_case_ids.h" +#include "../include/sm64.h" + +#include "area.h" +// #include "audio/external.h" +// #include "behavior_actions.h" +// #include "behavior_data.h" +#include "camera.h" +#include "../engine/graph_node.h" +#include "../engine/math_util.h" +#include "../engine/surface_collision.h" +// #include "game_init.h" +#include "interaction.h" +// #include "level_table.h" +#include "level_update.h" +// #include "main.h" +#include "mario.h" +#include "mario_actions_airborne.h" +#include "mario_actions_automatic.h" +#include "mario_actions_cutscene.h" +#include "mario_actions_moving.h" +#include "mario_actions_object.h" +#include "mario_actions_stationary.h" +#include "mario_actions_submerged.h" +#include "mario_misc.h" +#include "mario_step.h" +// #include "memory.h" +#include "../include/object_fields.h" +// #include "object_helpers.h" +// #include "object_list_processor.h" +// #include "print.h" +#include "save_file.h" +// #include "sound_init.h" +// #include "thread6.h" + + +u32 unused80339F10; +s8 filler80339F1C[20]; + +/************************************************** + * ANIMATIONS * + **************************************************/ + +/** + * Checks if Mario's animation has reached its end point. + */ +s32 is_anim_at_end(struct MarioState *m) { + struct Object *o = m->marioObj; + + return (o->header.gfx.animInfo.animFrame + 1) == o->header.gfx.animInfo.curAnim->loopEnd; +} + +/** + * Checks if Mario's animation has surpassed 2 frames before its end point. + */ +s32 is_anim_past_end(struct MarioState *m) { + struct Object *o = m->marioObj; + + return o->header.gfx.animInfo.animFrame >= (o->header.gfx.animInfo.curAnim->loopEnd - 2); +} + +/** + * Sets Mario's animation without any acceleration, running at its default rate. + */ +s16 set_mario_animation(struct MarioState *m, s32 targetAnimID) { + struct Object *o = m->marioObj; + struct Animation *targetAnim = m->animation->targetAnim; + + hack_load_mario_animation(m->animation, targetAnimID); + //if (load_patchable_table(m->animation, targetAnimID)) { + // targetAnim->values = (void *) VIRTUAL_TO_PHYSICAL((u8 *) targetAnim + (uintptr_t) targetAnim->values); + // targetAnim->index = (void *) VIRTUAL_TO_PHYSICAL((u8 *) targetAnim + (uintptr_t) targetAnim->index); + //} + + if (o->header.gfx.animInfo.animID != targetAnimID) { + o->header.gfx.animInfo.animID = targetAnimID; + o->header.gfx.animInfo.curAnim = targetAnim; + o->header.gfx.animInfo.animAccel = 0; + o->header.gfx.animInfo.animYTrans = m->unkB0; + + if (targetAnim->flags & ANIM_FLAG_2) { + o->header.gfx.animInfo.animFrame = targetAnim->startFrame; + } else { + if (targetAnim->flags & ANIM_FLAG_FORWARD) { + o->header.gfx.animInfo.animFrame = targetAnim->startFrame + 1; + } else { + o->header.gfx.animInfo.animFrame = targetAnim->startFrame - 1; + } + } + } + + return o->header.gfx.animInfo.animFrame; +} + +/** + * Sets Mario's animation where the animation is sped up or + * slowed down via acceleration. + */ +s16 set_mario_anim_with_accel(struct MarioState *m, s32 targetAnimID, s32 accel) { + struct Object *o = m->marioObj; + struct Animation *targetAnim = m->animation->targetAnim; + + hack_load_mario_animation(m->animation, targetAnimID); + //if (load_patchable_table(m->animation, targetAnimID)) { + // targetAnim->values = (void *) VIRTUAL_TO_PHYSICAL((u8 *) targetAnim + (uintptr_t) targetAnim->values); + // targetAnim->index = (void *) VIRTUAL_TO_PHYSICAL((u8 *) targetAnim + (uintptr_t) targetAnim->index); + //} + + if (o->header.gfx.animInfo.animID != targetAnimID) { + o->header.gfx.animInfo.animID = targetAnimID; + o->header.gfx.animInfo.curAnim = targetAnim; + o->header.gfx.animInfo.animYTrans = m->unkB0; + + if (targetAnim->flags & ANIM_FLAG_2) { + o->header.gfx.animInfo.animFrameAccelAssist = (targetAnim->startFrame << 0x10); + } else { + if (targetAnim->flags & ANIM_FLAG_FORWARD) { + o->header.gfx.animInfo.animFrameAccelAssist = (targetAnim->startFrame << 0x10) + accel; + } else { + o->header.gfx.animInfo.animFrameAccelAssist = (targetAnim->startFrame << 0x10) - accel; + } + } + + o->header.gfx.animInfo.animFrame = (o->header.gfx.animInfo.animFrameAccelAssist >> 0x10); + } + + o->header.gfx.animInfo.animAccel = accel; + + return o->header.gfx.animInfo.animFrame; +} + +/** + * Sets the animation to a specific "next" frame from the frame given. + */ +void set_anim_to_frame(struct MarioState *m, s16 animFrame) { + struct AnimInfo *animInfo = &m->marioObj->header.gfx.animInfo; + struct Animation *curAnim = animInfo->curAnim; + + if (animInfo->animAccel) { + if (curAnim->flags & ANIM_FLAG_FORWARD) { + animInfo->animFrameAccelAssist = (animFrame << 0x10) + animInfo->animAccel; + } else { + animInfo->animFrameAccelAssist = (animFrame << 0x10) - animInfo->animAccel; + } + } else { + if (curAnim->flags & ANIM_FLAG_FORWARD) { + animInfo->animFrame = animFrame + 1; + } else { + animInfo->animFrame = animFrame - 1; + } + } +} + +s32 is_anim_past_frame(struct MarioState *m, s16 animFrame) { + s32 isPastFrame; + s32 acceleratedFrame = animFrame << 0x10; + struct AnimInfo *animInfo = &m->marioObj->header.gfx.animInfo; + struct Animation *curAnim = animInfo->curAnim; + + if (animInfo->animAccel) { + if (curAnim->flags & ANIM_FLAG_FORWARD) { + isPastFrame = + (animInfo->animFrameAccelAssist > acceleratedFrame) + && (acceleratedFrame >= (animInfo->animFrameAccelAssist - animInfo->animAccel)); + } else { + isPastFrame = + (animInfo->animFrameAccelAssist < acceleratedFrame) + && (acceleratedFrame <= (animInfo->animFrameAccelAssist + animInfo->animAccel)); + } + } else { + if (curAnim->flags & ANIM_FLAG_FORWARD) { + isPastFrame = (animInfo->animFrame == (animFrame + 1)); + } else { + isPastFrame = ((animInfo->animFrame + 1) == animFrame); + } + } + + return isPastFrame; +} + +/** + * Rotates the animation's translation into the global coordinate system + * and returns the animation's flags. + */ +s16 find_mario_anim_flags_and_translation(struct Object *obj, s32 yaw, Vec3s translation) { + f32 dx; + f32 dz; + + struct Animation *curAnim = (void *) obj->header.gfx.animInfo.curAnim; + s16 animFrame = geo_update_animation_frame(&obj->header.gfx.animInfo, NULL); + u16 *animIndex = segmented_to_virtual((void *) curAnim->index); + s16 *animValues = segmented_to_virtual((void *) curAnim->values); + + f32 s = (f32) sins(yaw); + f32 c = (f32) coss(yaw); + + dx = *(animValues + (retrieve_animation_index(animFrame, &animIndex))) / 4.0f; + translation[1] = *(animValues + (retrieve_animation_index(animFrame, &animIndex))) / 4.0f; + dz = *(animValues + (retrieve_animation_index(animFrame, &animIndex))) / 4.0f; + + translation[0] = (dx * c) + (dz * s); + translation[2] = (-dx * s) + (dz * c); + + return curAnim->flags; +} + +/** + * Updates Mario's position from his animation's translation. + */ +void update_mario_pos_for_anim(struct MarioState *m) { + Vec3s translation; + s16 flags; + + flags = find_mario_anim_flags_and_translation(m->marioObj, m->faceAngle[1], translation); + + if (flags & (ANIM_FLAG_HOR_TRANS | ANIM_FLAG_6)) { + m->pos[0] += (f32) translation[0]; + m->pos[2] += (f32) translation[2]; + } + + if (flags & (ANIM_FLAG_VERT_TRANS | ANIM_FLAG_6)) { + m->pos[1] += (f32) translation[1]; + } +} + +/** + * Finds the vertical translation from Mario's animation. + */ +s16 return_mario_anim_y_translation(struct MarioState *m) { + Vec3s translation; + find_mario_anim_flags_and_translation(m->marioObj, 0, translation); + + return translation[1]; +} + +/************************************************** + * AUDIO * + **************************************************/ + +/** + * Plays a sound if if Mario doesn't have the flag being checked. + */ +void play_sound_if_no_flag(struct MarioState *m, u32 soundBits, u32 flags) { + if (!(m->flags & flags)) { + play_sound(soundBits, m->marioObj->header.gfx.cameraToObject); + m->flags |= flags; + } +} + +/** + * Plays a jump sound if one has not been played since the last action change. + */ +void play_mario_jump_sound(struct MarioState *m) { + if (!(m->flags & MARIO_MARIO_SOUND_PLAYED)) { +#ifndef VERSION_JP + if (m->action == ACT_TRIPLE_JUMP) { + play_sound(SOUND_MARIO_YAHOO_WAHA_YIPPEE + ((gAudioRandom % 5) << 16), + m->marioObj->header.gfx.cameraToObject); + } else { +#endif + play_sound(SOUND_MARIO_YAH_WAH_HOO + ((gAudioRandom % 3) << 16), + m->marioObj->header.gfx.cameraToObject); +#ifndef VERSION_JP + } +#endif + m->flags |= MARIO_MARIO_SOUND_PLAYED; + } +} + +/** + * Adjusts the volume/pitch of sounds from Mario's speed. + */ +void adjust_sound_for_speed(struct MarioState *m) { + s32 absForwardVel = (m->forwardVel > 0.0f) ? m->forwardVel : -m->forwardVel; + func_80320A4C(1, (absForwardVel > 100) ? 100 : absForwardVel); +} + +/** + * Spawns particles if the step sound says to, then either plays a step sound or relevant other sound. + */ +void play_sound_and_spawn_particles(struct MarioState *m, u32 soundBits, u32 waveParticleType) { + if (m->terrainSoundAddend == (SOUND_TERRAIN_WATER << 16)) { + if (waveParticleType != 0) { + m->particleFlags |= PARTICLE_SHALLOW_WATER_SPLASH; + } else { + m->particleFlags |= PARTICLE_SHALLOW_WATER_WAVE; + } + } else { + if (m->terrainSoundAddend == (SOUND_TERRAIN_SAND << 16)) { + m->particleFlags |= PARTICLE_DIRT; + } else if (m->terrainSoundAddend == (SOUND_TERRAIN_SNOW << 16)) { + m->particleFlags |= PARTICLE_SNOW; + } + } + + if ((m->flags & MARIO_METAL_CAP) || soundBits == SOUND_ACTION_UNSTUCK_FROM_GROUND + || soundBits == SOUND_MARIO_PUNCH_HOO) { + play_sound(soundBits, m->marioObj->header.gfx.cameraToObject); + } else { + play_sound(m->terrainSoundAddend + soundBits, m->marioObj->header.gfx.cameraToObject); + } +} + +/** + * Plays an environmental sound if one has not been played since the last action change. + */ +void play_mario_action_sound(struct MarioState *m, u32 soundBits, u32 waveParticleType) { + if (!(m->flags & MARIO_ACTION_SOUND_PLAYED)) { + play_sound_and_spawn_particles(m, soundBits, waveParticleType); + m->flags |= MARIO_ACTION_SOUND_PLAYED; + } +} + +/** + * Plays a landing sound, accounting for metal cap. + */ +void play_mario_landing_sound(struct MarioState *m, u32 soundBits) { + play_sound_and_spawn_particles( + m, (m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_LANDING : soundBits, 1); +} + +/** + * Plays a landing sound, accounting for metal cap. Unlike play_mario_landing_sound, + * this function uses play_mario_action_sound, making sure the sound is only + * played once per action. + */ +void play_mario_landing_sound_once(struct MarioState *m, u32 soundBits) { + play_mario_action_sound( + m, (m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_LANDING : soundBits, 1); +} + +/** + * Plays a heavy landing (ground pound, etc.) sound, accounting for metal cap. + */ +void play_mario_heavy_landing_sound(struct MarioState *m, u32 soundBits) { + play_sound_and_spawn_particles( + m, (m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_HEAVY_LANDING : soundBits, 1); +} + +/** + * Plays a heavy landing (ground pound, etc.) sound, accounting for metal cap. + * Unlike play_mario_heavy_landing_sound, this function uses play_mario_action_sound, + * making sure the sound is only played once per action. + */ +void play_mario_heavy_landing_sound_once(struct MarioState *m, u32 soundBits) { + play_mario_action_sound( + m, (m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_HEAVY_LANDING : soundBits, 1); +} + +/** + * Plays action and Mario sounds relevant to what was passed into the function. + */ +void play_mario_sound(struct MarioState *m, s32 actionSound, s32 marioSound) { + if (actionSound == SOUND_ACTION_TERRAIN_JUMP) { + play_mario_action_sound(m, (m->flags & MARIO_METAL_CAP) ? (s32) SOUND_ACTION_METAL_JUMP + : (s32) SOUND_ACTION_TERRAIN_JUMP, 1); + } else { + play_sound_if_no_flag(m, actionSound, MARIO_ACTION_SOUND_PLAYED); + } + + if (marioSound == 0) { + play_mario_jump_sound(m); + } + + if (marioSound != -1) { + play_sound_if_no_flag(m, marioSound, MARIO_MARIO_SOUND_PLAYED); + } +} + +/************************************************** + * ACTIONS * + **************************************************/ + +/** + * Sets Mario's other velocities from his forward speed. + */ +void mario_set_forward_vel(struct MarioState *m, f32 forwardVel) { + m->forwardVel = forwardVel; + + m->slideVelX = sins(m->faceAngle[1]) * m->forwardVel; + m->slideVelZ = coss(m->faceAngle[1]) * m->forwardVel; + + m->vel[0] = (f32) m->slideVelX; + m->vel[2] = (f32) m->slideVelZ; +} + +/** + * Returns the slipperiness class of Mario's floor. + */ +s32 mario_get_floor_class(struct MarioState *m) { + s32 floorClass; + + // The slide terrain type defaults to slide slipperiness. + // This doesn't matter too much since normally the slide terrain + // is checked for anyways. + if ((m->area->terrainType & TERRAIN_MASK) == TERRAIN_SLIDE) { + floorClass = SURFACE_CLASS_VERY_SLIPPERY; + } else { + floorClass = SURFACE_CLASS_DEFAULT; + } + + if (m->floor != NULL) { + switch (m->floor->type) { + case SURFACE_NOT_SLIPPERY: + case SURFACE_HARD_NOT_SLIPPERY: + case SURFACE_SWITCH: + floorClass = SURFACE_CLASS_NOT_SLIPPERY; + break; + + case SURFACE_SLIPPERY: + case SURFACE_NOISE_SLIPPERY: + case SURFACE_HARD_SLIPPERY: + case SURFACE_NO_CAM_COL_SLIPPERY: + floorClass = SURFACE_CLASS_SLIPPERY; + break; + + case SURFACE_VERY_SLIPPERY: + case SURFACE_ICE: + case SURFACE_HARD_VERY_SLIPPERY: + case SURFACE_NOISE_VERY_SLIPPERY_73: + case SURFACE_NOISE_VERY_SLIPPERY_74: + case SURFACE_NOISE_VERY_SLIPPERY: + case SURFACE_NO_CAM_COL_VERY_SLIPPERY: + floorClass = SURFACE_CLASS_VERY_SLIPPERY; + break; + } + } + + // Crawling allows Mario to not slide on certain steeper surfaces. + if (m->action == ACT_CRAWLING && m->floor->normal.y > 0.5f && floorClass == SURFACE_CLASS_DEFAULT) { + floorClass = SURFACE_CLASS_NOT_SLIPPERY; + } + + return floorClass; +} + +// clang-format off +s8 sTerrainSounds[7][6] = { + // default, hard, slippery, + // very slippery, noisy default, noisy slippery + { SOUND_TERRAIN_DEFAULT, SOUND_TERRAIN_STONE, SOUND_TERRAIN_GRASS, + SOUND_TERRAIN_GRASS, SOUND_TERRAIN_GRASS, SOUND_TERRAIN_DEFAULT }, // TERRAIN_GRASS + { SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE, + SOUND_TERRAIN_STONE, SOUND_TERRAIN_GRASS, SOUND_TERRAIN_GRASS }, // TERRAIN_STONE + { SOUND_TERRAIN_SNOW, SOUND_TERRAIN_ICE, SOUND_TERRAIN_SNOW, + SOUND_TERRAIN_ICE, SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE }, // TERRAIN_SNOW + { SOUND_TERRAIN_SAND, SOUND_TERRAIN_STONE, SOUND_TERRAIN_SAND, + SOUND_TERRAIN_SAND, SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE }, // TERRAIN_SAND + { SOUND_TERRAIN_SPOOKY, SOUND_TERRAIN_SPOOKY, SOUND_TERRAIN_SPOOKY, + SOUND_TERRAIN_SPOOKY, SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE }, // TERRAIN_SPOOKY + { SOUND_TERRAIN_DEFAULT, SOUND_TERRAIN_STONE, SOUND_TERRAIN_GRASS, + SOUND_TERRAIN_ICE, SOUND_TERRAIN_STONE, SOUND_TERRAIN_ICE }, // TERRAIN_WATER + { SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE, SOUND_TERRAIN_STONE, + SOUND_TERRAIN_STONE, SOUND_TERRAIN_ICE, SOUND_TERRAIN_ICE }, // TERRAIN_SLIDE +}; +// clang-format on + +/** + * Computes a value that should be added to terrain sounds before playing them. + * This depends on surfaces and terrain. + */ +u32 mario_get_terrain_sound_addend(struct MarioState *m) { + s16 floorSoundType; + s16 terrainType = m->area->terrainType & TERRAIN_MASK; + s32 ret = SOUND_TERRAIN_DEFAULT << 16; + s32 floorType; + + if (m->floor != NULL) { + floorType = m->floor->type; + + if ((gCurrLevelNum != LEVEL_LLL) && (m->floorHeight < (m->waterLevel - 10))) { + // Water terrain sound, excluding LLL since it uses water in the volcano. + ret = SOUND_TERRAIN_WATER << 16; + } else if (SURFACE_IS_QUICKSAND(floorType)) { + ret = SOUND_TERRAIN_SAND << 16; + } else { + switch (floorType) { + default: + floorSoundType = 0; + break; + + case SURFACE_NOT_SLIPPERY: + case SURFACE_HARD: + case SURFACE_HARD_NOT_SLIPPERY: + case SURFACE_SWITCH: + floorSoundType = 1; + break; + + case SURFACE_SLIPPERY: + case SURFACE_HARD_SLIPPERY: + case SURFACE_NO_CAM_COL_SLIPPERY: + floorSoundType = 2; + break; + + case SURFACE_VERY_SLIPPERY: + case SURFACE_ICE: + case SURFACE_HARD_VERY_SLIPPERY: + case SURFACE_NOISE_VERY_SLIPPERY_73: + case SURFACE_NOISE_VERY_SLIPPERY_74: + case SURFACE_NOISE_VERY_SLIPPERY: + case SURFACE_NO_CAM_COL_VERY_SLIPPERY: + floorSoundType = 3; + break; + + case SURFACE_NOISE_DEFAULT: + floorSoundType = 4; + break; + + case SURFACE_NOISE_SLIPPERY: + floorSoundType = 5; + break; + } + + ret = sTerrainSounds[terrainType][floorSoundType] << 16; + } + } + + return ret; +} + +/** + * Collides with walls and returns the most recent wall. + */ +struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius) { + struct WallCollisionData collisionData; + struct Surface *wall = NULL; + + collisionData.x = pos[0]; + collisionData.y = pos[1]; + collisionData.z = pos[2]; + collisionData.radius = radius; + collisionData.offsetY = offset; + + if (find_wall_collisions(&collisionData)) { + wall = collisionData.walls[collisionData.numWalls - 1]; + } + + pos[0] = collisionData.x; + pos[1] = collisionData.y; + pos[2] = collisionData.z; + + // This only returns the most recent wall and can also return NULL + // there are no wall collisions. + return wall; +} + +/** + * Finds the ceiling from a vec3f horizontally and a height (with 80 vertical buffer). + */ +f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil) { + UNUSED f32 unused; + + return find_ceil(pos[0], height + 80.0f, pos[2], ceil); +} + +/** + * Determines if Mario is facing "downhill." + */ +s32 mario_facing_downhill(struct MarioState *m, s32 turnYaw) { + s16 faceAngleYaw = m->faceAngle[1]; + + // This is never used in practice, as turnYaw is + // always passed as zero. + if (turnYaw && m->forwardVel < 0.0f) { + faceAngleYaw += 0x8000; + } + + faceAngleYaw = m->floorAngle - faceAngleYaw; + + return (-0x4000 < faceAngleYaw) && (faceAngleYaw < 0x4000); +} + +/** + * Determines if a surface is slippery based on the surface class. + */ +u32 mario_floor_is_slippery(struct MarioState *m) { + f32 normY; + + if ((m->area->terrainType & TERRAIN_MASK) == TERRAIN_SLIDE + && m->floor->normal.y < 0.9998477f //~cos(1 deg) + ) { + return TRUE; + } + + switch (mario_get_floor_class(m)) { + case SURFACE_VERY_SLIPPERY: + normY = 0.9848077f; //~cos(10 deg) + break; + + case SURFACE_SLIPPERY: + normY = 0.9396926f; //~cos(20 deg) + break; + + default: + normY = 0.7880108f; //~cos(38 deg) + break; + + case SURFACE_NOT_SLIPPERY: + normY = 0.0f; + break; + } + + return m->floor->normal.y <= normY; +} + +/** + * Determines if a surface is a slope based on the surface class. + */ +s32 mario_floor_is_slope(struct MarioState *m) { + f32 normY; + + if ((m->area->terrainType & TERRAIN_MASK) == TERRAIN_SLIDE + && m->floor->normal.y < 0.9998477f) { // ~cos(1 deg) + return TRUE; + } + + switch (mario_get_floor_class(m)) { + case SURFACE_VERY_SLIPPERY: + normY = 0.9961947f; // ~cos(5 deg) + break; + + case SURFACE_SLIPPERY: + normY = 0.9848077f; // ~cos(10 deg) + break; + + default: + normY = 0.9659258f; // ~cos(15 deg) + break; + + case SURFACE_NOT_SLIPPERY: + normY = 0.9396926f; // ~cos(20 deg) + break; + } + + return m->floor->normal.y <= normY; +} + +/** + * Determines if a surface is steep based on the surface class. + */ +s32 mario_floor_is_steep(struct MarioState *m) { + f32 normY; + s32 result = FALSE; + + // Interestingly, this function does not check for the + // slide terrain type. This means that steep behavior persists for + // non-slippery and slippery surfaces. + // This does not matter in vanilla game practice. + if (!mario_facing_downhill(m, FALSE)) { + switch (mario_get_floor_class(m)) { + case SURFACE_VERY_SLIPPERY: + normY = 0.9659258f; // ~cos(15 deg) + break; + + case SURFACE_SLIPPERY: + normY = 0.9396926f; // ~cos(20 deg) + break; + + default: + normY = 0.8660254f; // ~cos(30 deg) + break; + + case SURFACE_NOT_SLIPPERY: + normY = 0.8660254f; // ~cos(30 deg) + break; + } + + result = m->floor->normal.y <= normY; + } + + return result; +} + +/** + * Finds the floor height relative from Mario given polar displacement. + */ +f32 find_floor_height_relative_polar(struct MarioState *m, s16 angleFromMario, f32 distFromMario) { + struct Surface *floor; + f32 floorY; + + f32 y = sins(m->faceAngle[1] + angleFromMario) * distFromMario; + f32 x = coss(m->faceAngle[1] + angleFromMario) * distFromMario; + + floorY = find_floor(m->pos[0] + y, m->pos[1] + 100.0f, m->pos[2] + x, &floor); + + return floorY; +} + +/** + * Returns the slope of the floor based off points around Mario. + */ +s16 find_floor_slope(struct MarioState *m, s16 yawOffset) { + struct Surface *floor; + f32 forwardFloorY, backwardFloorY; + f32 forwardYDelta, backwardYDelta; + s16 result; + + f32 x = sins(m->faceAngle[1] + yawOffset) * 5.0f; + f32 z = coss(m->faceAngle[1] + yawOffset) * 5.0f; + + forwardFloorY = find_floor(m->pos[0] + x, m->pos[1] + 100.0f, m->pos[2] + z, &floor); + backwardFloorY = find_floor(m->pos[0] - x, m->pos[1] + 100.0f, m->pos[2] - z, &floor); + + //! If Mario is near OOB, these floorY's can sometimes be -11000. + // This will cause these to be off and give improper slopes. + forwardYDelta = forwardFloorY - m->pos[1]; + backwardYDelta = m->pos[1] - backwardFloorY; + + if (forwardYDelta * forwardYDelta < backwardYDelta * backwardYDelta) { + result = atan2s(5.0f, forwardYDelta); + } else { + result = atan2s(5.0f, backwardYDelta); + } + + return result; +} + +/** + * Adjusts Mario's camera and sound based on his action status. + */ +void update_mario_sound_and_camera(struct MarioState *m) { + u32 action = m->action; + s32 camPreset = m->area->camera->mode; + + if (action == ACT_FIRST_PERSON) { + raise_background_noise(2); + gCameraMovementFlags &= ~CAM_MOVE_C_UP_MODE; + // Go back to the last camera mode + set_camera_mode(m->area->camera, -1, 1); + } else if (action == ACT_SLEEPING) { + raise_background_noise(2); + } + + if (!(action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER))) { + if (camPreset == CAMERA_MODE_BEHIND_MARIO || camPreset == CAMERA_MODE_WATER_SURFACE) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } + } +} + +/** + * Transitions Mario to a steep jump action. + */ +void set_steep_jump_action(struct MarioState *m) { + m->marioObj->oMarioSteepJumpYaw = m->faceAngle[1]; + + if (m->forwardVel > 0.0f) { + //! ((s16)0x8000) has undefined behavior. Therefore, this downcast has + // undefined behavior if m->floorAngle >= 0. + s16 angleTemp = m->floorAngle + 0x8000; + s16 faceAngleTemp = m->faceAngle[1] - angleTemp; + + f32 y = sins(faceAngleTemp) * m->forwardVel; + f32 x = coss(faceAngleTemp) * m->forwardVel * 0.75f; + + m->forwardVel = sqrtf(y * y + x * x); + m->faceAngle[1] = atan2s(x, y) + angleTemp; + } + + drop_and_set_mario_action(m, ACT_STEEP_JUMP, 0); +} + +/** + * Sets Mario's vertical speed from his forward speed. + */ +static void set_mario_y_vel_based_on_fspeed(struct MarioState *m, f32 initialVelY, f32 multiplier) { + // get_additive_y_vel_for_jumps is always 0 and a stubbed function. + // It was likely trampoline related based on code location. + m->vel[1] = initialVelY + get_additive_y_vel_for_jumps() + m->forwardVel * multiplier; + + if (m->squishTimer != 0 || m->quicksandDepth > 1.0f) { + m->vel[1] *= 0.5f; + } +} + +/** + * Transitions for a variety of airborne actions. + */ +static u32 set_mario_action_airborne(struct MarioState *m, u32 action, u32 actionArg) { + f32 fowardVel; + + if ((m->squishTimer != 0 || m->quicksandDepth >= 1.0f) + && (action == ACT_DOUBLE_JUMP || action == ACT_TWIRLING)) { + action = ACT_JUMP; + } + + switch (action) { + case ACT_DOUBLE_JUMP: + set_mario_y_vel_based_on_fspeed(m, 52.0f, 0.25f); + m->forwardVel *= 0.8f; + break; + + case ACT_BACKFLIP: + m->marioObj->header.gfx.animInfo.animID = -1; + m->forwardVel = -16.0f; + set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); + break; + + case ACT_TRIPLE_JUMP: + set_mario_y_vel_based_on_fspeed(m, 69.0f, 0.0f); + m->forwardVel *= 0.8f; + break; + + case ACT_FLYING_TRIPLE_JUMP: + set_mario_y_vel_based_on_fspeed(m, 82.0f, 0.0f); + break; + + case ACT_WATER_JUMP: + case ACT_HOLD_WATER_JUMP: + if (actionArg == 0) { + set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.0f); + } + break; + + case ACT_BURNING_JUMP: + m->vel[1] = 31.5f; + m->forwardVel = 8.0f; + break; + + case ACT_RIDING_SHELL_JUMP: + set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.25f); + break; + + case ACT_JUMP: + case ACT_HOLD_JUMP: + m->marioObj->header.gfx.animInfo.animID = -1; + set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.25f); + m->forwardVel *= 0.8f; + break; + + case ACT_WALL_KICK_AIR: + case ACT_TOP_OF_POLE_JUMP: + set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); + if (m->forwardVel < 24.0f) { + m->forwardVel = 24.0f; + } + m->wallKickTimer = 0; + break; + + case ACT_SIDE_FLIP: + set_mario_y_vel_based_on_fspeed(m, 62.0f, 0.0f); + m->forwardVel = 8.0f; + m->faceAngle[1] = m->intendedYaw; + break; + + case ACT_STEEP_JUMP: + m->marioObj->header.gfx.animInfo.animID = -1; + set_mario_y_vel_based_on_fspeed(m, 42.0f, 0.25f); + m->faceAngle[0] = -0x2000; + break; + + case ACT_LAVA_BOOST: + m->vel[1] = 84.0f; + if (actionArg == 0) { + m->forwardVel = 0.0f; + } + break; + + case ACT_DIVE: + if ((fowardVel = m->forwardVel + 15.0f) > 48.0f) { + fowardVel = 48.0f; + } + mario_set_forward_vel(m, fowardVel); + break; + + case ACT_LONG_JUMP: + m->marioObj->header.gfx.animInfo.animID = -1; + set_mario_y_vel_based_on_fspeed(m, 30.0f, 0.0f); + m->marioObj->oMarioLongJumpIsSlow = m->forwardVel > 16.0f ? FALSE : TRUE; + + //! (BLJ's) This properly handles long jumps from getting forward speed with + // too much velocity, but misses backwards longs allowing high negative speeds. + if ((m->forwardVel *= 1.5f) > 48.0f) { + m->forwardVel = 48.0f; + } + break; + + case ACT_SLIDE_KICK: + m->vel[1] = 12.0f; + if (m->forwardVel < 32.0f) { + m->forwardVel = 32.0f; + } + break; + + case ACT_JUMP_KICK: + m->vel[1] = 20.0f; + break; + } + + m->peakHeight = m->pos[1]; + m->flags |= MARIO_UNKNOWN_08; + + return action; +} + +/** + * Transitions for a variety of moving actions. + */ +static u32 set_mario_action_moving(struct MarioState *m, u32 action, UNUSED u32 actionArg) { + s16 floorClass = mario_get_floor_class(m); + f32 forwardVel = m->forwardVel; + f32 mag = min(m->intendedMag, 8.0f); + + switch (action) { + case ACT_WALKING: + if (floorClass != SURFACE_CLASS_VERY_SLIPPERY) { + if (0.0f <= forwardVel && forwardVel < mag) { + m->forwardVel = mag; + } + } + + m->marioObj->oMarioWalkingPitch = 0; + break; + + case ACT_HOLD_WALKING: + if (0.0f <= forwardVel && forwardVel < mag / 2.0f) { + m->forwardVel = mag / 2.0f; + } + break; + + case ACT_BEGIN_SLIDING: + if (mario_facing_downhill(m, FALSE)) { + action = ACT_BUTT_SLIDE; + } else { + action = ACT_STOMACH_SLIDE; + } + break; + + case ACT_HOLD_BEGIN_SLIDING: + if (mario_facing_downhill(m, FALSE)) { + action = ACT_HOLD_BUTT_SLIDE; + } else { + action = ACT_HOLD_STOMACH_SLIDE; + } + break; + } + + return action; +} + +/** + * Transition for certain submerged actions, which is actually just the metal jump actions. + */ +static u32 set_mario_action_submerged(struct MarioState *m, u32 action, UNUSED u32 actionArg) { + if (action == ACT_METAL_WATER_JUMP || action == ACT_HOLD_METAL_WATER_JUMP) { + m->vel[1] = 32.0f; + } + + return action; +} + +/** + * Transitions for a variety of cutscene actions. + */ +static u32 set_mario_action_cutscene(struct MarioState *m, u32 action, UNUSED u32 actionArg) { + switch (action) { + case ACT_EMERGE_FROM_PIPE: + m->vel[1] = 52.0f; + break; + + case ACT_FALL_AFTER_STAR_GRAB: + mario_set_forward_vel(m, 0.0f); + break; + + case ACT_SPAWN_SPIN_AIRBORNE: + mario_set_forward_vel(m, 2.0f); + break; + + case ACT_SPECIAL_EXIT_AIRBORNE: + case ACT_SPECIAL_DEATH_EXIT: + m->vel[1] = 64.0f; + break; + } + + return action; +} + +/** + * Puts Mario into a given action, putting Mario through the appropriate + * specific function if needed. + */ +u32 set_mario_action(struct MarioState *m, u32 action, u32 actionArg) { + switch (action & ACT_GROUP_MASK) { + case ACT_GROUP_MOVING: + action = set_mario_action_moving(m, action, actionArg); + break; + + case ACT_GROUP_AIRBORNE: + action = set_mario_action_airborne(m, action, actionArg); + break; + + case ACT_GROUP_SUBMERGED: + action = set_mario_action_submerged(m, action, actionArg); + break; + + case ACT_GROUP_CUTSCENE: + action = set_mario_action_cutscene(m, action, actionArg); + break; + } + + // Resets the sound played flags, meaning Mario can play those sound types again. + m->flags &= ~(MARIO_ACTION_SOUND_PLAYED | MARIO_MARIO_SOUND_PLAYED); + + if (!(m->action & ACT_FLAG_AIR)) { + m->flags &= ~MARIO_UNKNOWN_18; + } + + // Initialize the action information. + m->prevAction = m->action; + m->action = action; + m->actionArg = actionArg; + m->actionState = 0; + m->actionTimer = 0; + + return TRUE; +} + +/** + * Puts Mario into a specific jumping action from a landing action. + */ +s32 set_jump_from_landing(struct MarioState *m) { + if (m->quicksandDepth >= 11.0f) { + if (m->heldObj == NULL) { + return set_mario_action(m, ACT_QUICKSAND_JUMP_LAND, 0); + } else { + return set_mario_action(m, ACT_HOLD_QUICKSAND_JUMP_LAND, 0); + } + } + + if (mario_floor_is_steep(m)) { + set_steep_jump_action(m); + } else { + if ((m->doubleJumpTimer == 0) || (m->squishTimer != 0)) { + set_mario_action(m, ACT_JUMP, 0); + } else { + switch (m->prevAction) { + case ACT_JUMP_LAND: + set_mario_action(m, ACT_DOUBLE_JUMP, 0); + break; + + case ACT_FREEFALL_LAND: + set_mario_action(m, ACT_DOUBLE_JUMP, 0); + break; + + case ACT_SIDE_FLIP_LAND_STOP: + set_mario_action(m, ACT_DOUBLE_JUMP, 0); + break; + + case ACT_DOUBLE_JUMP_LAND: + // If Mario has a wing cap, he ignores the typical speed + // requirement for a triple jump. + if (m->flags & MARIO_WING_CAP) { + set_mario_action(m, ACT_FLYING_TRIPLE_JUMP, 0); + } else if (m->forwardVel > 20.0f) { + set_mario_action(m, ACT_TRIPLE_JUMP, 0); + } else { + set_mario_action(m, ACT_JUMP, 0); + } + break; + + default: + set_mario_action(m, ACT_JUMP, 0); + break; + } + } + } + + m->doubleJumpTimer = 0; + + return TRUE; +} + +/** + * Puts Mario in a given action, as long as it is not overruled by + * either a quicksand or steep jump. + */ +s32 set_jumping_action(struct MarioState *m, u32 action, u32 actionArg) { + UNUSED u32 currAction = m->action; + + if (m->quicksandDepth >= 11.0f) { + // Checks whether Mario is holding an object or not. + if (m->heldObj == NULL) { + return set_mario_action(m, ACT_QUICKSAND_JUMP_LAND, 0); + } else { + return set_mario_action(m, ACT_HOLD_QUICKSAND_JUMP_LAND, 0); + } + } + + if (mario_floor_is_steep(m)) { + set_steep_jump_action(m); + } else { + set_mario_action(m, action, actionArg); + } + + return TRUE; +} + +/** + * Drop anything Mario is holding and set a new action. + */ +s32 drop_and_set_mario_action(struct MarioState *m, u32 action, u32 actionArg) { + mario_stop_riding_and_holding(m); + + return set_mario_action(m, action, actionArg); +} + +/** + * Increment Mario's hurt counter and set a new action. + */ +s32 hurt_and_set_mario_action(struct MarioState *m, u32 action, u32 actionArg, s16 hurtCounter) { + m->hurtCounter = hurtCounter; + + return set_mario_action(m, action, actionArg); +} + +/** + * Checks a variety of inputs for common transitions between many different + * actions. A common variant of the below function. + */ +s32 check_common_action_exits(struct MarioState *m) { + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_JUMP, 0); + } + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_WALKING, 0); + } + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + return FALSE; +} + +/** + * Checks a variety of inputs for common transitions between many different + * object holding actions. A holding variant of the above function. + */ +s32 check_common_hold_action_exits(struct MarioState *m) { + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_HOLD_JUMP, 0); + } + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_HOLD_FREEFALL, 0); + } + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HOLD_WALKING, 0); + } + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_HOLD_BEGIN_SLIDING, 0); + } + + return FALSE; +} + +/** + * Transitions Mario from a submerged action to a walking action. + */ +s32 transition_submerged_to_walking(struct MarioState *m) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + + vec3s_set(m->angleVel, 0, 0, 0); + + if (m->heldObj == NULL) { + return set_mario_action(m, ACT_WALKING, 0); + } else { + return set_mario_action(m, ACT_HOLD_WALKING, 0); + } +} + +/** + * This is the transition function typically for entering a submerged action for a + * non-submerged action. This also applies the water surface camera preset. + */ +s32 set_water_plunge_action(struct MarioState *m) { + m->forwardVel = m->forwardVel / 4.0f; + m->vel[1] = m->vel[1] / 2.0f; + + m->pos[1] = m->waterLevel - 100; + + m->faceAngle[2] = 0; + + vec3s_set(m->angleVel, 0, 0, 0); + + if (!(m->action & ACT_FLAG_DIVING)) { + m->faceAngle[0] = 0; + } + + if (m->area->camera->mode != CAMERA_MODE_WATER_SURFACE) { + set_camera_mode(m->area->camera, CAMERA_MODE_WATER_SURFACE, 1); + } + + return set_mario_action(m, ACT_WATER_PLUNGE, 0); +} + +/** + * These are the scaling values for the x and z axis for Mario + * when he is close to unsquishing. + */ +u8 sSquishScaleOverTime[16] = { 0x46, 0x32, 0x32, 0x3C, 0x46, 0x50, 0x50, 0x3C, + 0x28, 0x14, 0x14, 0x1E, 0x32, 0x3C, 0x3C, 0x28 }; + +/** + * Applies the squish to Mario's model via scaling. + */ +void squish_mario_model(struct MarioState *m) { + if (m->squishTimer != 0xFF) { + // If no longer squished, scale back to default. + if (m->squishTimer == 0) { + vec3f_set(m->marioObj->header.gfx.scale, 1.0f, 1.0f, 1.0f); + } + // If timer is less than 16, rubber-band Mario's size scale up and down. + else if (m->squishTimer <= 16) { + m->squishTimer -= 1; + + m->marioObj->header.gfx.scale[1] = + 1.0f - ((sSquishScaleOverTime[15 - m->squishTimer] * 0.6f) / 100.0f); + m->marioObj->header.gfx.scale[0] = + ((sSquishScaleOverTime[15 - m->squishTimer] * 0.4f) / 100.0f) + 1.0f; + + m->marioObj->header.gfx.scale[2] = m->marioObj->header.gfx.scale[0]; + } else { + m->squishTimer -= 1; + + vec3f_set(m->marioObj->header.gfx.scale, 1.4f, 0.4f, 1.4f); + } + } +} + +/** + * Debug function that prints floor normal, velocity, and action information. + */ +void debug_print_speed_action_normal(struct MarioState *m) { + f32 steepness; + f32 floor_nY; + + if (gShowDebugText) { + steepness = sqrtf( + ((m->floor->normal.x * m->floor->normal.x) + (m->floor->normal.z * m->floor->normal.z))); + floor_nY = m->floor->normal.y; + + print_text_fmt_int(210, 88, "ANG %d", (atan2s(floor_nY, steepness) * 180.0f) / 32768.0f); + + print_text_fmt_int(210, 72, "SPD %d", m->forwardVel); + + // STA short for "status," the official action name via SMS map. + print_text_fmt_int(210, 56, "STA %x", (m->action & ACT_ID_MASK)); + } +} + +/** + * Update the button inputs for Mario. + */ +void update_mario_button_inputs(struct MarioState *m) { + if (m->controller->buttonPressed & A_BUTTON) { + m->input |= INPUT_A_PRESSED; + } + + if (m->controller->buttonDown & A_BUTTON) { + m->input |= INPUT_A_DOWN; + } + + // Don't update for these buttons if squished. + if (m->squishTimer == 0) { + if (m->controller->buttonPressed & B_BUTTON) { + m->input |= INPUT_B_PRESSED; + } + + if (m->controller->buttonDown & Z_TRIG) { + m->input |= INPUT_Z_DOWN; + } + + if (m->controller->buttonPressed & Z_TRIG) { + m->input |= INPUT_Z_PRESSED; + } + } + + if (m->input & INPUT_A_PRESSED) { + m->framesSinceA = 0; + } else if (m->framesSinceA < 0xFF) { + m->framesSinceA += 1; + } + + if (m->input & INPUT_B_PRESSED) { + m->framesSinceB = 0; + } else if (m->framesSinceB < 0xFF) { + m->framesSinceB += 1; + } +} + +/** + * Updates the joystick intended magnitude. + */ +void update_mario_joystick_inputs(struct MarioState *m) { + struct Controller *controller = m->controller; + f32 mag = ((controller->stickMag / 64.0f) * (controller->stickMag / 64.0f)) * 64.0f; + + if (m->squishTimer == 0) { + m->intendedMag = mag / 2.0f; + } else { + m->intendedMag = mag / 8.0f; + } + + if (m->intendedMag > 0.0f) { + m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + m->area->camera->yaw; + m->input |= INPUT_NONZERO_ANALOG; + } else { + m->intendedYaw = m->faceAngle[1]; + } +} + +/** + * Resolves wall collisions, and updates a variety of inputs. + */ +void update_mario_geometry_inputs(struct MarioState *m) { + f32 gasLevel; + f32 ceilToFloorDist; + + f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 60.0f, 50.0f); + f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 30.0f, 24.0f); + + m->floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &m->floor); + + // If Mario is OOB, move his position to his graphical position (which was not updated) + // and check for the floor there. + // This can cause errant behavior when combined with astral projection, + // since the graphical position was not Mario's previous location. + if (m->floor == NULL) { + vec3f_copy(m->pos, m->marioObj->header.gfx.pos); + m->floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &m->floor); + } + + m->ceilHeight = vec3f_find_ceil(&m->pos[0], m->floorHeight, &m->ceil); + gasLevel = find_poison_gas_level(m->pos[0], m->pos[2]); + m->waterLevel = find_water_level(m->pos[0], m->pos[2]); + + if (m->floor != NULL) { + m->floorAngle = atan2s(m->floor->normal.z, m->floor->normal.x); + m->terrainSoundAddend = mario_get_terrain_sound_addend(m); + + if ((m->pos[1] > m->waterLevel - 40) && mario_floor_is_slippery(m)) { + m->input |= INPUT_ABOVE_SLIDE; + } + + if ((m->floor->flags & SURFACE_FLAG_DYNAMIC) + || (m->ceil && m->ceil->flags & SURFACE_FLAG_DYNAMIC)) { + ceilToFloorDist = m->ceilHeight - m->floorHeight; + + if ((0.0f <= ceilToFloorDist) && (ceilToFloorDist <= 150.0f)) { + m->input |= INPUT_SQUISHED; + } + } + + if (m->pos[1] > m->floorHeight + 100.0f) { + m->input |= INPUT_OFF_FLOOR; + } + + if (m->pos[1] < (m->waterLevel - 10)) { + m->input |= INPUT_IN_WATER; + } + + if (m->pos[1] < (gasLevel - 100.0f)) { + m->input |= INPUT_IN_POISON_GAS; + } + + } else { + level_trigger_warp(m, WARP_OP_DEATH); + } +} + +/** + * Handles Mario's input flags as well as a couple timers. + */ +void update_mario_inputs(struct MarioState *m) { + m->particleFlags = 0; + m->input = 0; + m->collidedObjInteractTypes = m->marioObj->collidedObjInteractTypes; + m->flags &= 0xFFFFFF; + + update_mario_button_inputs(m); + update_mario_joystick_inputs(m); + update_mario_geometry_inputs(m); + + debug_print_speed_action_normal(m); + + if (gCameraMovementFlags & CAM_MOVE_C_UP_MODE) { + if (m->action & ACT_FLAG_ALLOW_FIRST_PERSON) { + m->input |= INPUT_FIRST_PERSON; + } else { + gCameraMovementFlags &= ~CAM_MOVE_C_UP_MODE; + } + } + + if (!(m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED))) { + m->input |= INPUT_UNKNOWN_5; + } + + if (m->marioObj->oInteractStatus + & (INT_STATUS_HOOT_GRABBED_BY_MARIO | INT_STATUS_MARIO_UNK1 | INT_STATUS_HIT_BY_SHOCKWAVE)) { + m->input |= INPUT_UNKNOWN_10; + } + + // This function is located near other unused trampoline functions, + // perhaps logically grouped here with the timers. + stub_mario_step_1(m); + + if (m->wallKickTimer > 0) { + m->wallKickTimer--; + } + + if (m->doubleJumpTimer > 0) { + m->doubleJumpTimer--; + } +} + +/** + * Set's the camera preset for submerged action behaviors. + */ +void set_submerged_cam_preset_and_spawn_bubbles(struct MarioState *m) { + f32 heightBelowWater; + s16 camPreset; + + if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_SUBMERGED) { + heightBelowWater = (f32)(m->waterLevel - 80) - m->pos[1]; + camPreset = m->area->camera->mode; + + if (m->action & ACT_FLAG_METAL_WATER) { + if (camPreset != CAMERA_MODE_CLOSE) { + set_camera_mode(m->area->camera, CAMERA_MODE_CLOSE, 1); + } + } else { + if ((heightBelowWater > 800.0f) && (camPreset != CAMERA_MODE_BEHIND_MARIO)) { + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + } + + if ((heightBelowWater < 400.0f) && (camPreset != CAMERA_MODE_WATER_SURFACE)) { + set_camera_mode(m->area->camera, CAMERA_MODE_WATER_SURFACE, 1); + } + + // As long as Mario isn't drowning or at the top + // of the water with his head out, spawn bubbles. + if (!(m->action & ACT_FLAG_INTANGIBLE)) { + if ((m->pos[1] < (f32)(m->waterLevel - 160)) || (m->faceAngle[0] < -0x800)) { + m->particleFlags |= PARTICLE_BUBBLE; + } + } + } + } +} + +/** + * Both increments and decrements Mario's HP. + */ +void update_mario_health(struct MarioState *m) { + s32 terrainIsSnow; + + if (m->health >= 0x100) { + // When already healing or hurting Mario, Mario's HP is not changed any more here. + if (((u32) m->healCounter | (u32) m->hurtCounter) == 0) { + if ((m->input & INPUT_IN_POISON_GAS) && !(m->action & ACT_FLAG_INTANGIBLE)) { + if (!(m->flags & MARIO_METAL_CAP) && !gDebugLevelSelect) { + m->health -= 4; + } + } else { + if ((m->action & ACT_FLAG_SWIMMING) && !(m->action & ACT_FLAG_INTANGIBLE)) { + terrainIsSnow = (m->area->terrainType & TERRAIN_MASK) == TERRAIN_SNOW; + + // When Mario is near the water surface, recover health (unless in snow), + // when in snow terrains lose 3 health. + // If using the debug level select, do not lose any HP to water. + if ((m->pos[1] >= (m->waterLevel - 140)) && !terrainIsSnow) { + m->health += 0x1A; + } else if (!gDebugLevelSelect) { + m->health -= (terrainIsSnow ? 3 : 1); + } + } + } + } + + if (m->healCounter > 0) { + m->health += 0x40; + m->healCounter--; + } + if (m->hurtCounter > 0) { + m->health -= 0x40; + m->hurtCounter--; + } + + if (m->health > 0x880) { + m->health = 0x880; + } + if (m->health < 0x100) { + m->health = 0xFF; + } + + // Play a noise to alert the player when Mario is close to drowning. + if (((m->action & ACT_GROUP_MASK) == ACT_GROUP_SUBMERGED) && (m->health < 0x300)) { + play_sound(SOUND_MOVING_ALMOST_DROWNING, gDefaultSoundArgs); +#ifdef VERSION_SH + if (!gRumblePakTimer) { + gRumblePakTimer = 36; + if (is_rumble_finished_and_queue_empty()) { + queue_rumble_data(3, 30); + } + } + } else { + gRumblePakTimer = 0; +#endif + } + } +} + +/** + * Updates some basic info for camera usage. + */ +void update_mario_info_for_cam(struct MarioState *m) { + m->marioBodyState->action = m->action; + m->statusForCamera->action = m->action; + + vec3s_copy(m->statusForCamera->faceAngle, m->faceAngle); + + if (!(m->flags & MARIO_UNKNOWN_25)) { + vec3f_copy(m->statusForCamera->pos, m->pos); + } +} + +/** + * Resets Mario's model, done every time an action is executed. + */ +void mario_reset_bodystate(struct MarioState *m) { + struct MarioBodyState *bodyState = m->marioBodyState; + + bodyState->capState = MARIO_HAS_DEFAULT_CAP_OFF; + bodyState->eyeState = MARIO_EYES_BLINK; + bodyState->handState = MARIO_HAND_FISTS; + bodyState->modelState = 0; + bodyState->wingFlutter = FALSE; + + m->flags &= ~MARIO_METAL_SHOCK; +} + +/** + * Adjusts Mario's graphical height for quicksand. + */ +void sink_mario_in_quicksand(struct MarioState *m) { + struct Object *o = m->marioObj; + + if (o->header.gfx.throwMatrix) { + (*o->header.gfx.throwMatrix)[3][1] -= m->quicksandDepth; + } + + o->header.gfx.pos[1] -= m->quicksandDepth; +} + +/** + * Is a binary representation of the frames to flicker Mario's cap when the timer + * is running out. + * + * Equals [1000]^5 . [100]^8 . [10]^9 . [1] in binary, which is + * 100010001000100010001001001001001001001001001010101010101010101. + */ +u64 sCapFlickerFrames = 0x4444449249255555; + +/** + * Updates the cap flags mainly based on the cap timer. + */ +u32 update_and_return_cap_flags(struct MarioState *m) { + u32 flags = m->flags; + u32 action; + + if (m->capTimer > 0) { + action = m->action; + + if ((m->capTimer <= 60) + || ((action != ACT_READING_AUTOMATIC_DIALOG) && (action != ACT_READING_NPC_DIALOG) + && (action != ACT_READING_SIGN) && (action != ACT_IN_CANNON))) { + m->capTimer -= 1; + } + + if (m->capTimer == 0) { + stop_cap_music(); + + m->flags &= ~MARIO_SPECIAL_CAPS; + if (!(m->flags & MARIO_CAPS)) { + m->flags &= ~MARIO_CAP_ON_HEAD; + } + } + + if (m->capTimer == 60) { + fadeout_cap_music(); + } + + // This code flickers the cap through a long binary string, increasing in how + // common it flickers near the end. + if ((m->capTimer < 64) && ((1ULL << m->capTimer) & sCapFlickerFrames)) { + flags &= ~MARIO_SPECIAL_CAPS; + if (!(flags & MARIO_CAPS)) { + flags &= ~MARIO_CAP_ON_HEAD; + } + } + } + + return flags; +} + +/** + * Updates the Mario's cap, rendering, and hitbox. + */ +void mario_update_hitbox_and_cap_model(struct MarioState *m) { + struct MarioBodyState *bodyState = m->marioBodyState; + s32 flags = update_and_return_cap_flags(m); + + if (flags & MARIO_VANISH_CAP) { + bodyState->modelState = MODEL_STATE_NOISE_ALPHA; + } + + if (flags & MARIO_METAL_CAP) { + bodyState->modelState |= MODEL_STATE_METAL; + } + + if (flags & MARIO_METAL_SHOCK) { + bodyState->modelState |= MODEL_STATE_METAL; + } + + //! (Pause buffered hitstun) Since the global timer increments while paused, + // this can be paused through to give continual invisibility. This leads to + // no interaction with objects. + if ((m->invincTimer >= 3) && (gGlobalTimer & 1)) { + gMarioState->marioObj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; + } + + if (flags & MARIO_CAP_IN_HAND) { + if (flags & MARIO_WING_CAP) { + bodyState->handState = MARIO_HAND_HOLDING_WING_CAP; + } else { + bodyState->handState = MARIO_HAND_HOLDING_CAP; + } + } + + if (flags & MARIO_CAP_ON_HEAD) { + if (flags & MARIO_WING_CAP) { + bodyState->capState = MARIO_HAS_WING_CAP_ON; + } else { + bodyState->capState = MARIO_HAS_DEFAULT_CAP_ON; + } + } + + // Short hitbox for crouching/crawling/etc. + if (m->action & ACT_FLAG_SHORT_HITBOX) { + m->marioObj->hitboxHeight = 100.0f; + } else { + m->marioObj->hitboxHeight = 160.0f; + } + + if ((m->flags & MARIO_TELEPORTING) && (m->fadeWarpOpacity != 0xFF)) { + bodyState->modelState &= ~0xFF; + bodyState->modelState |= (0x100 | m->fadeWarpOpacity); + } +} + +/** + * An unused and possibly a debug function. Z + another button input + * sets Mario with a different cap. + */ +static void debug_update_mario_cap(u16 button, s32 flags, u16 capTimer, u16 capMusic) { +// // This checks for Z_TRIG instead of Z_DOWN flag +// // (which is also what other debug functions do), +// // so likely debug behavior rather than unused behavior. +// if ((gPlayer1Controller->buttonDown & Z_TRIG) && (gPlayer1Controller->buttonPressed & button) +// && !(gMarioState->flags & flags)) { +// gMarioState->flags |= (flags + MARIO_CAP_ON_HEAD); + +// if (capTimer > gMarioState->capTimer) { +// gMarioState->capTimer = capTimer; +// } + +// play_cap_music(capMusic); +// } +} + +#ifdef VERSION_SH +void func_sh_8025574C(void) { + if (gMarioState->particleFlags & PARTICLE_HORIZONTAL_STAR) { + queue_rumble_data(5, 80); + } else if (gMarioState->particleFlags & PARTICLE_VERTICAL_STAR) { + queue_rumble_data(5, 80); + } else if (gMarioState->particleFlags & PARTICLE_TRIANGLE) { + queue_rumble_data(5, 80); + } + if(gMarioState->heldObj && gMarioState->heldObj->behavior == segmented_to_virtual(bhvBobomb)) { + reset_rumble_timers(); + } +} +#endif + +/** + * Main function for executing Mario's behavior. + */ +s32 execute_mario_action(UNUSED struct Object *o) { + s32 inLoop = TRUE; + + if (gMarioState->action) { + gMarioState->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; + mario_reset_bodystate(gMarioState); + update_mario_inputs(gMarioState); + mario_handle_special_floors(gMarioState); + mario_process_interactions(gMarioState); + + // If Mario is OOB, stop executing actions. + if (gMarioState->floor == NULL) { + return 0; + } + + // The function can loop through many action shifts in one frame, + // which can lead to unexpected sub-frame behavior. Could potentially hang + // if a loop of actions were found, but there has not been a situation found. + while (inLoop) { + switch (gMarioState->action & ACT_GROUP_MASK) { + case ACT_GROUP_STATIONARY: + inLoop = mario_execute_stationary_action(gMarioState); + break; + + case ACT_GROUP_MOVING: + inLoop = mario_execute_moving_action(gMarioState); + break; + + case ACT_GROUP_AIRBORNE: + inLoop = mario_execute_airborne_action(gMarioState); + break; + + case ACT_GROUP_SUBMERGED: + inLoop = mario_execute_submerged_action(gMarioState); + break; + + case ACT_GROUP_CUTSCENE: + inLoop = mario_execute_cutscene_action(gMarioState); + break; + + case ACT_GROUP_AUTOMATIC: + inLoop = mario_execute_automatic_action(gMarioState); + break; + + case ACT_GROUP_OBJECT: + inLoop = mario_execute_object_action(gMarioState); + break; + } + } + + sink_mario_in_quicksand(gMarioState); + squish_mario_model(gMarioState); + set_submerged_cam_preset_and_spawn_bubbles(gMarioState); + update_mario_health(gMarioState); + update_mario_info_for_cam(gMarioState); + mario_update_hitbox_and_cap_model(gMarioState); + + // Both of the wind handling portions play wind audio only in + // non-Japanese releases. + if (gMarioState->floor->type == SURFACE_HORIZONTAL_WIND) { + spawn_wind_particles(0, (gMarioState->floor->force << 8)); +#ifndef VERSION_JP + play_sound(SOUND_ENV_WIND2, gMarioState->marioObj->header.gfx.cameraToObject); +#endif + } + + if (gMarioState->floor->type == SURFACE_VERTICAL_WIND) { + spawn_wind_particles(1, 0); +#ifndef VERSION_JP + play_sound(SOUND_ENV_WIND2, gMarioState->marioObj->header.gfx.cameraToObject); +#endif + } + + play_infinite_stairs_music(); + gMarioState->marioObj->oInteractStatus = 0; +#ifdef VERSION_SH + func_sh_8025574C(); +#endif + + return gMarioState->particleFlags; + } + + return 0; +} + +/************************************************** + * INITIALIZATION * + **************************************************/ + +void init_mario(void) { + Vec3s capPos; + struct Object *capObject; + + unused80339F10 = 0; + + gMarioState->actionTimer = 0; + gMarioState->framesSinceA = 0xFF; + gMarioState->framesSinceB = 0xFF; + + gMarioState->invincTimer = 0; + + if (save_file_get_flags() + & (SAVE_FLAG_CAP_ON_GROUND | SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI + | SAVE_FLAG_CAP_ON_MR_BLIZZARD)) { + gMarioState->flags = 0; + } else { + gMarioState->flags = (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD); + } + + gMarioState->forwardVel = 0.0f; + gMarioState->squishTimer = 0; + + gMarioState->hurtCounter = 0; + gMarioState->healCounter = 0; + + gMarioState->capTimer = 0; + gMarioState->quicksandDepth = 0.0f; + + gMarioState->heldObj = NULL; + gMarioState->riddenObj = NULL; + gMarioState->usedObj = NULL; + + gMarioState->waterLevel = + find_water_level(gMarioSpawnInfo->startPos[0], gMarioSpawnInfo->startPos[2]); + + gMarioState->area = gCurrentArea; + gMarioState->marioObj = gMarioObject; + gMarioState->marioObj->header.gfx.animInfo.animID = -1; + vec3s_copy(gMarioState->faceAngle, gMarioSpawnInfo->startAngle); + vec3s_set(gMarioState->angleVel, 0, 0, 0); + vec3s_to_vec3f(gMarioState->pos, gMarioSpawnInfo->startPos); + vec3f_set(gMarioState->vel, 0, 0, 0); + gMarioState->floorHeight = + find_floor(gMarioState->pos[0], gMarioState->pos[1], gMarioState->pos[2], &gMarioState->floor); + + if (gMarioState->pos[1] < gMarioState->floorHeight) { + gMarioState->pos[1] = gMarioState->floorHeight; + } + + gMarioState->marioObj->header.gfx.pos[1] = gMarioState->pos[1]; + + gMarioState->action = + (gMarioState->pos[1] <= (gMarioState->waterLevel - 100)) ? ACT_WATER_IDLE : ACT_IDLE; + + mario_reset_bodystate(gMarioState); + update_mario_info_for_cam(gMarioState); + gMarioState->marioBodyState->punchState = 0; + + gMarioState->marioObj->oPosX = gMarioState->pos[0]; + gMarioState->marioObj->oPosY = gMarioState->pos[1]; + gMarioState->marioObj->oPosZ = gMarioState->pos[2]; + + gMarioState->marioObj->oMoveAnglePitch = gMarioState->faceAngle[0]; + gMarioState->marioObj->oMoveAngleYaw = gMarioState->faceAngle[1]; + gMarioState->marioObj->oMoveAngleRoll = gMarioState->faceAngle[2]; + + vec3f_copy(gMarioState->marioObj->header.gfx.pos, gMarioState->pos); + vec3s_set(gMarioState->marioObj->header.gfx.angle, 0, gMarioState->faceAngle[1], 0); + +#ifdef TODO_LATER + if (save_file_get_cap_pos(capPos)) { + capObject = spawn_object(gMarioState->marioObj, MODEL_MARIOS_CAP, bhvNormalCap); + + capObject->oPosX = capPos[0]; + capObject->oPosY = capPos[1]; + capObject->oPosZ = capPos[2]; + + capObject->oForwardVelS32 = 0; + + capObject->oMoveAngleYaw = 0; + } +#endif +} + +void init_mario_from_save_file(void) { + gMarioState->unk00 = 0; + gMarioState->flags = 0; + gMarioState->action = 0; + gMarioState->spawnInfo = gMarioSpawnInfo; + gMarioState->statusForCamera = &gPlayerCameraState; // PATCH + gMarioState->marioBodyState = &gBodyStates[0]; + gMarioState->controller = &gController; + gMarioState->animation = &D_80339D10; + + gMarioState->numCoins = 0; + gMarioState->numStars = + save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1); + gMarioState->numKeys = 0; + + gMarioState->numLives = 4; + gMarioState->health = 0x880; + + gMarioState->prevNumStarsForDialog = gMarioState->numStars; + gMarioState->unkB0 = 0xBD; + + gHudDisplay.coins = 0; + gHudDisplay.wedges = 8; +} diff --git a/src/game/mario.h b/src/game/mario.h new file mode 100644 index 0000000..e18b3d7 --- /dev/null +++ b/src/game/mario.h @@ -0,0 +1,54 @@ +#ifndef MARIO_H +#define MARIO_H + +#include "../include/PR/ultratypes.h" + +#include "../include/macros.h" +#include "../include/types.h" + +s32 is_anim_at_end(struct MarioState *m); +s32 is_anim_past_end(struct MarioState *m); +s16 set_mario_animation(struct MarioState *m, s32 targetAnimID); +s16 set_mario_anim_with_accel(struct MarioState *m, s32 targetAnimID, s32 accel); +void set_anim_to_frame(struct MarioState *m, s16 animFrame); +s32 is_anim_past_frame(struct MarioState *m, s16 animFrame); +s16 find_mario_anim_flags_and_translation(struct Object *o, s32 yaw, Vec3s translation); +void update_mario_pos_for_anim(struct MarioState *m); +s16 return_mario_anim_y_translation(struct MarioState *m); +void play_sound_if_no_flag(struct MarioState *m, u32 soundBits, u32 flags); +void play_mario_jump_sound(struct MarioState *m); +void adjust_sound_for_speed(struct MarioState *m); +void play_sound_and_spawn_particles(struct MarioState *m, u32 soundBits, u32 waveParticleType); +void play_mario_action_sound(struct MarioState *m, u32 soundBits, u32 waveParticleType); +void play_mario_landing_sound(struct MarioState *m, u32 soundBits); +void play_mario_landing_sound_once(struct MarioState *m, u32 soundBits); +void play_mario_heavy_landing_sound(struct MarioState *m, u32 soundBits); +void play_mario_heavy_landing_sound_once(struct MarioState *m, u32 soundBits); +void play_mario_sound(struct MarioState *m, s32 primarySoundBits, s32 scondarySoundBits); +void mario_set_forward_vel(struct MarioState *m, f32 speed); +s32 mario_get_floor_class(struct MarioState *m); +u32 mario_get_terrain_sound_addend(struct MarioState *m); +struct Surface *resolve_and_return_wall_collisions(Vec3f pos, f32 offset, f32 radius); +f32 vec3f_find_ceil(Vec3f pos, f32 height, struct Surface **ceil); +s32 mario_facing_downhill(struct MarioState *m, s32 turnYaw); +u32 mario_floor_is_slippery(struct MarioState *m); +s32 mario_floor_is_slope(struct MarioState *m); +s32 mario_floor_is_steep(struct MarioState *m); +f32 find_floor_height_relative_polar(struct MarioState *m, s16 angleFromMario, f32 distFromMario); +s16 find_floor_slope(struct MarioState *m, s16 yawOffset); +void update_mario_sound_and_camera(struct MarioState *m); +void set_steep_jump_action(struct MarioState *m); +u32 set_mario_action(struct MarioState *, u32 action, u32 actionArg); +s32 set_jump_from_landing(struct MarioState *m); +s32 set_jumping_action(struct MarioState *m, u32 action, u32 actionArg); +s32 drop_and_set_mario_action(struct MarioState *m, u32 action, u32 actionArg); +s32 hurt_and_set_mario_action(struct MarioState *m, u32 action, u32 actionArg, s16 hurtCounter); +s32 check_common_action_exits(struct MarioState *m); +s32 check_common_hold_action_exits(struct MarioState *m); +s32 transition_submerged_to_walking(struct MarioState *m); +s32 set_water_plunge_action(struct MarioState *m); +s32 execute_mario_action(UNUSED struct Object *o); +void init_mario(void); +void init_mario_from_save_file(void); + +#endif // MARIO_H diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c new file mode 100644 index 0000000..2ed3d2c --- /dev/null +++ b/src/game/mario_actions_airborne.c @@ -0,0 +1,2122 @@ +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "../include/sm64.h" +#include "area.h" +//#include "audio/external.h" +#include "camera.h" +#include "../engine/graph_node.h" +#include "../engine/math_util.h" +//#include "game_init.h" +#include "interaction.h" +#include "level_update.h" +#include "mario.h" +#include "mario_step.h" +#include "save_file.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + +void play_flip_sounds(struct MarioState *m, s16 frame1, s16 frame2, s16 frame3) { + s32 animFrame = m->marioObj->header.gfx.animInfo.animFrame; + if (animFrame == frame1 || animFrame == frame2 || animFrame == frame3) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } +} + +void play_far_fall_sound(struct MarioState *m) { + u32 action = m->action; + if (!(action & ACT_FLAG_INVULNERABLE) && action != ACT_TWIRLING && action != ACT_FLYING + && !(m->flags & MARIO_UNKNOWN_18)) { + if (m->peakHeight - m->pos[1] > 1150.0f) { + play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject); + m->flags |= MARIO_UNKNOWN_18; + } + } +} + +#ifndef VERSION_JP +void play_knockback_sound(struct MarioState *m) { + if (m->actionArg == 0 && (m->forwardVel <= -28.0f || m->forwardVel >= 28.0f)) { + play_sound_if_no_flag(m, SOUND_MARIO_DOH, MARIO_MARIO_SOUND_PLAYED); + } else { + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); + } +} +#endif + +s32 lava_boost_on_wall(struct MarioState *m) { + m->faceAngle[1] = atan2s(m->wall->normal.z, m->wall->normal.x); + + if (m->forwardVel < 24.0f) { + m->forwardVel = 24.0f; + } + + if (!(m->flags & MARIO_METAL_CAP)) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18; + } + + play_sound(SOUND_MARIO_ON_FIRE, m->marioObj->header.gfx.cameraToObject); + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, ACT_LAVA_BOOST, 1); +} + +s32 check_fall_damage(struct MarioState *m, u32 hardFallAction) { + f32 fallHeight; + f32 damageHeight; + + fallHeight = m->peakHeight - m->pos[1]; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + + //! Never true + if (m->actionState == ACT_GROUND_POUND) { + damageHeight = 600.0f; + } else { + damageHeight = 1150.0f; + } + +#pragma GCC diagnostic pop + + if (m->action != ACT_TWIRLING && m->floor->type != SURFACE_BURNING) { + if (m->vel[1] < -55.0f) { + if (fallHeight > 3000.0f) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 16 : 24; +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + set_camera_shake_from_hit(SHAKE_FALL_DAMAGE); + play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); + return drop_and_set_mario_action(m, hardFallAction, 4); + } else if (fallHeight > damageHeight && !mario_floor_is_slippery(m)) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 8 : 12; + m->squishTimer = 30; +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + set_camera_shake_from_hit(SHAKE_FALL_DAMAGE); + play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject); + } + } + } + + return FALSE; +} + +s32 check_kick_or_dive_in_air(struct MarioState *m) { + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, m->forwardVel > 28.0f ? ACT_DIVE : ACT_JUMP_KICK, 0); + } + return FALSE; +} + +s32 should_get_stuck_in_ground(struct MarioState *m) { + u32 terrainType = m->area->terrainType & TERRAIN_MASK; + struct Surface *floor = m->floor; + s32 flags = floor->flags; + s32 type = floor->type; + + if (floor != NULL && (terrainType == TERRAIN_SNOW || terrainType == TERRAIN_SAND) + && type != SURFACE_BURNING && SURFACE_IS_NOT_HARD(type)) { + if (!(flags & 0x01) && m->peakHeight - m->pos[1] > 1000.0f && floor->normal.y >= 0.8660254f) { + return TRUE; + } + } + + return FALSE; +} + +s32 check_fall_damage_or_get_stuck(struct MarioState *m, u32 hardFallAction) { + if (should_get_stuck_in_ground(m)) { +#ifdef VERSION_JP + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#else + play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); +#endif + m->particleFlags |= PARTICLE_MIST_CIRCLE; + drop_and_set_mario_action(m, ACT_FEET_STUCK_IN_GROUND, 0); +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + return TRUE; + } + + return check_fall_damage(m, hardFallAction); +} + +s32 check_horizontal_wind(struct MarioState *m) { + struct Surface *floor; + f32 speed; + s16 pushAngle; + + floor = m->floor; + + if (floor->type == SURFACE_HORIZONTAL_WIND) { + pushAngle = floor->force << 8; + + m->slideVelX += 1.2f * sins(pushAngle); + m->slideVelZ += 1.2f * coss(pushAngle); + + speed = sqrtf(m->slideVelX * m->slideVelX + m->slideVelZ * m->slideVelZ); + + if (speed > 48.0f) { + m->slideVelX = m->slideVelX * 48.0f / speed; + m->slideVelZ = m->slideVelZ * 48.0f / speed; + speed = 32.0f; //! This was meant to be 48? + } else if (speed > 32.0f) { + speed = 32.0f; + } + + m->vel[0] = m->slideVelX; + m->vel[2] = m->slideVelZ; + m->slideYaw = atan2s(m->slideVelZ, m->slideVelX); + m->forwardVel = speed * coss(m->faceAngle[1] - m->slideYaw); + +#ifdef VERSION_JP + play_sound(SOUND_ENV_WIND2, m->marioObj->header.gfx.cameraToObject); +#endif + return TRUE; + } + + return FALSE; +} + +void update_air_with_turn(struct MarioState *m) { + f32 dragThreshold; + s16 intendedDYaw; + f32 intendedMag; + + if (!check_horizontal_wind(m)) { + dragThreshold = m->action == ACT_LONG_JUMP ? 48.0f : 32.0f; + m->forwardVel = approach_f32(m->forwardVel, 0.0f, 0.35f, 0.35f); + + if (m->input & INPUT_NONZERO_ANALOG) { + intendedDYaw = m->intendedYaw - m->faceAngle[1]; + intendedMag = m->intendedMag / 32.0f; + + m->forwardVel += 1.5f * coss(intendedDYaw) * intendedMag; + m->faceAngle[1] += 512.0f * sins(intendedDYaw) * intendedMag; + } + + //! Uncapped air speed. Net positive when moving forward. + if (m->forwardVel > dragThreshold) { + m->forwardVel -= 1.0f; + } + if (m->forwardVel < -16.0f) { + m->forwardVel += 2.0f; + } + + m->vel[0] = m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->vel[2] = m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); + } +} + +void update_air_without_turn(struct MarioState *m) { + f32 sidewaysSpeed = 0.0f; + f32 dragThreshold; + s16 intendedDYaw; + f32 intendedMag; + + if (!check_horizontal_wind(m)) { + dragThreshold = m->action == ACT_LONG_JUMP ? 48.0f : 32.0f; + m->forwardVel = approach_f32(m->forwardVel, 0.0f, 0.35f, 0.35f); + + if (m->input & INPUT_NONZERO_ANALOG) { + intendedDYaw = m->intendedYaw - m->faceAngle[1]; + intendedMag = m->intendedMag / 32.0f; + + m->forwardVel += intendedMag * coss(intendedDYaw) * 1.5f; + sidewaysSpeed = intendedMag * sins(intendedDYaw) * 10.0f; + } + + //! Uncapped air speed. Net positive when moving forward. + if (m->forwardVel > dragThreshold) { + m->forwardVel -= 1.0f; + } + if (m->forwardVel < -16.0f) { + m->forwardVel += 2.0f; + } + + m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); + + m->slideVelX += sidewaysSpeed * sins(m->faceAngle[1] + 0x4000); + m->slideVelZ += sidewaysSpeed * coss(m->faceAngle[1] + 0x4000); + + m->vel[0] = m->slideVelX; + m->vel[2] = m->slideVelZ; + } +} + +void update_lava_boost_or_twirling(struct MarioState *m) { + s16 intendedDYaw; + f32 intendedMag; + + if (m->input & INPUT_NONZERO_ANALOG) { + intendedDYaw = m->intendedYaw - m->faceAngle[1]; + intendedMag = m->intendedMag / 32.0f; + + m->forwardVel += coss(intendedDYaw) * intendedMag; + m->faceAngle[1] += sins(intendedDYaw) * intendedMag * 1024.0f; + + if (m->forwardVel < 0.0f) { + m->faceAngle[1] += 0x8000; + m->forwardVel *= -1.0f; + } + + if (m->forwardVel > 32.0f) { + m->forwardVel -= 2.0f; + } + } + + m->vel[0] = m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->vel[2] = m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); +} + +void update_flying_yaw(struct MarioState *m) { + s16 targetYawVel = -(s16)(m->controller->stickX * (m->forwardVel / 4.0f)); + + if (targetYawVel > 0) { + if (m->angleVel[1] < 0) { + m->angleVel[1] += 0x40; + if (m->angleVel[1] > 0x10) { + m->angleVel[1] = 0x10; + } + } else { + m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x10, 0x20); + } + } else if (targetYawVel < 0) { + if (m->angleVel[1] > 0) { + m->angleVel[1] -= 0x40; + if (m->angleVel[1] < -0x10) { + m->angleVel[1] = -0x10; + } + } else { + m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x20, 0x10); + } + } else { + m->angleVel[1] = approach_s32(m->angleVel[1], 0, 0x40, 0x40); + } + + m->faceAngle[1] += m->angleVel[1]; + m->faceAngle[2] = 20 * -m->angleVel[1]; +} + +void update_flying_pitch(struct MarioState *m) { + s16 targetPitchVel = -(s16)(m->controller->stickY * (m->forwardVel / 5.0f)); + + if (targetPitchVel > 0) { + if (m->angleVel[0] < 0) { + m->angleVel[0] += 0x40; + if (m->angleVel[0] > 0x20) { + m->angleVel[0] = 0x20; + } + } else { + m->angleVel[0] = approach_s32(m->angleVel[0], targetPitchVel, 0x20, 0x40); + } + } else if (targetPitchVel < 0) { + if (m->angleVel[0] > 0) { + m->angleVel[0] -= 0x40; + if (m->angleVel[0] < -0x20) { + m->angleVel[0] = -0x20; + } + } else { + m->angleVel[0] = approach_s32(m->angleVel[0], targetPitchVel, 0x40, 0x20); + } + } else { + m->angleVel[0] = approach_s32(m->angleVel[0], 0, 0x40, 0x40); + } +} + +void update_flying(struct MarioState *m) { + UNUSED u32 unused; + + update_flying_pitch(m); + update_flying_yaw(m); + + m->forwardVel -= 2.0f * ((f32) m->faceAngle[0] / 0x4000) + 0.1f; + m->forwardVel -= 0.5f * (1.0f - coss(m->angleVel[1])); + + if (m->forwardVel < 0.0f) { + m->forwardVel = 0.0f; + } + + if (m->forwardVel > 16.0f) { + m->faceAngle[0] += (m->forwardVel - 32.0f) * 6.0f; + } else if (m->forwardVel > 4.0f) { + m->faceAngle[0] += (m->forwardVel - 32.0f) * 10.0f; + } else { + m->faceAngle[0] -= 0x400; + } + + m->faceAngle[0] += m->angleVel[0]; + + if (m->faceAngle[0] > 0x2AAA) { + m->faceAngle[0] = 0x2AAA; + } + if (m->faceAngle[0] < -0x2AAA) { + m->faceAngle[0] = -0x2AAA; + } + + m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]); + m->vel[1] = m->forwardVel * sins(m->faceAngle[0]); + m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]); + + m->slideVelX = m->vel[0]; + m->slideVelZ = m->vel[2]; +} + +u32 common_air_action_step(struct MarioState *m, u32 landAction, s32 animation, u32 stepArg) { + u32 stepResult; + + update_air_without_turn(m); + + stepResult = perform_air_step(m, stepArg); + switch (stepResult) { + case AIR_STEP_NONE: + set_mario_animation(m, animation); + break; + + case AIR_STEP_LANDED: + if (!check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB)) { + set_mario_action(m, landAction, 0); + } + break; + + case AIR_STEP_HIT_WALL: + set_mario_animation(m, animation); + + if (m->forwardVel > 16.0f) { +#ifdef VERSION_SH + queue_rumble_data(5, 40); +#endif + mario_bonk_reflection(m, FALSE); + m->faceAngle[1] += 0x8000; + + if (m->wall != NULL) { + set_mario_action(m, ACT_AIR_HIT_WALL, 0); + } else { + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + //! Hands-free holding. Bonking while no wall is referenced + // sets Mario's action to a non-holding action without + // dropping the object, causing the hands-free holding + // glitch. This can be achieved using an exposed ceiling, + // out of bounds, grazing the bottom of a wall while + // falling such that the final quarter step does not find a + // wall collision, or by rising into the top of a wall such + // that the final quarter step detects a ledge, but you are + // not able to ledge grab it. + if (m->forwardVel >= 38.0f) { + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + } else { + if (m->forwardVel > 8.0f) { + mario_set_forward_vel(m, -8.0f); + } + return set_mario_action(m, ACT_SOFT_BONK, 0); + } + } + } else { + mario_set_forward_vel(m, 0.0f); + } + break; + + case AIR_STEP_GRABBED_LEDGE: + set_mario_animation(m, MARIO_ANIM_IDLE_ON_LEDGE); + drop_and_set_mario_action(m, ACT_LEDGE_GRAB, 0); + break; + + case AIR_STEP_GRABBED_CEILING: + set_mario_action(m, ACT_START_HANGING, 0); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return stepResult; +} + +s32 act_jump(struct MarioState *m) { + if (check_kick_or_dive_in_air(m)) { + return TRUE; + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + common_air_action_step(m, ACT_JUMP_LAND, MARIO_ANIM_SINGLE_JUMP, + AIR_STEP_CHECK_LEDGE_GRAB | AIR_STEP_CHECK_HANG); + return FALSE; +} + +s32 act_double_jump(struct MarioState *m) { + s32 animation = (m->vel[1] >= 0.0f) + ? MARIO_ANIM_DOUBLE_JUMP_RISE + : MARIO_ANIM_DOUBLE_JUMP_FALL; + + if (check_kick_or_dive_in_air(m)) { + return TRUE; + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_HOOHOO); + common_air_action_step(m, ACT_DOUBLE_JUMP_LAND, animation, + AIR_STEP_CHECK_LEDGE_GRAB | AIR_STEP_CHECK_HANG); + return FALSE; +} + +s32 act_triple_jump(struct MarioState *m) { + if (gSpecialTripleJump) { + return set_mario_action(m, ACT_SPECIAL_TRIPLE_JUMP, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + +#ifndef VERSION_JP + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); +#else + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAHOO); +#endif + + common_air_action_step(m, ACT_TRIPLE_JUMP_LAND, MARIO_ANIM_TRIPLE_JUMP, 0); +#ifdef VERSION_SH + if (m->action == ACT_TRIPLE_JUMP_LAND) { + queue_rumble_data(5, 40); + } +#endif + play_flip_sounds(m, 2, 8, 20); + return FALSE; +} + +s32 act_backflip(struct MarioState *m) { + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAH_WAH_HOO); + common_air_action_step(m, ACT_BACKFLIP_LAND, MARIO_ANIM_BACKFLIP, 0); +#ifdef VERSION_SH + if (m->action == ACT_BACKFLIP_LAND) { + queue_rumble_data(5, 40); + } +#endif + play_flip_sounds(m, 2, 3, 17); + return FALSE; +} + +s32 act_freefall(struct MarioState *m) { + s32 animation; + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + switch (m->actionArg) { + case 0: + animation = MARIO_ANIM_GENERAL_FALL; + break; + case 1: + animation = MARIO_ANIM_FALL_FROM_SLIDE; + break; + case 2: + animation = MARIO_ANIM_FALL_FROM_SLIDE_KICK; + break; + } + + common_air_action_step(m, ACT_FREEFALL_LAND, animation, AIR_STEP_CHECK_LEDGE_GRAB); + return FALSE; +} + +s32 act_hold_jump(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if ((m->input & INPUT_B_PRESSED) && !(m->heldObj->oInteractionSubtype & INT_SUBTYPE_HOLDABLE_NPC)) { + return set_mario_action(m, ACT_AIR_THROW, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return drop_and_set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + common_air_action_step(m, ACT_HOLD_JUMP_LAND, MARIO_ANIM_JUMP_WITH_LIGHT_OBJ, + AIR_STEP_CHECK_LEDGE_GRAB); + return FALSE; +} + +s32 act_hold_freefall(struct MarioState *m) { + s32 animation; + if (m->actionArg == 0) { + animation = MARIO_ANIM_FALL_WITH_LIGHT_OBJ; + } else { + animation = MARIO_ANIM_FALL_FROM_SLIDING_WITH_LIGHT_OBJ; + } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if ((m->input & INPUT_B_PRESSED) && !(m->heldObj->oInteractionSubtype & INT_SUBTYPE_HOLDABLE_NPC)) { + return set_mario_action(m, ACT_AIR_THROW, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return drop_and_set_mario_action(m, ACT_GROUND_POUND, 0); + } + + common_air_action_step(m, ACT_HOLD_FREEFALL_LAND, animation, AIR_STEP_CHECK_LEDGE_GRAB); + return FALSE; +} + +s32 act_side_flip(struct MarioState *m) { + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + + if (common_air_action_step(m, ACT_SIDE_FLIP_LAND, MARIO_ANIM_SLIDEFLIP, AIR_STEP_CHECK_LEDGE_GRAB) + != AIR_STEP_GRABBED_LEDGE) { + m->marioObj->header.gfx.angle[1] += 0x8000; + } + + // This must be one line to match on -O2 + // clang-format off + if (m->marioObj->header.gfx.animInfo.animFrame == 6) play_sound(SOUND_ACTION_SIDE_FLIP_UNK, m->marioObj->header.gfx.cameraToObject); + // clang-format on + return FALSE; +} + +s32 act_wall_kick_air(struct MarioState *m) { + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_jump_sound(m); + common_air_action_step(m, ACT_JUMP_LAND, MARIO_ANIM_SLIDEJUMP, AIR_STEP_CHECK_LEDGE_GRAB); + return FALSE; +} + +s32 act_long_jump(struct MarioState *m) { + s32 animation; + if (!m->marioObj->oMarioLongJumpIsSlow) { + animation = MARIO_ANIM_FAST_LONGJUMP; + } else { + animation = MARIO_ANIM_SLOW_LONGJUMP; + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAHOO); + + if (m->floor->type == SURFACE_VERTICAL_WIND && m->actionState == 0) { + play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); + m->actionState = 1; + } + + common_air_action_step(m, ACT_LONG_JUMP_LAND, animation, AIR_STEP_CHECK_LEDGE_GRAB); +#ifdef VERSION_SH + if (m->action == ACT_LONG_JUMP_LAND) { + queue_rumble_data(5, 40); + } +#endif + return FALSE; +} + +s32 act_riding_shell_air(struct MarioState *m) { + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + set_mario_animation(m, MARIO_ANIM_JUMP_RIDING_SHELL); + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_RIDING_SHELL_GROUND, 1); + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 0.0f); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + m->marioObj->header.gfx.pos[1] += 42.0f; + return FALSE; +} + +s32 act_twirling(struct MarioState *m) { + s16 startTwirlYaw = m->twirlYaw; + s16 yawVelTarget; + + if (m->input & INPUT_A_DOWN) { + yawVelTarget = 0x2000; + } else { + yawVelTarget = 0x1800; + } + + m->angleVel[1] = approach_s32(m->angleVel[1], yawVelTarget, 0x200, 0x200); + m->twirlYaw += m->angleVel[1]; + + set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_START_TWIRL : MARIO_ANIM_TWIRL); + if (is_anim_past_end(m)) { + m->actionArg = 1; + } + + if (startTwirlYaw > m->twirlYaw) { + play_sound(SOUND_ACTION_TWIRL, m->marioObj->header.gfx.cameraToObject); + } + + update_lava_boost_or_twirling(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_TWIRL_LAND, 0); + break; + + case AIR_STEP_HIT_WALL: + mario_bonk_reflection(m, FALSE); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + m->marioObj->header.gfx.angle[1] += m->twirlYaw; + return FALSE; +} + +s32 act_dive(struct MarioState *m) { + if (m->actionArg == 0) { + play_mario_sound(m, SOUND_ACTION_THROW, SOUND_MARIO_HOOHOO); + } else { + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + } + + set_mario_animation(m, MARIO_ANIM_DIVE); + if (mario_check_object_grab(m)) { + mario_grab_used_object(m); + m->marioBodyState->grabPos = GRAB_POS_LIGHT_OBJ; + if (m->action != ACT_DIVE) { + return TRUE; + } + } + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + if (m->vel[1] < 0.0f && m->faceAngle[0] > -0x2AAA) { + m->faceAngle[0] -= 0x200; + if (m->faceAngle[0] < -0x2AAA) { + m->faceAngle[0] = -0x2AAA; + } + } + m->marioObj->header.gfx.angle[0] = -m->faceAngle[0]; + break; + + case AIR_STEP_LANDED: + if (should_get_stuck_in_ground(m) && m->faceAngle[0] == -0x2AAA) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif +#ifdef VERSION_JP + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#else + play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); +#endif + m->particleFlags |= PARTICLE_MIST_CIRCLE; + drop_and_set_mario_action(m, ACT_HEAD_STUCK_IN_GROUND, 0); + } else if (!check_fall_damage(m, ACT_HARD_FORWARD_GROUND_KB)) { + if (m->heldObj == NULL) { + set_mario_action(m, ACT_DIVE_SLIDE, 0); + } else { + set_mario_action(m, ACT_DIVE_PICKING_UP, 0); + } + } + m->faceAngle[0] = 0; + break; + + case AIR_STEP_HIT_WALL: + mario_bonk_reflection(m, TRUE); + m->faceAngle[0] = 0; + + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + m->particleFlags |= PARTICLE_VERTICAL_STAR; + drop_and_set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return FALSE; +} + +s32 act_air_throw(struct MarioState *m) { + if (++(m->actionTimer) == 4) { + mario_throw_held_object(m); + } + + play_sound_if_no_flag(m, SOUND_MARIO_WAH2, MARIO_MARIO_SOUND_PLAYED); + set_mario_animation(m, MARIO_ANIM_THROW_LIGHT_OBJECT); + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (!check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB)) { + m->action = ACT_AIR_THROW_LAND; + } + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 0.0f); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return FALSE; +} + +s32 act_water_jump(struct MarioState *m) { + if (m->forwardVel < 15.0f) { + mario_set_forward_vel(m, 15.0f); + } + + play_mario_sound(m, SOUND_ACTION_UNKNOWN432, 0); + set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP); + + switch (perform_air_step(m, AIR_STEP_CHECK_LEDGE_GRAB)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_JUMP_LAND, 0); + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 15.0f); + break; + + case AIR_STEP_GRABBED_LEDGE: +#ifndef VERSION_JP + set_mario_animation(m, MARIO_ANIM_IDLE_ON_LEDGE); +#endif + set_mario_action(m, ACT_LEDGE_GRAB, 0); + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return FALSE; +} + +s32 act_hold_water_jump(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->forwardVel < 15.0f) { + mario_set_forward_vel(m, 15.0f); + } + + play_mario_sound(m, SOUND_ACTION_UNKNOWN432, 0); + set_mario_animation(m, MARIO_ANIM_JUMP_WITH_LIGHT_OBJ); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_HOLD_JUMP_LAND, 0); + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 15.0f); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return FALSE; +} + +s32 act_steep_jump(struct MarioState *m) { + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + mario_set_forward_vel(m, 0.98f * m->forwardVel); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (!check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB)) { + m->faceAngle[0] = 0; + set_mario_action(m, m->forwardVel < 0.0f ? ACT_BEGIN_SLIDING : ACT_JUMP_LAND, 0); + } + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 0.0f); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP); + m->marioObj->header.gfx.angle[1] = m->marioObj->oMarioSteepJumpYaw; + return FALSE; +} + +s32 act_ground_pound(struct MarioState *m) { + u32 stepResult; + f32 yOffset; + + play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED); + + if (m->actionState == 0) { + if (m->actionTimer < 10) { + yOffset = 20 - 2 * m->actionTimer; + if (m->pos[1] + yOffset + 160.0f < m->ceilHeight) { + m->pos[1] += yOffset; + m->peakHeight = m->pos[1]; + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + } + } + + m->vel[1] = -50.0f; + mario_set_forward_vel(m, 0.0f); + + set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_START_GROUND_POUND + : MARIO_ANIM_TRIPLE_JUMP_GROUND_POUND); + if (m->actionTimer == 0) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + + m->actionTimer++; + if (m->actionTimer >= m->marioObj->header.gfx.animInfo.curAnim->loopEnd + 4) { + play_sound(SOUND_MARIO_GROUND_POUND_WAH, m->marioObj->header.gfx.cameraToObject); + m->actionState = 1; + } + } else { + set_mario_animation(m, MARIO_ANIM_GROUND_POUND); + + stepResult = perform_air_step(m, 0); + if (stepResult == AIR_STEP_LANDED) { + if (should_get_stuck_in_ground(m)) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif +#ifdef VERSION_JP + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#else + play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); +#endif + m->particleFlags |= PARTICLE_MIST_CIRCLE; + set_mario_action(m, ACT_BUTT_STUCK_IN_GROUND, 0); + } else { + play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_HEAVY_LANDING); + if (!check_fall_damage(m, ACT_HARD_BACKWARD_GROUND_KB)) { + m->particleFlags |= PARTICLE_MIST_CIRCLE | PARTICLE_HORIZONTAL_STAR; + set_mario_action(m, ACT_GROUND_POUND_LAND, 0); + } + } + set_camera_shake_from_hit(SHAKE_GROUND_POUND); + } else if (stepResult == AIR_STEP_HIT_WALL) { + mario_set_forward_vel(m, -16.0f); + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + } + } + + return FALSE; +} + +s32 act_burning_jump(struct MarioState *m) { + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, m->actionArg == 0 ? 0 : -1); + mario_set_forward_vel(m, m->forwardVel); + + if (perform_air_step(m, 0) == AIR_STEP_LANDED) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + set_mario_action(m, ACT_BURNING_GROUND, 0); + } + + set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_SINGLE_JUMP : MARIO_ANIM_FIRE_LAVA_BURN); + m->particleFlags |= PARTICLE_FIRE; + play_sound(SOUND_MOVING_LAVA_BURN, m->marioObj->header.gfx.cameraToObject); + + m->marioObj->oMarioBurnTimer += 3; + + m->health -= 10; + if (m->health < 0x100) { + m->health = 0xFF; + } +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + return FALSE; +} + +s32 act_burning_fall(struct MarioState *m) { + mario_set_forward_vel(m, m->forwardVel); + + if (perform_air_step(m, 0) == AIR_STEP_LANDED) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + set_mario_action(m, ACT_BURNING_GROUND, 0); + } + + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + m->particleFlags |= PARTICLE_FIRE; + m->marioObj->oMarioBurnTimer += 3; + + m->health -= 10; + if (m->health < 0x100) { + m->health = 0xFF; + } +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + return FALSE; +} + +s32 act_crazy_box_bounce(struct MarioState *m) { + f32 minSpeed; + + if (m->actionTimer == 0) { + switch (m->actionArg) { + case 0: + m->vel[1] = 45.0f; + minSpeed = 32.0f; + break; + + case 1: + m->vel[1] = 60.0f; + minSpeed = 36.0f; + break; + + case 2: + m->vel[1] = 100.0f; + minSpeed = 48.0f; + break; + } + + play_sound(minSpeed < 40.0f ? SOUND_GENERAL_BOING1 : SOUND_GENERAL_BOING2, + m->marioObj->header.gfx.cameraToObject); + + if (m->forwardVel < minSpeed) { + mario_set_forward_vel(m, minSpeed); + } + + m->actionTimer = 1; + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + set_mario_animation(m, MARIO_ANIM_DIVE); + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (m->actionArg < 2) { + set_mario_action(m, ACT_CRAZY_BOX_BOUNCE, m->actionArg + 1); + } else { + m->heldObj->oInteractStatus = INT_STATUS_STOP_RIDING; + m->heldObj = NULL; + set_mario_action(m, ACT_STOMACH_SLIDE, 0); + } +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + m->particleFlags |= PARTICLE_MIST_CIRCLE; + break; + + case AIR_STEP_HIT_WALL: + mario_bonk_reflection(m, FALSE); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + m->marioObj->header.gfx.angle[0] = atan2s(m->forwardVel, -m->vel[1]); + return FALSE; +} + +u32 common_air_knockback_step(struct MarioState *m, u32 landAction, u32 hardFallAction, s32 animation, + f32 speed) { + u32 stepResult; + + mario_set_forward_vel(m, speed); + + stepResult = perform_air_step(m, 0); + switch (stepResult) { + case AIR_STEP_NONE: + set_mario_animation(m, animation); + break; + + case AIR_STEP_LANDED: +#ifdef VERSION_SH + if (m->action == ACT_SOFT_BONK) { + queue_rumble_data(5, 80); + } +#endif + if (!check_fall_damage_or_get_stuck(m, hardFallAction)) { +#ifndef VERSION_JP + if (m->action == ACT_THROWN_FORWARD || m->action == ACT_THROWN_BACKWARD) { + set_mario_action(m, landAction, m->hurtCounter); + } else { + set_mario_action(m, landAction, m->actionArg); + } +#else + set_mario_action(m, landAction, m->actionArg); +#endif + } + break; + + case AIR_STEP_HIT_WALL: + set_mario_animation(m, MARIO_ANIM_BACKWARD_AIR_KB); + mario_bonk_reflection(m, FALSE); + + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + mario_set_forward_vel(m, -speed); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return stepResult; +} + +s32 check_wall_kick(struct MarioState *m) { + if ((m->input & INPUT_A_PRESSED) && m->wallKickTimer != 0 && m->prevAction == ACT_AIR_HIT_WALL) { + m->faceAngle[1] += 0x8000; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + + return FALSE; +} + +s32 act_backward_air_kb(struct MarioState *m) { + if (check_wall_kick(m)) { + return TRUE; + } + +#ifndef VERSION_JP + play_knockback_sound(m); +#else + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#endif + common_air_knockback_step(m, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB, 0x0002, -16.0f); + return FALSE; +} + +s32 act_forward_air_kb(struct MarioState *m) { + if (check_wall_kick(m)) { + return TRUE; + } + +#ifndef VERSION_JP + play_knockback_sound(m); +#else + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#endif + common_air_knockback_step(m, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB, 0x002D, 16.0f); + return FALSE; +} + +s32 act_hard_backward_air_kb(struct MarioState *m) { +#ifndef VERSION_JP + play_knockback_sound(m); +#else + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#endif + common_air_knockback_step(m, ACT_HARD_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB, 0x0002, + -16.0f); + return FALSE; +} + +s32 act_hard_forward_air_kb(struct MarioState *m) { +#ifndef VERSION_JP + play_knockback_sound(m); +#else + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#endif + common_air_knockback_step(m, ACT_HARD_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB, 0x002D, 16.0f); + return FALSE; +} + +s32 act_thrown_backward(struct MarioState *m) { + u32 landAction; + if (m->actionArg != 0) { + landAction = ACT_HARD_BACKWARD_GROUND_KB; + } else { + landAction = ACT_BACKWARD_GROUND_KB; + } + + play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_MARIO_SOUND_PLAYED); + + common_air_knockback_step(m, landAction, ACT_HARD_BACKWARD_GROUND_KB, 0x0002, m->forwardVel); + + m->forwardVel *= 0.98f; + return FALSE; +} + +s32 act_thrown_forward(struct MarioState *m) { + s16 pitch; + + u32 landAction; + if (m->actionArg != 0) { + landAction = ACT_HARD_FORWARD_GROUND_KB; + } else { + landAction = ACT_FORWARD_GROUND_KB; + } + + play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_MARIO_SOUND_PLAYED); + + if (common_air_knockback_step(m, landAction, ACT_HARD_FORWARD_GROUND_KB, 0x002D, m->forwardVel) + == AIR_STEP_NONE) { + pitch = atan2s(m->forwardVel, -m->vel[1]); + if (pitch > 0x1800) { + pitch = 0x1800; + } + + m->marioObj->header.gfx.angle[0] = pitch + 0x1800; + } + + m->forwardVel *= 0.98f; + return FALSE; +} + +s32 act_soft_bonk(struct MarioState *m) { + if (check_wall_kick(m)) { + return TRUE; + } + +#ifndef VERSION_JP + play_knockback_sound(m); +#else + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#endif + + common_air_knockback_step(m, ACT_FREEFALL_LAND, ACT_HARD_BACKWARD_GROUND_KB, 0x0056, m->forwardVel); + return FALSE; +} + +s32 act_getting_blown(struct MarioState *m) { + if (m->actionState == 0) { + if (m->forwardVel > -60.0f) { + m->forwardVel -= 6.0f; + } else { + m->actionState = 1; + } + } else { + if (m->forwardVel < -16.0f) { + m->forwardVel += 0.8f; + } + + if (m->vel[1] < 0.0f && m->unkC4 < 4.0f) { + m->unkC4 += 0.05f; + } + } + + if (++(m->actionTimer) == 20) { + mario_blow_off_cap(m, 50.0f); + } + + mario_set_forward_vel(m, m->forwardVel); +#ifdef VERSION_JP + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#endif + set_mario_animation(m, MARIO_ANIM_BACKWARD_AIR_KB); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_HARD_BACKWARD_AIR_KB, 0); + break; + + case AIR_STEP_HIT_WALL: + set_mario_animation(m, MARIO_ANIM_AIR_FORWARD_KB); + mario_bonk_reflection(m, FALSE); + + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + mario_set_forward_vel(m, -m->forwardVel); + break; + } + + return FALSE; +} + +s32 act_air_hit_wall(struct MarioState *m) { + if (m->heldObj != NULL) { + mario_drop_held_object(m); + } + + if (++(m->actionTimer) <= 2) { + if (m->input & INPUT_A_PRESSED) { + m->vel[1] = 52.0f; + m->faceAngle[1] += 0x8000; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + } else if (m->forwardVel >= 38.0f) { + m->wallKickTimer = 5; + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + m->particleFlags |= PARTICLE_VERTICAL_STAR; + return set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + } else { + m->wallKickTimer = 5; + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + if (m->forwardVel > 8.0f) { + mario_set_forward_vel(m, -8.0f); + } + return set_mario_action(m, ACT_SOFT_BONK, 0); + } + +#ifdef AVOID_UB + return +#endif + set_mario_animation(m, MARIO_ANIM_START_WALLKICK); + + //! Missing return statement. The returned value is the result of the call + // to set_mario_animation. In practice, this value is nonzero. + // This results in this action "cancelling" into itself. It is supposed to + // execute on two frames, but instead it executes twice on the same frame. + // This results in firsties only being possible for a single frame, instead + // of two. +} + +s32 act_forward_rollout(struct MarioState *m) { + if (m->actionState == 0) { + m->vel[1] = 30.0f; + m->actionState = 1; + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + if (m->actionState == 1) { + if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 4) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + } else { + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + } + break; + + case AIR_STEP_LANDED: + set_mario_action(m, ACT_FREEFALL_LAND_STOP, 0); + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 0.0f); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + if (m->actionState == 1 && is_anim_past_end(m)) { + m->actionState = 2; + } + return FALSE; +} + +s32 act_backward_rollout(struct MarioState *m) { + if (m->actionState == 0) { + m->vel[1] = 30.0f; + m->actionState = 1; + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, 0); + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + if (m->actionState == 1) { + if (set_mario_animation(m, MARIO_ANIM_BACKWARD_SPINNING) == 4) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + } else { + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + } + break; + + case AIR_STEP_LANDED: + set_mario_action(m, ACT_FREEFALL_LAND_STOP, 0); + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 0.0f); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + if (m->actionState == 1 && m->marioObj->header.gfx.animInfo.animFrame == 2) { + m->actionState = 2; + } + return FALSE; +} + +s32 act_butt_slide_air(struct MarioState *m) { + if (++(m->actionTimer) > 30 && m->pos[1] - m->floorHeight > 500.0f) { + return set_mario_action(m, ACT_FREEFALL, 1); + } + + update_air_with_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (m->actionState == 0 && m->vel[1] < 0.0f && m->floor->normal.y >= 0.9848077f) { + m->vel[1] = -m->vel[1] / 2.0f; + m->actionState = 1; + } else { + set_mario_action(m, ACT_BUTT_SLIDE, 0); + } + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + break; + + case AIR_STEP_HIT_WALL: + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + set_mario_animation(m, MARIO_ANIM_SLIDE); + return FALSE; +} + +s32 act_hold_butt_slide_air(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_HOLD_FREEFALL, 1); + } + + if (++m->actionTimer > 30 && m->pos[1] - m->floorHeight > 500.0f) { + return set_mario_action(m, ACT_HOLD_FREEFALL, 1); + } + + update_air_with_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (m->actionState == 0 && m->vel[1] < 0.0f && m->floor->normal.y >= 0.9848077f) { + m->vel[1] = -m->vel[1] / 2.0f; + m->actionState = 1; + } else { + set_mario_action(m, ACT_HOLD_BUTT_SLIDE, 0); + } + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + break; + + case AIR_STEP_HIT_WALL: + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + mario_drop_held_object(m); + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + set_mario_animation(m, MARIO_ANIM_SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ); + return FALSE; +} + +s32 act_lava_boost(struct MarioState *m) { +#ifdef VERSION_SH + if (!(m->flags & MARIO_MARIO_SOUND_PLAYED)) { + play_sound_if_no_flag(m, SOUND_MARIO_ON_FIRE, MARIO_MARIO_SOUND_PLAYED); + queue_rumble_data(5, 80); + } +#else + play_sound_if_no_flag(m, SOUND_MARIO_ON_FIRE, MARIO_MARIO_SOUND_PLAYED); +#endif + + if (!(m->input & INPUT_NONZERO_ANALOG)) { + m->forwardVel = approach_f32(m->forwardVel, 0.0f, 0.35f, 0.35f); + } + + update_lava_boost_or_twirling(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (m->floor->type == SURFACE_BURNING) { + m->actionState = 0; + if (!(m->flags & MARIO_METAL_CAP)) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18; + } + m->vel[1] = 84.0f; + play_sound(SOUND_MARIO_ON_FIRE, m->marioObj->header.gfx.cameraToObject); +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + } else { + play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + if (m->actionState < 2 && m->vel[1] < 0.0f) { + m->vel[1] = -m->vel[1] * 0.4f; + mario_set_forward_vel(m, m->forwardVel * 0.5f); + m->actionState += 1; + } else { + set_mario_action(m, ACT_LAVA_BOOST_LAND, 0); + } + } + break; + + case AIR_STEP_HIT_WALL: + mario_bonk_reflection(m, FALSE); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + set_mario_animation(m, MARIO_ANIM_FIRE_LAVA_BURN); + if ((m->area->terrainType & TERRAIN_MASK) != TERRAIN_SNOW && !(m->flags & MARIO_METAL_CAP) + && m->vel[1] > 0.0f) { + m->particleFlags |= PARTICLE_FIRE; + if (m->actionState == 0) { + play_sound(SOUND_MOVING_LAVA_BURN, m->marioObj->header.gfx.cameraToObject); + } + } + + if (m->health < 0x100) { + level_trigger_warp(m, WARP_OP_DEATH); + } + + m->marioBodyState->eyeState = MARIO_EYES_DEAD; +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + return FALSE; +} + +s32 act_slide_kick(struct MarioState *m) { + if (m->actionState == 0 && m->actionTimer == 0) { + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_HOOHOO); + set_mario_animation(m, MARIO_ANIM_SLIDE_KICK); + } + + if (++(m->actionTimer) > 30 && m->pos[1] - m->floorHeight > 500.0f) { + return set_mario_action(m, ACT_FREEFALL, 2); + } + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + if (m->actionState == 0) { + m->marioObj->header.gfx.angle[0] = atan2s(m->forwardVel, -m->vel[1]); + if (m->marioObj->header.gfx.angle[0] > 0x1800) { + m->marioObj->header.gfx.angle[0] = 0x1800; + } + } + break; + + case AIR_STEP_LANDED: + if (m->actionState == 0 && m->vel[1] < 0.0f) { + m->vel[1] = -m->vel[1] / 2.0f; + m->actionState = 1; + m->actionTimer = 0; + } else { + set_mario_action(m, ACT_SLIDE_KICK_SLIDE, 0); + } + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + break; + + case AIR_STEP_HIT_WALL: + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + m->particleFlags |= PARTICLE_VERTICAL_STAR; + + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return FALSE; +} + +s32 act_jump_kick(struct MarioState *m) { + s32 animFrame; + + if (m->actionState == 0) { + play_sound_if_no_flag(m, SOUND_MARIO_PUNCH_HOO, MARIO_ACTION_SOUND_PLAYED); + m->marioObj->header.gfx.animInfo.animID = -1; + set_mario_animation(m, MARIO_ANIM_AIR_KICK); + m->actionState = 1; + } + + animFrame = m->marioObj->header.gfx.animInfo.animFrame; + if (animFrame == 0) { + m->marioBodyState->punchState = (2 << 6) | 6; + } + if (animFrame >= 0 && animFrame < 8) { + m->flags |= MARIO_KICKING; + } + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (!check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB)) { + set_mario_action(m, ACT_FREEFALL_LAND, 0); + } + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, 0.0f); + break; + } + + return FALSE; +} + +s32 act_shot_from_cannon(struct MarioState *m) { + if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { + m->statusForCamera->cameraEvent = CAM_EVENT_SHOT_FROM_CANNON; + } + + mario_set_forward_vel(m, m->forwardVel); + + play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + set_mario_animation(m, MARIO_ANIM_AIRBORNE_ON_STOMACH); + m->faceAngle[0] = atan2s(m->forwardVel, m->vel[1]); + m->marioObj->header.gfx.angle[0] = -m->faceAngle[0]; + break; + + case AIR_STEP_LANDED: + set_mario_action(m, ACT_DIVE_SLIDE, 0); + m->faceAngle[0] = 0; + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, -16.0f); + + m->faceAngle[0] = 0; + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + if ((m->flags & MARIO_WING_CAP) && m->vel[1] < 0.0f) { + set_mario_action(m, ACT_FLYING, 0); + } + + if ((m->forwardVel -= 0.05) < 10.0f) { + mario_set_forward_vel(m, 10.0f); + } + + if (m->vel[1] > 0.0f) { + m->particleFlags |= PARTICLE_DUST; + } +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + return FALSE; +} + +s32 act_flying(struct MarioState *m) { + s16 startPitch = m->faceAngle[0]; + + if (m->input & INPUT_Z_PRESSED) { + if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } + return set_mario_action(m, ACT_GROUND_POUND, 1); + } + + if (!(m->flags & MARIO_WING_CAP)) { + if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + } + + if (m->actionState == 0) { + if (m->actionArg == 0) { + set_mario_animation(m, MARIO_ANIM_FLY_FROM_CANNON); + } else { + set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING_FLIP); + if (m->marioObj->header.gfx.animInfo.animFrame == 1) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + } + + if (is_anim_at_end(m)) { + if (m->actionArg == 2) { + load_level_init_text(0); + m->actionArg = 1; + } + + set_mario_animation(m, MARIO_ANIM_WING_CAP_FLY); + m->actionState = 1; + } + } + + update_flying(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_NONE: + m->marioObj->header.gfx.angle[0] = -m->faceAngle[0]; + m->marioObj->header.gfx.angle[2] = m->faceAngle[2]; + m->actionTimer = 0; + break; + + case AIR_STEP_LANDED: + set_mario_action(m, ACT_DIVE_SLIDE, 0); + + set_mario_animation(m, MARIO_ANIM_DIVE); + set_anim_to_frame(m, 7); + + m->faceAngle[0] = 0; + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + break; + + case AIR_STEP_HIT_WALL: + if (m->wall != NULL) { + mario_set_forward_vel(m, -16.0f); + m->faceAngle[0] = 0; + + if (m->vel[1] > 0.0f) { + m->vel[1] = 0.0f; + } + + play_sound((m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_BONK + : SOUND_ACTION_BONK, + m->marioObj->header.gfx.cameraToObject); + + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_AIR_KB, 0); + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } else { + if (m->actionTimer++ == 0) { + play_sound(SOUND_ACTION_HIT, m->marioObj->header.gfx.cameraToObject); + } + + if (m->actionTimer == 30) { + m->actionTimer = 0; + } + + m->faceAngle[0] -= 0x200; + if (m->faceAngle[0] < -0x2AAA) { + m->faceAngle[0] = -0x2AAA; + } + + m->marioObj->header.gfx.angle[0] = -m->faceAngle[0]; + m->marioObj->header.gfx.angle[2] = m->faceAngle[2]; + } + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + if (m->faceAngle[0] > 0x800 && m->forwardVel >= 48.0f) { + m->particleFlags |= PARTICLE_DUST; + } + + if (startPitch <= 0 && m->faceAngle[0] > 0 && m->forwardVel >= 48.0f) { + play_sound(SOUND_ACTION_FLYING_FAST, m->marioObj->header.gfx.cameraToObject); +#ifndef VERSION_JP + play_sound(SOUND_MARIO_YAHOO_WAHA_YIPPEE + ((gAudioRandom % 5) << 16), + m->marioObj->header.gfx.cameraToObject); +#endif +#ifdef VERSION_SH + queue_rumble_data(50, 40); +#endif + } + + play_sound(SOUND_MOVING_FLYING, m->marioObj->header.gfx.cameraToObject); + adjust_sound_for_speed(m); + return FALSE; +} + +s32 act_riding_hoot(struct MarioState *m) { + if (!(m->input & INPUT_A_DOWN) || (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK7)) { + m->usedObj->oInteractStatus = 0; + m->usedObj->oHootMarioReleaseTime = gGlobalTimer; + + play_sound_if_no_flag(m, SOUND_MARIO_UH, MARIO_MARIO_SOUND_PLAYED); +#ifdef VERSION_SH + queue_rumble_data(4, 40); +#endif + return set_mario_action(m, ACT_FREEFALL, 0); + } + + m->pos[0] = m->usedObj->oPosX; + m->pos[1] = m->usedObj->oPosY - 92.5f; + m->pos[2] = m->usedObj->oPosZ; + + m->faceAngle[1] = 0x4000 - m->usedObj->oMoveAngleYaw; + + if (m->actionState == 0) { + set_mario_animation(m, MARIO_ANIM_HANG_ON_CEILING); + if (is_anim_at_end(m)) { + set_mario_animation(m, MARIO_ANIM_HANG_ON_OWL); + m->actionState = 1; + } + } + + vec3f_set(m->vel, 0.0f, 0.0f, 0.0f); + vec3f_set(m->marioObj->header.gfx.pos, m->pos[0], m->pos[1], m->pos[2]); + vec3s_set(m->marioObj->header.gfx.angle, 0, 0x4000 - m->faceAngle[1], 0); + return FALSE; +} + +s32 act_flying_triple_jump(struct MarioState *m) { +#ifndef VERSION_JP + if (m->input & (INPUT_B_PRESSED | INPUT_Z_PRESSED)) { + if (m->area->camera->mode == CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, m->area->camera->defMode, 1); + } + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } else { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + } +#else + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } +#endif + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAHOO); + if (m->actionState == 0) { + set_mario_animation(m, MARIO_ANIM_TRIPLE_JUMP_FLY); + + if (m->marioObj->header.gfx.animInfo.animFrame == 7) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + + if (is_anim_past_end(m)) { + set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING); +#ifdef VERSION_SH + queue_rumble_data(8, 80); +#endif + m->actionState = 1; + } + } + + if (m->actionState == 1 && m->marioObj->header.gfx.animInfo.animFrame == 1) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + + if (m->vel[1] < 4.0f) { + if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + } + + if (m->forwardVel < 32.0f) { + mario_set_forward_vel(m, 32.0f); + } + + set_mario_action(m, ACT_FLYING, 1); + } + + if (m->actionTimer++ == 10 && m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { + set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); + } + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (!check_fall_damage_or_get_stuck(m, ACT_HARD_BACKWARD_GROUND_KB)) { + set_mario_action(m, ACT_DOUBLE_JUMP_LAND, 0); + } + break; + + case AIR_STEP_HIT_WALL: + mario_bonk_reflection(m, FALSE); + break; + + case AIR_STEP_HIT_LAVA_WALL: + lava_boost_on_wall(m); + break; + } + + return FALSE; +} + +s32 act_top_of_pole_jump(struct MarioState *m) { + play_mario_jump_sound(m); + common_air_action_step(m, ACT_FREEFALL_LAND, MARIO_ANIM_HANDSTAND_JUMP, AIR_STEP_CHECK_LEDGE_GRAB); + return FALSE; +} + +s32 act_vertical_wind(struct MarioState *m) { + s16 intendedDYaw = m->intendedYaw - m->faceAngle[1]; + f32 intendedMag = m->intendedMag / 32.0f; + + play_sound_if_no_flag(m, SOUND_MARIO_HERE_WE_GO, MARIO_MARIO_SOUND_PLAYED); + if (m->actionState == 0) { + set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING_FLIP); + if (m->marioObj->header.gfx.animInfo.animFrame == 1) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); +#ifdef VERSION_SH + queue_rumble_data(8, 80); +#endif + } + + if (is_anim_past_end(m)) { + m->actionState = 1; + } + } else { + set_mario_animation(m, MARIO_ANIM_AIRBORNE_ON_STOMACH); + } + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_DIVE_SLIDE, 0); + break; + + case AIR_STEP_HIT_WALL: + mario_set_forward_vel(m, -16.0f); + break; + } + + m->marioObj->header.gfx.angle[0] = (s16)(6144.0f * intendedMag * coss(intendedDYaw)); + m->marioObj->header.gfx.angle[2] = (s16)(-4096.0f * intendedMag * sins(intendedDYaw)); + return FALSE; +} + +s32 act_special_triple_jump(struct MarioState *m) { + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_DIVE, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + play_mario_sound(m, SOUND_ACTION_TERRAIN_JUMP, SOUND_MARIO_YAHOO); + + update_air_without_turn(m); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + if (m->actionState++ == 0) { + m->vel[1] = 42.0f; + } else { + set_mario_action(m, ACT_FREEFALL_LAND_STOP, 0); + } + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + break; + + case AIR_STEP_HIT_WALL: + mario_bonk_reflection(m, TRUE); + break; + } + + if (m->actionState == 0 || m->vel[1] > 0.0f) { + if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + } else { + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + } + + m->particleFlags |= PARTICLE_SPARKLES; + return FALSE; +} + +s32 check_common_airborne_cancels(struct MarioState *m) { + if (m->pos[1] < m->waterLevel - 100) { + return set_water_plunge_action(m); + } + + if (m->input & INPUT_SQUISHED) { + return drop_and_set_mario_action(m, ACT_SQUISHED, 0); + } + + if (m->floor->type == SURFACE_VERTICAL_WIND && (m->action & ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION)) { + return drop_and_set_mario_action(m, ACT_VERTICAL_WIND, 0); + } + + m->quicksandDepth = 0.0f; + return FALSE; +} + +s32 mario_execute_airborne_action(struct MarioState *m) { + u32 cancel; + + if (check_common_airborne_cancels(m)) { + return TRUE; + } + + play_far_fall_sound(m); + + /* clang-format off */ + switch (m->action) { + case ACT_JUMP: cancel = act_jump(m); break; + case ACT_DOUBLE_JUMP: cancel = act_double_jump(m); break; + case ACT_FREEFALL: cancel = act_freefall(m); break; + case ACT_HOLD_JUMP: cancel = act_hold_jump(m); break; + case ACT_HOLD_FREEFALL: cancel = act_hold_freefall(m); break; + case ACT_SIDE_FLIP: cancel = act_side_flip(m); break; + case ACT_WALL_KICK_AIR: cancel = act_wall_kick_air(m); break; + case ACT_TWIRLING: cancel = act_twirling(m); break; + case ACT_WATER_JUMP: cancel = act_water_jump(m); break; + case ACT_HOLD_WATER_JUMP: cancel = act_hold_water_jump(m); break; + case ACT_STEEP_JUMP: cancel = act_steep_jump(m); break; + case ACT_BURNING_JUMP: cancel = act_burning_jump(m); break; + case ACT_BURNING_FALL: cancel = act_burning_fall(m); break; + case ACT_TRIPLE_JUMP: cancel = act_triple_jump(m); break; + case ACT_BACKFLIP: cancel = act_backflip(m); break; + case ACT_LONG_JUMP: cancel = act_long_jump(m); break; + case ACT_RIDING_SHELL_JUMP: + case ACT_RIDING_SHELL_FALL: cancel = act_riding_shell_air(m); break; + case ACT_DIVE: cancel = act_dive(m); break; + case ACT_AIR_THROW: cancel = act_air_throw(m); break; + case ACT_BACKWARD_AIR_KB: cancel = act_backward_air_kb(m); break; + case ACT_FORWARD_AIR_KB: cancel = act_forward_air_kb(m); break; + case ACT_HARD_FORWARD_AIR_KB: cancel = act_hard_forward_air_kb(m); break; + case ACT_HARD_BACKWARD_AIR_KB: cancel = act_hard_backward_air_kb(m); break; + case ACT_SOFT_BONK: cancel = act_soft_bonk(m); break; + case ACT_AIR_HIT_WALL: cancel = act_air_hit_wall(m); break; + case ACT_FORWARD_ROLLOUT: cancel = act_forward_rollout(m); break; + case ACT_SHOT_FROM_CANNON: cancel = act_shot_from_cannon(m); break; + case ACT_BUTT_SLIDE_AIR: cancel = act_butt_slide_air(m); break; + case ACT_HOLD_BUTT_SLIDE_AIR: cancel = act_hold_butt_slide_air(m); break; + case ACT_LAVA_BOOST: cancel = act_lava_boost(m); break; + case ACT_GETTING_BLOWN: cancel = act_getting_blown(m); break; + case ACT_BACKWARD_ROLLOUT: cancel = act_backward_rollout(m); break; + case ACT_CRAZY_BOX_BOUNCE: cancel = act_crazy_box_bounce(m); break; + case ACT_SPECIAL_TRIPLE_JUMP: cancel = act_special_triple_jump(m); break; + case ACT_GROUND_POUND: cancel = act_ground_pound(m); break; + case ACT_THROWN_FORWARD: cancel = act_thrown_forward(m); break; + case ACT_THROWN_BACKWARD: cancel = act_thrown_backward(m); break; + case ACT_FLYING_TRIPLE_JUMP: cancel = act_flying_triple_jump(m); break; + case ACT_SLIDE_KICK: cancel = act_slide_kick(m); break; + case ACT_JUMP_KICK: cancel = act_jump_kick(m); break; + case ACT_FLYING: cancel = act_flying(m); break; + case ACT_RIDING_HOOT: cancel = act_riding_hoot(m); break; + case ACT_TOP_OF_POLE_JUMP: cancel = act_top_of_pole_jump(m); break; + case ACT_VERTICAL_WIND: cancel = act_vertical_wind(m); break; + } + /* clang-format on */ + + return cancel; +} diff --git a/src/game/mario_actions_airborne.h b/src/game/mario_actions_airborne.h new file mode 100644 index 0000000..9c753b1 --- /dev/null +++ b/src/game/mario_actions_airborne.h @@ -0,0 +1,10 @@ +#ifndef MARIO_ACTIONS_AIRBORNE_H +#define MARIO_ACTIONS_AIRBORNE_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +s32 mario_execute_airborne_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_AIRBORNE_H diff --git a/src/game/mario_actions_automatic.c b/src/game/mario_actions_automatic.c new file mode 100644 index 0000000..4581267 --- /dev/null +++ b/src/game/mario_actions_automatic.c @@ -0,0 +1,896 @@ +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "../include/sm64.h" +//#include "behavior_data.h" +#include "mario_actions_automatic.h" +//#include "audio/external.h" +#include "area.h" +#include "mario.h" +#include "mario_step.h" +#include "../engine/math_util.h" +#include "memory.h" +#include "../engine/graph_node.h" +#include "save_file.h" +#include "../engine/surface_collision.h" +#include "interaction.h" +#include "camera.h" +//#include "level_table.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + +static Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f }; + +#define POLE_NONE 0 +#define POLE_TOUCHED_FLOOR 1 +#define POLE_FELL_OFF 2 + +#define HANG_NONE 0 +#define HANG_HIT_CEIL_OR_OOB 1 +#define HANG_LEFT_CEIL 2 + +void add_tree_leaf_particles(struct MarioState *m) { +// f32 leafHeight; + +// if (m->usedObj->behavior == segmented_to_virtual(bhvTree)) { +// // make leaf effect spawn higher on the Shifting Sand Land palm tree +// if (gCurrLevelNum == LEVEL_SSL) { +// leafHeight = 250.0f; +// } else { +// leafHeight = 100.0f; +// } +// if (m->pos[1] - m->floorHeight > leafHeight) { +// m->particleFlags |= PARTICLE_LEAF; +// } +// } +} + +void play_climbing_sounds(struct MarioState *m, s32 b) { +// s32 isOnTree = (m->usedObj->behavior == segmented_to_virtual(bhvTree)); + s32 isOnTree = FALSE; + + if (b == 1) { + if (is_anim_past_frame(m, 1)) { + play_sound(isOnTree ? SOUND_ACTION_CLIMB_UP_TREE : SOUND_ACTION_CLIMB_UP_POLE, + m->marioObj->header.gfx.cameraToObject); + } + } else { + play_sound(isOnTree ? SOUND_MOVING_SLIDE_DOWN_TREE : SOUND_MOVING_SLIDE_DOWN_POLE, + m->marioObj->header.gfx.cameraToObject); + } +} + +s32 set_pole_position(struct MarioState *m, f32 offsetY) { + UNUSED s32 unused1; + UNUSED s32 unused2; + UNUSED s32 unused3; + struct Surface *floor; + struct Surface *ceil; + f32 floorHeight; + f32 ceilHeight; + s32 collided; + s32 result = POLE_NONE; + f32 poleTop = m->usedObj->hitboxHeight - 100.0f; + struct Object *marioObj = m->marioObj; + + if (marioObj->oMarioPolePos > poleTop) { + marioObj->oMarioPolePos = poleTop; + } + + m->pos[0] = m->usedObj->oPosX; + m->pos[2] = m->usedObj->oPosZ; + m->pos[1] = m->usedObj->oPosY + marioObj->oMarioPolePos + offsetY; + + collided = f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 60.0f, 50.0f); + collided |= f32_find_wall_collision(&m->pos[0], &m->pos[1], &m->pos[2], 30.0f, 24.0f); + + ceilHeight = vec3f_find_ceil(m->pos, m->pos[1], &ceil); + if (m->pos[1] > ceilHeight - 160.0f) { + m->pos[1] = ceilHeight - 160.0f; + marioObj->oMarioPolePos = m->pos[1] - m->usedObj->oPosY; + } + + floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &floor); + if (m->pos[1] < floorHeight) { + m->pos[1] = floorHeight; + set_mario_action(m, ACT_IDLE, 0); + result = POLE_TOUCHED_FLOOR; + } else if (marioObj->oMarioPolePos < -m->usedObj->hitboxDownOffset) { + m->pos[1] = m->usedObj->oPosY - m->usedObj->hitboxDownOffset; + set_mario_action(m, ACT_FREEFALL, 0); + result = POLE_FELL_OFF; + } else if (collided) { + if (m->pos[1] > floorHeight + 20.0f) { + m->forwardVel = -2.0f; + set_mario_action(m, ACT_SOFT_BONK, 0); + result = POLE_FELL_OFF; + } else { + set_mario_action(m, ACT_IDLE, 0); + result = POLE_TOUCHED_FLOOR; + } + } + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, m->usedObj->oMoveAnglePitch, m->faceAngle[1], + m->usedObj->oMoveAngleRoll); + + return result; +} + +s32 act_holding_pole(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + +#ifdef VERSION_JP + if (m->input & INPUT_A_PRESSED) { + add_tree_leaf_particles(m); + m->faceAngle[1] += 0x8000; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + add_tree_leaf_particles(m); + m->forwardVel = -2.0f; + return set_mario_action(m, ACT_SOFT_BONK, 0); + } +#else + if ((m->input & INPUT_Z_PRESSED) || m->health < 0x100) { + add_tree_leaf_particles(m); + m->forwardVel = -2.0f; + return set_mario_action(m, ACT_SOFT_BONK, 0); + } + + if (m->input & INPUT_A_PRESSED) { + add_tree_leaf_particles(m); + m->faceAngle[1] += 0x8000; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } +#endif + + if (m->controller->stickY > 16.0f) { + f32 poleTop = m->usedObj->hitboxHeight - 100.0f; + const BehaviorScript *poleBehavior = virtual_to_segmented(0x13, m->usedObj->behavior); + + if (marioObj->oMarioPolePos < poleTop - 0.4f) { + return set_mario_action(m, ACT_CLIMBING_POLE, 0); + } + +// if (poleBehavior != bhvGiantPole && m->controller->stickY > 50.0f) { + if (m->controller->stickY > 50.0f) { + return set_mario_action(m, ACT_TOP_OF_POLE_TRANSITION, 0); + } + } + + if (m->controller->stickY < -16.0f) { + marioObj->oMarioPoleYawVel -= m->controller->stickY * 2; + if (marioObj->oMarioPoleYawVel > 0x1000) { + marioObj->oMarioPoleYawVel = 0x1000; + } + + m->faceAngle[1] += marioObj->oMarioPoleYawVel; + marioObj->oMarioPolePos -= marioObj->oMarioPoleYawVel / 0x100; + +// if (m->usedObj->behavior == segmented_to_virtual(bhvTree)) { +// //! The Shifting Sand Land palm tree check is done climbing up in +// // add_tree_leaf_particles, but not here, when climbing down. +// if (m->pos[1] - m->floorHeight > 100.0f) { +// m->particleFlags |= PARTICLE_LEAF; +// } +// } + play_climbing_sounds(m, 2); +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + func_80320A4C(1, marioObj->oMarioPoleYawVel / 0x100 * 2); + } else { + marioObj->oMarioPoleYawVel = 0; + m->faceAngle[1] -= m->controller->stickX * 16.0f; + } + + if (set_pole_position(m, 0.0f) == POLE_NONE) { + set_mario_animation(m, MARIO_ANIM_IDLE_ON_POLE); + } + + return FALSE; +} + +s32 act_climbing_pole(struct MarioState *m) { + s32 sp24; + struct Object *marioObj = m->marioObj; + s16 cameraAngle = m->area->camera->yaw; + +#ifndef VERSION_JP + if (m->health < 0x100) { + add_tree_leaf_particles(m); + m->forwardVel = -2.0f; + return set_mario_action(m, ACT_SOFT_BONK, 0); + } +#endif + + if (m->input & INPUT_A_PRESSED) { + add_tree_leaf_particles(m); + m->faceAngle[1] += 0x8000; + return set_mario_action(m, ACT_WALL_KICK_AIR, 0); + } + + if (m->controller->stickY < 8.0f) { + return set_mario_action(m, ACT_HOLDING_POLE, 0); + } + + marioObj->oMarioPolePos += m->controller->stickY / 8.0f; + marioObj->oMarioPoleYawVel = 0; + m->faceAngle[1] = cameraAngle - approach_s32((s16)(cameraAngle - m->faceAngle[1]), 0, 0x400, 0x400); + + if (set_pole_position(m, 0.0f) == POLE_NONE) { + sp24 = m->controller->stickY / 4.0f * 0x10000; + set_mario_anim_with_accel(m, MARIO_ANIM_CLIMB_UP_POLE, sp24); + add_tree_leaf_particles(m); + play_climbing_sounds(m, 1); + } + + return FALSE; +} + +s32 act_grab_pole_slow(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED); + + if (set_pole_position(m, 0.0f) == POLE_NONE) { + set_mario_animation(m, MARIO_ANIM_GRAB_POLE_SHORT); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_HOLDING_POLE, 0); + } + add_tree_leaf_particles(m); + } + + return FALSE; +} + +s32 act_grab_pole_fast(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED); + m->faceAngle[1] += marioObj->oMarioPoleYawVel; + marioObj->oMarioPoleYawVel = marioObj->oMarioPoleYawVel * 8 / 10; + + if (set_pole_position(m, 0.0f) == POLE_NONE) { + if (marioObj->oMarioPoleYawVel > 0x800) { + set_mario_animation(m, MARIO_ANIM_GRAB_POLE_SWING_PART1); + } else { + set_mario_animation(m, MARIO_ANIM_GRAB_POLE_SWING_PART2); + if (is_anim_at_end(m)) { + marioObj->oMarioPoleYawVel = 0; + set_mario_action(m, ACT_HOLDING_POLE, 0); + } + } + add_tree_leaf_particles(m); + } + + return FALSE; +} + +s32 act_top_of_pole_transition(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + marioObj->oMarioPoleYawVel = 0; + if (m->actionArg == 0) { + set_mario_animation(m, MARIO_ANIM_START_HANDSTAND); + if (is_anim_at_end(m)) { + return set_mario_action(m, ACT_TOP_OF_POLE, 0); + } + } else { + set_mario_animation(m, MARIO_ANIM_RETURN_FROM_HANDSTAND); + if (m->marioObj->header.gfx.animInfo.animFrame == 0) { + return set_mario_action(m, ACT_HOLDING_POLE, 0); + } + } + + set_pole_position(m, return_mario_anim_y_translation(m)); + return FALSE; +} + +s32 act_top_of_pole(struct MarioState *m) { + UNUSED struct Object *marioObj = m->marioObj; + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_TOP_OF_POLE_JUMP, 0); + } + if (m->controller->stickY < -16.0f) { + return set_mario_action(m, ACT_TOP_OF_POLE_TRANSITION, 1); + } + + m->faceAngle[1] -= m->controller->stickX * 16.0f; + + set_mario_animation(m, MARIO_ANIM_HANDSTAND_IDLE); + set_pole_position(m, return_mario_anim_y_translation(m)); + return FALSE; +} + +s32 perform_hanging_step(struct MarioState *m, Vec3f nextPos) { + UNUSED s32 unused; + struct Surface *ceil; + struct Surface *floor; + f32 ceilHeight; + f32 floorHeight; + f32 ceilOffset; + + m->wall = resolve_and_return_wall_collisions(nextPos, 50.0f, 50.0f); + floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); + ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + + if (floor == NULL) { + return HANG_HIT_CEIL_OR_OOB; + } + if (ceil == NULL) { + return HANG_LEFT_CEIL; + } + if (ceilHeight - floorHeight <= 160.0f) { + return HANG_HIT_CEIL_OR_OOB; + } + if (ceil->type != SURFACE_HANGABLE) { + return HANG_LEFT_CEIL; + } + + ceilOffset = ceilHeight - (nextPos[1] + 160.0f); + if (ceilOffset < -30.0f) { + return HANG_HIT_CEIL_OR_OOB; + } + if (ceilOffset > 30.0f) { + return HANG_LEFT_CEIL; + } + + nextPos[1] = m->ceilHeight - 160.0f; + vec3f_copy(m->pos, nextPos); + + m->floor = floor; + m->floorHeight = floorHeight; + m->ceil = ceil; + m->ceilHeight = ceilHeight; + + return HANG_NONE; +} + +s32 update_hang_moving(struct MarioState *m) { + s32 stepResult; + Vec3f nextPos; + f32 maxSpeed = 4.0f; + + m->forwardVel += 1.0f; + if (m->forwardVel > maxSpeed) { + m->forwardVel = maxSpeed; + } + + m->faceAngle[1] = + m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + + m->slideYaw = m->faceAngle[1]; + m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); + + m->vel[0] = m->slideVelX; + m->vel[1] = 0.0f; + m->vel[2] = m->slideVelZ; + + nextPos[0] = m->pos[0] - m->ceil->normal.y * m->vel[0]; + nextPos[2] = m->pos[2] - m->ceil->normal.y * m->vel[2]; + nextPos[1] = m->pos[1]; + + stepResult = perform_hanging_step(m, nextPos); + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + return stepResult; +} + +void update_hang_stationary(struct MarioState *m) { + m->forwardVel = 0.0f; + m->slideVelX = 0.0f; + m->slideVelZ = 0.0f; + + m->pos[1] = m->ceilHeight - 160.0f; + vec3f_copy(m->vel, gVec3fZero); + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); +} + +s32 act_start_hanging(struct MarioState *m) { +#ifdef VERSION_SH + if (m->actionTimer++ == 0) { + queue_rumble_data(5, 80); + } +#else + m->actionTimer++; +#endif + + if ((m->input & INPUT_NONZERO_ANALOG) && m->actionTimer >= 31) { + return set_mario_action(m, ACT_HANGING, 0); + } + + if (!(m->input & INPUT_A_DOWN)) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + //! Crash if Mario's referenced ceiling is NULL (same for other hanging actions) + if (m->ceil->type != SURFACE_HANGABLE) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + set_mario_animation(m, MARIO_ANIM_HANG_ON_CEILING); + play_sound_if_no_flag(m, SOUND_ACTION_HANGING_STEP, MARIO_ACTION_SOUND_PLAYED); + update_hang_stationary(m); + + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_HANGING, 0); + } + + return FALSE; +} + +s32 act_hanging(struct MarioState *m) { + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HANG_MOVING, m->actionArg); + } + + if (!(m->input & INPUT_A_DOWN)) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + if (m->ceil->type != SURFACE_HANGABLE) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->actionArg & 1) { + set_mario_animation(m, MARIO_ANIM_HANDSTAND_LEFT); + } else { + set_mario_animation(m, MARIO_ANIM_HANDSTAND_RIGHT); + } + + update_hang_stationary(m); + + return FALSE; +} + +s32 act_hang_moving(struct MarioState *m) { + if (!(m->input & INPUT_A_DOWN)) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_GROUND_POUND, 0); + } + + if (m->ceil->type != SURFACE_HANGABLE) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->actionArg & 1) { + set_mario_animation(m, MARIO_ANIM_MOVE_ON_WIRE_NET_RIGHT); + } else { + set_mario_animation(m, MARIO_ANIM_MOVE_ON_WIRE_NET_LEFT); + } + + if (m->marioObj->header.gfx.animInfo.animFrame == 12) { + play_sound(SOUND_ACTION_HANGING_STEP, m->marioObj->header.gfx.cameraToObject); +#ifdef VERSION_SH + queue_rumble_data(5, 30); +#endif + } + + if (is_anim_past_end(m)) { + m->actionArg ^= 1; + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_HANGING, m->actionArg); + } + } + + if (update_hang_moving(m) == HANG_LEFT_CEIL) { + set_mario_action(m, ACT_FREEFALL, 0); + } + + return FALSE; +} + +s32 let_go_of_ledge(struct MarioState *m) { + f32 floorHeight; + struct Surface *floor; + + m->vel[1] = 0.0f; + m->forwardVel = -8.0f; + m->pos[0] -= 60.0f * sins(m->faceAngle[1]); + m->pos[2] -= 60.0f * coss(m->faceAngle[1]); + + floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &floor); + if (floorHeight < m->pos[1] - 100.0f) { + m->pos[1] -= 100.0f; + } else { + m->pos[1] = floorHeight; + } + + return set_mario_action(m, ACT_SOFT_BONK, 0); +} + +void climb_up_ledge(struct MarioState *m) { + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_LEFT); + m->pos[0] += 14.0f * sins(m->faceAngle[1]); + m->pos[2] += 14.0f * coss(m->faceAngle[1]); + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); +} + +void update_ledge_climb_camera(struct MarioState *m) { + f32 sp4; + + if (m->actionTimer < 14) { + sp4 = m->actionTimer; + } else { + sp4 = 14.0f; + } + m->statusForCamera->pos[0] = m->pos[0] + sp4 * sins(m->faceAngle[1]); + m->statusForCamera->pos[2] = m->pos[2] + sp4 * coss(m->faceAngle[1]); + m->statusForCamera->pos[1] = m->pos[1]; + m->actionTimer++; + m->flags |= MARIO_UNKNOWN_25; +} + +void update_ledge_climb(struct MarioState *m, s32 animation, u32 endAction) { + stop_and_set_height_to_floor(m); + + set_mario_animation(m, animation); + if (is_anim_at_end(m)) { + set_mario_action(m, endAction, 0); + if (endAction == ACT_IDLE) { + climb_up_ledge(m); + } + } +} + +s32 act_ledge_grab(struct MarioState *m) { + f32 heightAboveFloor; + s16 intendedDYaw = m->intendedYaw - m->faceAngle[1]; + s32 hasSpaceForMario = (m->ceilHeight - m->floorHeight >= 160.0f); + + if (m->actionTimer < 10) { + m->actionTimer++; + } + + if (m->floor->normal.y < 0.9063078f) { + return let_go_of_ledge(m); + } + + if (m->input & (INPUT_Z_PRESSED | INPUT_OFF_FLOOR)) { + return let_go_of_ledge(m); + } + + if ((m->input & INPUT_A_PRESSED) && hasSpaceForMario) { + return set_mario_action(m, ACT_LEDGE_CLIMB_FAST, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK1) { + m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18; + } + return let_go_of_ledge(m); + } +#ifdef VERSION_EU + // On EU, you can't slow climb up ledges while holding A. + if (m->actionTimer == 10 && (m->input & INPUT_NONZERO_ANALOG) && !(m->input & INPUT_A_DOWN)) +#else + if (m->actionTimer == 10 && (m->input & INPUT_NONZERO_ANALOG)) +#endif + { + if (intendedDYaw >= -0x4000 && intendedDYaw <= 0x4000) { + if (hasSpaceForMario) { + return set_mario_action(m, ACT_LEDGE_CLIMB_SLOW_1, 0); + } + } else { + return let_go_of_ledge(m); + } + } + + heightAboveFloor = m->pos[1] - find_floor_height_relative_polar(m, -0x8000, 30.0f); + if (hasSpaceForMario && heightAboveFloor < 100.0f) { + return set_mario_action(m, ACT_LEDGE_CLIMB_FAST, 0); + } + + if (m->actionArg == 0) { + play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED); + } + + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_IDLE_ON_LEDGE); + + return FALSE; +} + +s32 act_ledge_climb_slow(struct MarioState *m) { + if (m->input & INPUT_OFF_FLOOR) { + return let_go_of_ledge(m); + } + + if (m->actionTimer >= 28 + && (m->input + & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE))) { + climb_up_ledge(m); + return check_common_action_exits(m); + } + + if (m->actionTimer == 10) { + play_sound_if_no_flag(m, SOUND_MARIO_EEUH, MARIO_MARIO_SOUND_PLAYED); + } + + update_ledge_climb(m, MARIO_ANIM_SLOW_LEDGE_GRAB, ACT_IDLE); + + update_ledge_climb_camera(m); + if (m->marioObj->header.gfx.animInfo.animFrame == 17) { + m->action = ACT_LEDGE_CLIMB_SLOW_2; + } + + return FALSE; +} + +s32 act_ledge_climb_down(struct MarioState *m) { + if (m->input & INPUT_OFF_FLOOR) { + return let_go_of_ledge(m); + } + + play_sound_if_no_flag(m, SOUND_MARIO_WHOA, MARIO_MARIO_SOUND_PLAYED); + + update_ledge_climb(m, MARIO_ANIM_CLIMB_DOWN_LEDGE, ACT_LEDGE_GRAB); + m->actionArg = 1; + + return FALSE; +} + +s32 act_ledge_climb_fast(struct MarioState *m) { + if (m->input & INPUT_OFF_FLOOR) { + return let_go_of_ledge(m); + } + + play_sound_if_no_flag(m, SOUND_MARIO_UH2, MARIO_MARIO_SOUND_PLAYED); + + update_ledge_climb(m, MARIO_ANIM_FAST_LEDGE_GRAB, ACT_IDLE); + + if (m->marioObj->header.gfx.animInfo.animFrame == 8) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + update_ledge_climb_camera(m); + + return FALSE; +} + +s32 act_grabbed(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK2) { + s32 thrown = (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK6) == 0; + + m->faceAngle[1] = m->usedObj->oMoveAngleYaw; + vec3f_copy(m->pos, m->marioObj->header.gfx.pos); +#ifdef VERSION_SH + queue_rumble_data(5, 60); +#endif + + return set_mario_action(m, (m->forwardVel >= 0.0f) ? ACT_THROWN_FORWARD : ACT_THROWN_BACKWARD, + thrown); + } + + set_mario_animation(m, MARIO_ANIM_BEING_GRABBED); + return FALSE; +} + +s32 act_in_cannon(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + s16 startFacePitch = m->faceAngle[0]; + s16 startFaceYaw = m->faceAngle[1]; + + switch (m->actionState) { + case 0: + m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + m->usedObj->oInteractStatus = INT_STATUS_INTERACTED; + + m->statusForCamera->cameraEvent = CAM_EVENT_CANNON; + m->statusForCamera->usedObj = m->usedObj; + + vec3f_set(m->vel, 0.0f, 0.0f, 0.0f); + + m->pos[0] = m->usedObj->oPosX; + m->pos[1] = m->usedObj->oPosY + 350.0f; + m->pos[2] = m->usedObj->oPosZ; + + m->forwardVel = 0.0f; + + m->actionState = 1; + break; + + case 1: + if (m->usedObj->oAction == 1) { + m->faceAngle[0] = m->usedObj->oMoveAnglePitch; + m->faceAngle[1] = m->usedObj->oMoveAngleYaw; + + marioObj->oMarioCannonObjectYaw = m->usedObj->oMoveAngleYaw; + marioObj->oMarioCannonInputYaw = 0; + + m->actionState = 2; + } + break; + + case 2: + m->faceAngle[0] -= (s16)(m->controller->stickY * 10.0f); + marioObj->oMarioCannonInputYaw -= (s16)(m->controller->stickX * 10.0f); + + if (m->faceAngle[0] > 0x38E3) { + m->faceAngle[0] = 0x38E3; + } + if (m->faceAngle[0] < 0) { + m->faceAngle[0] = 0; + } + + if (marioObj->oMarioCannonInputYaw > 0x4000) { + marioObj->oMarioCannonInputYaw = 0x4000; + } + if (marioObj->oMarioCannonInputYaw < -0x4000) { + marioObj->oMarioCannonInputYaw = -0x4000; + } + + m->faceAngle[1] = marioObj->oMarioCannonObjectYaw + marioObj->oMarioCannonInputYaw; + if (m->input & INPUT_A_PRESSED) { + m->forwardVel = 100.0f * coss(m->faceAngle[0]); + + m->vel[1] = 100.0f * sins(m->faceAngle[0]); + + m->pos[0] += 120.0f * coss(m->faceAngle[0]) * sins(m->faceAngle[1]); + m->pos[1] += 120.0f * sins(m->faceAngle[0]); + m->pos[2] += 120.0f * coss(m->faceAngle[0]) * coss(m->faceAngle[1]); + + play_sound(SOUND_ACTION_FLYING_FAST, m->marioObj->header.gfx.cameraToObject); + play_sound(SOUND_OBJ_POUNDING_CANNON, m->marioObj->header.gfx.cameraToObject); + + m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + + set_mario_action(m, ACT_SHOT_FROM_CANNON, 0); +#ifdef VERSION_SH + queue_rumble_data(60, 70); +#endif + m->usedObj->oAction = 2; + return FALSE; + } else if (m->faceAngle[0] != startFacePitch || m->faceAngle[1] != startFaceYaw) { + play_sound(SOUND_MOVING_AIM_CANNON, m->marioObj->header.gfx.cameraToObject); +#ifdef VERSION_SH + reset_rumble_timers_2(0); +#endif + } + } + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + set_mario_animation(m, MARIO_ANIM_DIVE); + + return FALSE; +} + +s32 act_tornado_twirling(struct MarioState *m) { + struct Surface *floor; + Vec3f nextPos; + f32 sinAngleVel; + f32 cosAngleVel; + f32 floorHeight; + struct Object *marioObj = m->marioObj; + struct Object *usedObj = m->usedObj; + s16 prevTwirlYaw = m->twirlYaw; + + f32 dx = (m->pos[0] - usedObj->oPosX) * 0.95f; + f32 dz = (m->pos[2] - usedObj->oPosZ) * 0.95f; + + if (m->vel[1] < 60.0f) { + m->vel[1] += 1.0f; + } + + if ((marioObj->oMarioTornadoPosY += m->vel[1]) < 0.0f) { + marioObj->oMarioTornadoPosY = 0.0f; + } + if (marioObj->oMarioTornadoPosY > usedObj->hitboxHeight) { + if (m->vel[1] < 20.0f) { + m->vel[1] = 20.0f; + } + return set_mario_action(m, ACT_TWIRLING, 1); + } + + if (m->angleVel[1] < 0x3000) { + m->angleVel[1] += 0x100; + } + + if (marioObj->oMarioTornadoYawVel < 0x1000) { + marioObj->oMarioTornadoYawVel += 0x100; + } + + m->twirlYaw += m->angleVel[1]; + + sinAngleVel = sins(marioObj->oMarioTornadoYawVel); + cosAngleVel = coss(marioObj->oMarioTornadoYawVel); + + nextPos[0] = usedObj->oPosX + dx * cosAngleVel + dz * sinAngleVel; + nextPos[2] = usedObj->oPosZ - dx * sinAngleVel + dz * cosAngleVel; + nextPos[1] = usedObj->oPosY + marioObj->oMarioTornadoPosY; + + f32_find_wall_collision(&nextPos[0], &nextPos[1], &nextPos[2], 60.0f, 50.0f); + + floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); + if (floor != NULL) { + m->floor = floor; + m->floorHeight = floorHeight; + vec3f_copy(m->pos, nextPos); + } else { + if (nextPos[1] >= m->floorHeight) { + m->pos[1] = nextPos[1]; + } else { + m->pos[1] = m->floorHeight; + } + } + + m->actionTimer++; + + set_mario_animation(m, (m->actionArg == 0) ? MARIO_ANIM_START_TWIRL : MARIO_ANIM_TWIRL); + + if (is_anim_past_end(m)) { + m->actionArg = 1; + } + + // Play sound on angle overflow + if (prevTwirlYaw > m->twirlYaw) { + play_sound(SOUND_ACTION_TWIRL, m->marioObj->header.gfx.cameraToObject); + } + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1] + m->twirlYaw, 0); +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + + return FALSE; +} + +s32 check_common_automatic_cancels(struct MarioState *m) { + if (m->pos[1] < m->waterLevel - 100) { + return set_water_plunge_action(m); + } + + return FALSE; +} + +s32 mario_execute_automatic_action(struct MarioState *m) { + s32 cancel; + + if (check_common_automatic_cancels(m)) { + return TRUE; + } + + m->quicksandDepth = 0.0f; + + /* clang-format off */ + switch (m->action) { + case ACT_HOLDING_POLE: cancel = act_holding_pole(m); break; + case ACT_GRAB_POLE_SLOW: cancel = act_grab_pole_slow(m); break; + case ACT_GRAB_POLE_FAST: cancel = act_grab_pole_fast(m); break; + case ACT_CLIMBING_POLE: cancel = act_climbing_pole(m); break; + case ACT_TOP_OF_POLE_TRANSITION: cancel = act_top_of_pole_transition(m); break; + case ACT_TOP_OF_POLE: cancel = act_top_of_pole(m); break; + case ACT_START_HANGING: cancel = act_start_hanging(m); break; + case ACT_HANGING: cancel = act_hanging(m); break; + case ACT_HANG_MOVING: cancel = act_hang_moving(m); break; + case ACT_LEDGE_GRAB: cancel = act_ledge_grab(m); break; + case ACT_LEDGE_CLIMB_SLOW_1: cancel = act_ledge_climb_slow(m); break; + case ACT_LEDGE_CLIMB_SLOW_2: cancel = act_ledge_climb_slow(m); break; + case ACT_LEDGE_CLIMB_DOWN: cancel = act_ledge_climb_down(m); break; + case ACT_LEDGE_CLIMB_FAST: cancel = act_ledge_climb_fast(m); break; + case ACT_GRABBED: cancel = act_grabbed(m); break; + case ACT_IN_CANNON: cancel = act_in_cannon(m); break; + case ACT_TORNADO_TWIRLING: cancel = act_tornado_twirling(m); break; + } + /* clang-format on */ + + return cancel; +} diff --git a/src/game/mario_actions_automatic.h b/src/game/mario_actions_automatic.h new file mode 100644 index 0000000..6be471c --- /dev/null +++ b/src/game/mario_actions_automatic.h @@ -0,0 +1,10 @@ +#ifndef MARIO_ACTIONS_AUTOMATIC_H +#define MARIO_ACTIONS_AUTOMATIC_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +s32 mario_execute_automatic_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_AUTOMATIC_H diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c new file mode 100644 index 0000000..bf247e7 --- /dev/null +++ b/src/game/mario_actions_cutscene.c @@ -0,0 +1,2731 @@ + +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +//#include "prevent_bss_reordering.h" +#include "../include/sm64.h" +#include "area.h" +//#include "audio/external.h" +//#include "behavior_data.h" +#include "camera.h" +//#include "dialog_ids.h" +//#include "engine/behavior_script.h" +#include "../engine/graph_node.h" +#include "../engine/math_util.h" +#include "../engine/surface_collision.h" +//#include "game_init.h" +//#include "gfx_dimensions.h" +//#include "ingame_menu.h" +#include "interaction.h" +//#include "level_table.h" +#include "level_update.h" +#include "mario.h" +#include "mario_actions_moving.h" +#include "mario_step.h" +//#include "moving_texture.h" +//#include "object_helpers.h" +//#include "object_list_processor.h" +#include "save_file.h" +#include "../include/seq_ids.h" +//#include "sound_init.h" +//#include "thread6.h" +#include "../include/PR/os_cont.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + + + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 + +#define GFX_DIMENSIONS_FROM_LEFT_EDGE(v) (v) +#define GFX_DIMENSIONS_FROM_RIGHT_EDGE(v) (SCREEN_WIDTH - (v)) +#define GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(v) (v) +#define GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(v) (SCREEN_WIDTH - (v)) +#define GFX_DIMENSIONS_ASPECT_RATIO (4.0f / 3.0f) + +// If screen is taller than it is wide, radius should be equal to SCREEN_HEIGHT since we scale horizontally +#define GFX_DIMENSIONS_FULL_RADIUS (SCREEN_HEIGHT * (GFX_DIMENSIONS_ASPECT_RATIO > 1 ? GFX_DIMENSIONS_ASPECT_RATIO : 1)) + + + + + + +// TODO: put this elsewhere +enum SaveOption { SAVE_OPT_SAVE_AND_CONTINUE = 1, SAVE_OPT_SAVE_AND_QUIT, SAVE_OPT_CONTINUE_DONT_SAVE }; + +static struct Object *sIntroWarpPipeObj; +static struct Object *sEndPeachObj; +static struct Object *sEndRightToadObj; +static struct Object *sEndLeftToadObj; +static struct Object *sEndJumboStarObj; +static UNUSED s32 sUnused; +static s16 sEndPeachAnimation; +static s16 sEndToadAnims[2]; + +static Vp sEndCutsceneVp = { { { 640, 480, 511, 0 }, { 640, 480, 511, 0 } } }; +static struct CreditsEntry *sDispCreditsEntry = NULL; + +// related to peach gfx? +static s8 D_8032CBE4 = 0; +static s8 D_8032CBE8 = 0; +static s8 D_8032CBEC[7] = { 2, 3, 2, 1, 2, 3, 2 }; + +static u8 sStarsNeededForDialog[] = { 1, 3, 8, 30, 50, 70 }; + +/** + * Data for the jumbo star cutscene. It specifies the flight path after triple + * jumping. Each entry is one keyframe. + * The first number is playback speed, 1000 is the maximum and means it lasts + * 1 frame. 20 means that it lasts 1000/20 = 50 frames. + * Speed 0 marks the last keyframe. Since the cubic spline looks 3 keyframes + * ahead, there should be at least 2 more entries afterwards. + * The last three numbers of each entry are x, y and z coordinates of points + * that define the curve. + */ +static Vec4s sJumboStarKeyframes[27] = { + { 20, 0, 678, -2916 }, { 30, 0, 680, -3500 }, { 40, 1000, 700, -4000 }, + { 50, 2500, 750, -3500 }, { 50, 3500, 800, -2000 }, { 50, 4000, 850, 0 }, + { 50, 3500, 900, 2000 }, { 50, 2000, 950, 3500 }, { 50, 0, 1000, 4000 }, + { 50, -2000, 1050, 3500 }, { 50, -3500, 1100, 2000 }, { 50, -4000, 1150, 0 }, + { 50, -3500, 1200, -2000 }, { 50, -2000, 1250, -3500 }, { 50, 0, 1300, -4000 }, + { 50, 2000, 1350, -3500 }, { 50, 3500, 1400, -2000 }, { 50, 4000, 1450, 0 }, + { 50, 3500, 1500, 2000 }, { 50, 2000, 1600, 3500 }, { 50, 0, 1700, 4000 }, + { 50, -2000, 1800, 3500 }, { 50, -3500, 1900, 2000 }, { 30, -4000, 2000, 0 }, + { 0, -3500, 2100, -2000 }, { 0, -2000, 2200, -3500 }, { 0, 0, 2300, -4000 }, +}; + +/** + * get_credits_str_width: Calculate width of a Credits String + * Loop over each character in a credits string and increment the length. If the + * character is a space, increment by 4; otherwise increment by 7. Once the next + * character is a null character (equal to 0), stop counting the length since + * that's the end of the string. + */ +s32 get_credits_str_width(char *str) { + u32 c; + s32 length = 0; + + while ((c = *str++) != 0) { + length += (c == ' ' ? 4 : 7); + } + + return length; +} + +#define CREDIT_TEXT_MARGIN_X ((s32)(GFX_DIMENSIONS_ASPECT_RATIO * 21)) +#define CREDIT_TEXT_X_LEFT GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(CREDIT_TEXT_MARGIN_X) +#define CREDIT_TEXT_X_RIGHT GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(CREDIT_TEXT_MARGIN_X) + +/** + * print_displaying_credits_entry: Print the current displaying Credits Entry + * Called in render_game. This function checks if sDispCreditsEntry points to a + * credits entry (see act_credits_cutscene), and if so, display it. The reason + * this is called every frame in render_game is because the credits need to + * display on top of everything else. + * To print a credits entry, we take the first character of the first string, + * subtract the value of the 0 character, and use that as the number of lines to + * print, excluding the title. Then, we print the title (after advancing the + * pointer by 1) at X 28, Y either 28 or 172, depending on the print at bottom + * flag. Finally, we print each line using the number of lines previously stored + * as a counter, and increase the Y value by either the constant 16 (JP only) or + * by the value of lineHeight. + */ +void print_displaying_credits_entry(void) { +// char **currStrPtr; +// char *titleStr; +// s16 numLines; +// s16 strY; +//#ifndef VERSION_JP +// s16 lineHeight; +//#endif +// +// if (sDispCreditsEntry != NULL) { +// currStrPtr = (char **) sDispCreditsEntry->unk0C; +// titleStr = *currStrPtr++; +// numLines = *titleStr++ - '0'; +// +// strY = (sDispCreditsEntry->unk02 & 0x20 ? 28 : 172) + (numLines == 1) * 16; +//#ifndef VERSION_JP +// lineHeight = 16; +//#endif +// +// dl_rgba16_begin_cutscene_msg_fade(); +// print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY, titleStr); +// +//#ifndef VERSION_JP +// switch (numLines) { +// case 4: +// print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 24, *currStrPtr++); +// numLines = 2; +// lineHeight = 24; +// break; +// case 5: +// print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++); +// numLines = 3; +// break; +//#ifdef VERSION_EU +// case 6: +// print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++); +// numLines = 3; +// break; +// case 7: +// print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 16, *currStrPtr++); +// print_credits_str_ascii(CREDIT_TEXT_X_LEFT, strY + 32, *currStrPtr++); +// numLines = 3; +// break; +//#endif +// } +//#endif +// +// while (numLines-- > 0) { +// print_credits_str_ascii(CREDIT_TEXT_X_RIGHT - get_credits_str_width(*currStrPtr), strY, *currStrPtr); +// +//#ifdef VERSION_JP +// strY += 16; +//#else +// strY += lineHeight; +//#endif +// +// currStrPtr++; +// } +// +//// dl_rgba16_stop_cutscene_msg_fade(); +// sDispCreditsEntry = NULL; +// } +} + +void bhv_end_peach_loop(void) { +// cur_obj_init_animation_with_sound(sEndPeachAnimation); +// if (cur_obj_check_if_near_animation_end()) { +// // anims: 0-3, 4, 5, 6-8, 9, 10, 11 +// if (sEndPeachAnimation < 3 || sEndPeachAnimation == 6 || sEndPeachAnimation == 7) { +// sEndPeachAnimation++; +// } +// } +} + +void bhv_end_toad_loop(void) { +// s32 toadAnimIndex = (gCurrentObject->oPosX >= 0.0f); + +// cur_obj_init_animation_with_sound(sEndToadAnims[toadAnimIndex]); +// if (cur_obj_check_if_near_animation_end()) { +// // 0-1, 2-3, 4, 5, 6, 7 +// if (sEndToadAnims[toadAnimIndex] == 0 || sEndToadAnims[toadAnimIndex] == 2) { +// sEndToadAnims[toadAnimIndex]++; +// } +// } +} + +// Geo switch case function for controlling Peach's eye state. +s32 geo_switch_peach_eyes(s32 run, struct GraphNode *node, UNUSED s32 a2) { +// struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; +// s16 timer; + +// if (run == TRUE) { +// if (D_8032CBE4 == 0) { +// timer = (gAreaUpdateCounter + 0x20) >> 1 & 0x1F; +// if (timer < 7) { +// switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBEC[timer]; +// } else { +// switchCase->selectedCase = D_8032CBE8 * 4 + 1; +// } +// } else { +// switchCase->selectedCase = D_8032CBE8 * 4 + D_8032CBE4 - 1; +// } +// } + +// return 0; +} + +// unused +static void stub_is_textbox_active(u16 *a0) { +// if (get_dialog_id() == -1) { +// *a0 = 0; +// } +} + +/** + * get_star_collection_dialog: Determine what dialog should show when Mario + * collects a star. + * Determines if Mario has collected enough stars to get a dialog for it, and + * if so, return the dialog ID. Otherwise, return 0. A dialog is returned if + * numStars has reached a milestone and prevNumStarsForDialog has not reached it. + */ +s32 get_star_collection_dialog(struct MarioState *m) { +// s32 i; +// s32 dialogID = 0; +// s32 numStarsRequired; + +// for (i = 0; i < ARRAY_COUNT(sStarsNeededForDialog); i++) { +// numStarsRequired = sStarsNeededForDialog[i]; +// if (m->prevNumStarsForDialog < numStarsRequired && m->numStars >= numStarsRequired) { +// dialogID = i + DIALOG_141; +// break; +// } +// } + +// m->prevNumStarsForDialog = m->numStars; +// return dialogID; +} + +// save menu handler +void handle_save_menu(struct MarioState *m) { +// s32 dialogID; +// // wait for the menu to show up +// if (is_anim_past_end(m) && gSaveOptSelectIndex != 0) { +// // save and continue / save and quit +// if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_CONTINUE || gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) { +// save_file_do_save(gCurrSaveFileNum - 1); + +// if (gSaveOptSelectIndex == SAVE_OPT_SAVE_AND_QUIT) { +// fade_into_special_warp(-2, 0); // reset game +// } +// } + +// // not quitting +// if (gSaveOptSelectIndex != SAVE_OPT_SAVE_AND_QUIT) { +// disable_time_stop(); +// m->faceAngle[1] += 0x8000; +// // figure out what dialog to show, if we should +// dialogID = get_star_collection_dialog(m); +// if (dialogID != 0) { +// play_peachs_jingle(); +// // look up for dialog +// set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID); +// } else { +// set_mario_action(m, ACT_IDLE, 0); +// } +// } +// } +} + +/** + * spawn_obj_at_mario_rel_yaw: Spawns object at Mario with relative yaw. + * Spawns object with given behavior and model and copies over Mario's position + * and yaw plus relative yaw. + */ +struct Object *spawn_obj_at_mario_rel_yaw(struct MarioState *m, s32 model, const BehaviorScript *behavior, s16 relYaw) { +// struct Object *o = spawn_object(m->marioObj, model, behavior); + +// o->oFaceAngleYaw = m->faceAngle[1] + relYaw; +// o->oPosX = m->pos[0]; +// o->oPosY = m->pos[1]; +// o->oPosZ = m->pos[2]; + +// return o; +} + +/** + * cutscene_take_cap_off: Put Mario's cap on. + * Clears "cap on head" flag, sets "cap in hand" flag, plays sound + * SOUND_ACTION_UNKNOWN43D. + */ +void cutscene_take_cap_off(struct MarioState *m) { + m->flags &= ~MARIO_CAP_ON_HEAD; + m->flags |= MARIO_CAP_IN_HAND; + play_sound(SOUND_ACTION_UNKNOWN43D, m->marioObj->header.gfx.cameraToObject); +} + +/** + * cutscene_put_cap_on: Put Mario's cap on. + * Clears "cap in hand" flag, sets "cap on head" flag, plays sound + * SOUND_ACTION_UNKNOWN43E. + */ +void cutscene_put_cap_on(struct MarioState *m) { + m->flags &= ~MARIO_CAP_IN_HAND; + m->flags |= MARIO_CAP_ON_HEAD; + play_sound(SOUND_ACTION_UNKNOWN43E, m->marioObj->header.gfx.cameraToObject); +} + +/** + * mario_ready_to_speak: Determine if Mario is able to speak to a NPC + * The following conditions must be met in order for Mario to be considered + * ready to speak. + * 1: Mario's action must be in the stationary or moving action groups, or if + * not, he must be in the "waiting for dialog" state. + * 2: Mario mat not be riding a shell or be invulnerable. + * 3: Mario must not be in first person mode. + */ +s32 mario_ready_to_speak(void) { + u32 actionGroup = gMarioState->action & ACT_GROUP_MASK; + s32 isReadyToSpeak = FALSE; + + if ((gMarioState->action == ACT_WAITING_FOR_DIALOG || actionGroup == ACT_GROUP_STATIONARY + || actionGroup == ACT_GROUP_MOVING) + && (!(gMarioState->action & (ACT_FLAG_RIDING_SHELL | ACT_FLAG_INVULNERABLE)) + && gMarioState->action != ACT_FIRST_PERSON)) { + isReadyToSpeak = TRUE; + } + + return isReadyToSpeak; +} + +// (can) place Mario in dialog? +// initiate dialog? +// return values: +// 0 = not in dialog +// 1 = starting dialog +// 2 = speaking +s32 set_mario_npc_dialog(s32 actionArg) { + s32 dialogState = 0; + + // in dialog + if (gMarioState->action == ACT_READING_NPC_DIALOG) { + if (gMarioState->actionState < 8) { + dialogState = 1; // starting dialog + } + if (gMarioState->actionState == 8) { + if (actionArg == 0) { + gMarioState->actionState++; // exit dialog + } else { + dialogState = 2; + } + } + } else if (actionArg != 0 && mario_ready_to_speak()) { + gMarioState->usedObj = gCurrentObject; + set_mario_action(gMarioState, ACT_READING_NPC_DIALOG, actionArg); + dialogState = 1; // starting dialog + } + + return dialogState; +} + +// actionargs: +// 1 : no head turn +// 2 : look up +// 3 : look down +// actionstate values: +// 0 - 7: looking toward npc +// 8: in dialog +// 9 - 22: looking away from npc +// 23: end +s32 act_reading_npc_dialog(struct MarioState *m) { + s32 headTurnAmount = 0; + s16 angleToNPC; + + if (m->actionArg == 2) { + headTurnAmount = -1024; + } + if (m->actionArg == 3) { + headTurnAmount = 384; + } + + if (m->actionState < 8) { + // turn to NPC + angleToNPC = mario_obj_angle_to_object(m, m->usedObj); + m->faceAngle[1] = + angleToNPC - approach_s32((angleToNPC - m->faceAngle[1]) << 16 >> 16, 0, 2048, 2048); + // turn head to npc + m->actionTimer += headTurnAmount; + // set animation + set_mario_animation(m, m->heldObj == NULL ? MARIO_ANIM_FIRST_PERSON + : MARIO_ANIM_IDLE_WITH_LIGHT_OBJ); + } else if (m->actionState >= 9 && m->actionState < 17) { + // look back from facing NPC + m->actionTimer -= headTurnAmount; + } else if (m->actionState == 23) { + if (m->flags & MARIO_CAP_IN_HAND) { + set_mario_action(m, ACT_PUTTING_ON_CAP, 0); + } else { + set_mario_action(m, m->heldObj == NULL ? ACT_IDLE : ACT_HOLD_IDLE, 0); + } + } + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0); + + if (m->actionState != 8) { + m->actionState++; + } + + return FALSE; +} + +// puts Mario in a state where he's waiting for (npc) dialog; doesn't do much +s32 act_waiting_for_dialog(struct MarioState *m) { + set_mario_animation(m, m->heldObj == NULL ? MARIO_ANIM_FIRST_PERSON + : MARIO_ANIM_IDLE_WITH_LIGHT_OBJ); + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + return FALSE; +} + +// makes Mario disappear and triggers warp +s32 act_disappeared(struct MarioState *m) { + set_mario_animation(m, MARIO_ANIM_A_POSE); + stop_and_set_height_to_floor(m); + m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + if (m->actionArg) { + m->actionArg--; + if ((m->actionArg & 0xFFFF) == 0) { + level_trigger_warp(m, m->actionArg >> 16); + } + } + return FALSE; +} + +s32 act_reading_automatic_dialog(struct MarioState *m) { +// u32 actionArg; +// +// m->actionState++; +// if (m->actionState == 2) { +// enable_time_stop(); +// } +// if (m->actionState < 9) { +// set_mario_animation(m, m->prevAction == ACT_STAR_DANCE_WATER ? MARIO_ANIM_WATER_IDLE +// : MARIO_ANIM_FIRST_PERSON); +// // always look up for automatic dialogs +// m->actionTimer -= 1024; +// } else { +// // set Mario dialog +// if (m->actionState == 9) { +// actionArg = m->actionArg; +// if (GET_HIGH_U16_OF_32(actionArg) == 0) { +// create_dialog_box(GET_LOW_U16_OF_32(actionArg)); +// } else { +// create_dialog_box_with_var(GET_HIGH_U16_OF_32(actionArg), GET_LOW_U16_OF_32(actionArg)); +// } +// } +// // wait until dialog is done +// else if (m->actionState == 10) { +// if (get_dialog_id() >= 0) { +// m->actionState--; +// } +// } +// // look back down +// else if (m->actionState < 19) { +// m->actionTimer += 1024; +// } +// // finished action +// else if (m->actionState == 25) { +// disable_time_stop(); +// if (gNeverEnteredCastle) { +// gNeverEnteredCastle = FALSE; +// play_cutscene_music(SEQUENCE_ARGS(0, SEQ_LEVEL_INSIDE_CASTLE)); +// } +// if (m->prevAction == ACT_STAR_DANCE_WATER) { +// set_mario_action(m, ACT_WATER_IDLE, 0); // 100c star? +// } else { +// // make Mario walk into door after star dialog +// set_mario_action(m, m->prevAction == ACT_UNLOCKING_STAR_DOOR ? ACT_WALKING : ACT_IDLE, +// 0); +// } +// } +// } +// // apply head turn +// vec3s_set(m->marioBodyState->headAngle, m->actionTimer, 0, 0); +// return FALSE; +} + +s32 act_reading_sign(struct MarioState *m) { +// struct Object *marioObj = m->marioObj; +// +// play_sound_if_no_flag(m, SOUND_ACTION_READ_SIGN, MARIO_ACTION_SOUND_PLAYED); +// +// switch (m->actionState) { +// // start dialog +// case 0: +// trigger_cutscene_dialog(1); +// enable_time_stop(); +// // reading sign +// set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); +// m->actionState = 1; +// // intentional fall through +// // turn toward sign +// case 1: +// m->faceAngle[1] += marioObj->oMarioPoleUnk108 / 11; +// m->pos[0] += marioObj->oMarioReadingSignDPosX / 11.0f; +// m->pos[2] += marioObj->oMarioReadingSignDPosZ / 11.0f; +// // create the text box +// if (m->actionTimer++ == 10) { +// create_dialog_inverted_box(m->usedObj->oBehParams2ndByte); +// m->actionState = 2; +// } +// break; +// // in dialog +// case 2: +// // dialog finished +//// if (gCamera->cutscene == 0) { +// disable_time_stop(); +// set_mario_action(m, ACT_IDLE, 0); +//// } +// break; +// } +// +// vec3f_copy(marioObj->header.gfx.pos, m->pos); +// vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); +// return FALSE; +} + +s32 act_debug_free_move(struct MarioState *m) { + struct Controller *gPlayer1Controller = &gController; + + struct Surface *surf; + f32 floorHeight; + Vec3f pos; + f32 speed; + u32 action; + + // integer immediates, generates convert instructions for some reason + speed = gPlayer1Controller->buttonDown & B_BUTTON ? 4 : 1; + if (gPlayer1Controller->buttonDown & L_TRIG) { + speed = 0.01f; + } + + set_mario_animation(m, MARIO_ANIM_A_POSE); + vec3f_copy(pos, m->pos); + + if (gPlayer1Controller->buttonDown & U_JPAD) { + pos[1] += 16.0f * speed; + } + if (gPlayer1Controller->buttonDown & D_JPAD) { + pos[1] -= 16.0f * speed; + } + + if (m->intendedMag > 0) { + pos[0] += 32.0f * speed * sins(m->intendedYaw); + pos[2] += 32.0f * speed * coss(m->intendedYaw); + } + + resolve_and_return_wall_collisions(pos, 60.0f, 50.0f); + + floorHeight = find_floor(pos[0], pos[1], pos[2], &surf); + if (surf != NULL) { + if (pos[1] < floorHeight) { + pos[1] = floorHeight; + } + vec3f_copy(m->pos, pos); + } + + m->faceAngle[1] = m->intendedYaw; + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + + if (gPlayer1Controller->buttonPressed == A_BUTTON) { + if (m->pos[1] <= m->waterLevel - 100) { + action = ACT_WATER_IDLE; + } else { + action = ACT_IDLE; + } + set_mario_action(m, action, 0); + } + + return FALSE; +} + +void general_star_dance_handler(struct MarioState *m, s32 isInWater) { +// s32 dialogID; +// if (m->actionState == 0) { +// switch (++m->actionTimer) { +// case 1: +// spawn_object(m->marioObj, MODEL_STAR, bhvCelebrationStar); +// disable_background_sound(); +// if (m->actionArg & 1) { +// play_course_clear(); +// } else { +// if (gCurrLevelNum == LEVEL_BOWSER_1 || gCurrLevelNum == LEVEL_BOWSER_2) { +// play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_KEY), 0); +// } else { +// play_music(SEQ_PLAYER_ENV, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_COLLECT_STAR), 0); +// } +// } +// break; + +// case 42: +// play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); +// break; + +// case 80: +// if ((m->actionArg & 1) == 0) { +// level_trigger_warp(m, WARP_OP_STAR_EXIT); +// } else { +// enable_time_stop(); +// create_dialog_box_with_response(gLastCompletedStarNum == 7 ? DIALOG_013 : DIALOG_014); +// m->actionState = 1; +// } +// break; +// } +// } else if (m->actionState == 1 && gDialogResponse) { +// if (gDialogResponse == 1) { +// save_file_do_save(gCurrSaveFileNum - 1); +// } +// m->actionState = 2; +// } else if (m->actionState == 2 && is_anim_at_end(m)) { +// disable_time_stop(); +// enable_background_sound(); +// dialogID = get_star_collection_dialog(m); +// if (dialogID != 0) { +// // look up for dialog +// set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, dialogID); +// } else { +// set_mario_action(m, isInWater ? ACT_WATER_IDLE : ACT_IDLE, 0); +// } +// } +} + +s32 act_star_dance(struct MarioState *m) { + m->faceAngle[1] = m->area->camera->yaw; + set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_STAR_DANCE + : MARIO_ANIM_STAR_DANCE); + general_star_dance_handler(m, 0); + if (m->actionState != 2 && m->actionTimer >= 40) { + m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN; + } + stop_and_set_height_to_floor(m); + return FALSE; +} + +s32 act_star_dance_water(struct MarioState *m) { + m->faceAngle[1] = m->area->camera->yaw; + set_mario_animation(m, m->actionState == 2 ? MARIO_ANIM_RETURN_FROM_WATER_STAR_DANCE + : MARIO_ANIM_WATER_STAR_DANCE); + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + general_star_dance_handler(m, 1); + if (m->actionState != 2 && m->actionTimer >= 62) { + m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN; + } + return FALSE; +} + +s32 act_fall_after_star_grab(struct MarioState *m) { + if (m->pos[1] < m->waterLevel - 130) { + play_sound(SOUND_ACTION_UNKNOWN430, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_WATER_SPLASH; + return set_mario_action(m, ACT_STAR_DANCE_WATER, m->actionArg); + } + if (perform_air_step(m, 1) == AIR_STEP_LANDED) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + set_mario_action(m, m->actionArg & 1 ? ACT_STAR_DANCE_NO_EXIT : ACT_STAR_DANCE_EXIT, + m->actionArg); + } + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + return FALSE; +} + +s32 common_death_handler(struct MarioState *m, s32 animation, s32 frameToDeathWarp) { + s32 animFrame = set_mario_animation(m, animation); + if (animFrame == frameToDeathWarp) { + level_trigger_warp(m, WARP_OP_DEATH); + } + m->marioBodyState->eyeState = MARIO_EYES_DEAD; + stop_and_set_height_to_floor(m); + return animFrame; +} + +s32 act_standing_death(struct MarioState *m) { + if (m->input & INPUT_IN_POISON_GAS) { + return set_mario_action(m, ACT_SUFFOCATION, 0); + } + + play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); + common_death_handler(m, MARIO_ANIM_DYING_FALL_OVER, 80); + if (m->marioObj->header.gfx.animInfo.animFrame == 77) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + } + return FALSE; +} + +s32 act_electrocution(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); + common_death_handler(m, MARIO_ANIM_ELECTROCUTION, 43); + return FALSE; +} + +s32 act_suffocation(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); + common_death_handler(m, MARIO_ANIM_SUFFOCATING, 86); + return FALSE; +} + +s32 act_death_on_back(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); + if (common_death_handler(m, MARIO_ANIM_DYING_ON_BACK, 54) == 40) { + play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + } + return FALSE; +} + +s32 act_death_on_stomach(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); + if (common_death_handler(m, MARIO_ANIM_DYING_ON_STOMACH, 37) == 37) { + play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + } + return FALSE; +} + +s32 act_quicksand_death(struct MarioState *m) { + if (m->actionState == 0) { + set_mario_animation(m, MARIO_ANIM_DYING_IN_QUICKSAND); + set_anim_to_frame(m, 60); + m->actionState = 1; + } + if (m->actionState == 1) { + if (m->quicksandDepth >= 100.0f) { + play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED); + } + if ((m->quicksandDepth += 5.0f) >= 180.0f) { + level_trigger_warp(m, WARP_OP_DEATH); + m->actionState = 2; + } + } + stationary_ground_step(m); + play_sound(SOUND_MOVING_QUICKSAND_DEATH, m->marioObj->header.gfx.cameraToObject); + return FALSE; +} + +s32 act_eaten_by_bubba(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_DYING, MARIO_ACTION_SOUND_PLAYED); + set_mario_animation(m, MARIO_ANIM_A_POSE); + m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + m->health = 0xFF; + if (m->actionTimer++ == 60) { + level_trigger_warp(m, WARP_OP_DEATH); + } + return FALSE; +} + +// set animation and forwardVel; when perform_air_step returns AIR_STEP_LANDED, +// set the new action +s32 launch_mario_until_land(struct MarioState *m, s32 endAction, s32 animation, f32 forwardVel) { + s32 airStepLanded; + mario_set_forward_vel(m, forwardVel); + set_mario_animation(m, animation); + airStepLanded = (perform_air_step(m, 0) == AIR_STEP_LANDED); + if (airStepLanded) { + set_mario_action(m, endAction, 0); + } + return airStepLanded; +} + +s32 act_unlocking_key_door(struct MarioState *m) { +// m->faceAngle[1] = m->usedObj->oMoveAngleYaw; + +// m->pos[0] = m->usedObj->oPosX + coss(m->faceAngle[1]) * 75.0f; +// m->pos[2] = m->usedObj->oPosZ + sins(m->faceAngle[1]) * 75.0f; + +// if (m->actionArg & 2) { +// m->faceAngle[1] += 0x8000; +// } + +// if (m->actionTimer == 0) { +// spawn_obj_at_mario_rel_yaw(m, MODEL_BOWSER_KEY_CUTSCENE, bhvBowserKeyUnlockDoor, 0); +// set_mario_animation(m, MARIO_ANIM_UNLOCK_DOOR); +// } + +// switch (m->marioObj->header.gfx.animInfo.animFrame) { +// case 79: +// play_sound(SOUND_GENERAL_DOOR_INSERT_KEY, m->marioObj->header.gfx.cameraToObject); +// break; +// case 111: +// play_sound(SOUND_GENERAL_DOOR_TURN_KEY, m->marioObj->header.gfx.cameraToObject); +// break; +// } + +// update_mario_pos_for_anim(m); +// stop_and_set_height_to_floor(m); + +// if (is_anim_at_end(m)) { +// if (m->usedObj->oBehParams >> 24 == 1) { +// save_file_set_flags(SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR); +// save_file_clear_flags(SAVE_FLAG_HAVE_KEY_2); +// } else { +// save_file_set_flags(SAVE_FLAG_UNLOCKED_BASEMENT_DOOR); +// save_file_clear_flags(SAVE_FLAG_HAVE_KEY_1); +// } +// set_mario_action(m, ACT_WALKING, 0); +// } + +// m->actionTimer++; +// return FALSE; +} + +s32 act_unlocking_star_door(struct MarioState *m) { +// switch (m->actionState) { +// case 0: +// m->faceAngle[1] = m->usedObj->oMoveAngleYaw; +// if (m->actionArg & 2) { +// m->faceAngle[1] += 0x8000; +// } +// m->marioObj->oMarioReadingSignDPosX = m->pos[0]; +// m->marioObj->oMarioReadingSignDPosZ = m->pos[2]; +// set_mario_animation(m, MARIO_ANIM_SUMMON_STAR); +// m->actionState++; +// break; +// case 1: +// if (is_anim_at_end(m)) { +// spawn_object(m->marioObj, MODEL_STAR, bhvUnlockDoorStar); +// m->actionState++; +// } +// break; +// case 2: +// if (m->actionTimer++ == 70) { +// set_mario_animation(m, MARIO_ANIM_RETURN_STAR_APPROACH_DOOR); +// m->actionState++; +// } +// break; +// case 3: +// if (is_anim_at_end(m)) { +// save_file_set_flags(get_door_save_file_flag(m->usedObj)); +// set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, DIALOG_038); +// } +// break; +// } + +// m->pos[0] = m->marioObj->oMarioReadingSignDPosX; +// m->pos[2] = m->marioObj->oMarioReadingSignDPosZ; + +// update_mario_pos_for_anim(m); +// stop_and_set_height_to_floor(m); + +// return FALSE; +} + +s32 act_entering_star_door(struct MarioState *m) { + f32 targetDX; + f32 targetDZ; + s16 targetAngle; + + if (m->actionTimer++ == 0) { + m->interactObj->oInteractStatus = 0x00010000; + + // ~30 degrees / 1/12 rot + targetAngle = m->usedObj->oMoveAngleYaw + 0x1555; + if (m->actionArg & 2) { + targetAngle += 0x5556; // ~120 degrees / 1/3 rot (total 150d / 5/12) + } + + // targetDX and targetDZ are the offsets to add to Mario's position to + // have Mario stand 150 units in front of the door + + targetDX = m->usedObj->oPosX + 150.0f * sins(targetAngle) - m->pos[0]; + targetDZ = m->usedObj->oPosZ + 150.0f * coss(targetAngle) - m->pos[2]; + + m->marioObj->oMarioReadingSignDPosX = targetDX / 20.0f; + m->marioObj->oMarioReadingSignDPosZ = targetDZ / 20.0f; + + m->faceAngle[1] = atan2s(targetDZ, targetDX); + } + + // set Mario's animation + if (m->actionTimer < 15) { + set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); + } + + // go through door? for 20 frames + else if (m->actionTimer < 35) { + m->pos[0] += m->marioObj->oMarioReadingSignDPosX; + m->pos[2] += m->marioObj->oMarioReadingSignDPosZ; + + set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, 0x00028000); + } + + else { + m->faceAngle[1] = m->usedObj->oMoveAngleYaw; + + if (m->actionArg & 2) { + m->faceAngle[1] += 0x8000; + } + + m->pos[0] += 12.0f * sins(m->faceAngle[1]); + m->pos[2] += 12.0f * coss(m->faceAngle[1]); + + set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, 0x00028000); + } + + stop_and_set_height_to_floor(m); + + if (m->actionTimer == 48) { + set_mario_action(m, ACT_IDLE, 0); + } + + return FALSE; +} + +s32 act_going_through_door(struct MarioState *m) { + if (m->actionTimer == 0) { + if (m->actionArg & 1) { + m->interactObj->oInteractStatus = 0x00010000; + set_mario_animation(m, MARIO_ANIM_PULL_DOOR_WALK_IN); + } else { + m->interactObj->oInteractStatus = 0x00020000; + set_mario_animation(m, MARIO_ANIM_PUSH_DOOR_WALK_IN); + } + } + m->faceAngle[1] = m->usedObj->oMoveAngleYaw; + m->pos[0] = m->usedObj->oPosX; + m->pos[2] = m->usedObj->oPosZ; + + update_mario_pos_for_anim(m); + stop_and_set_height_to_floor(m); + + if (m->actionArg & 4) { + if (m->actionTimer == 16) { + level_trigger_warp(m, WARP_OP_WARP_DOOR); + } + } else if (is_anim_at_end(m)) { + if (m->actionArg & 2) { + m->faceAngle[1] += 0x8000; + } + set_mario_action(m, ACT_IDLE, 0); + } + + m->actionTimer++; + return FALSE; +} + +s32 act_warp_door_spawn(struct MarioState *m) { + if (m->actionState == 0) { + m->actionState = 1; + if (m->actionArg & 1) { + m->usedObj->oInteractStatus = 0x00040000; + } else { + m->usedObj->oInteractStatus = 0x00080000; + } + } else if (m->usedObj->oAction == 0) { +// if (gNeverEnteredCastle == TRUE && gCurrLevelNum == LEVEL_CASTLE) { + set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, 0); // DIALOG_021); +// } else { +// set_mario_action(m, ACT_IDLE, 0); +// } + } + set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); + stop_and_set_height_to_floor(m); + return FALSE; +} + +s32 act_emerge_from_pipe(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + if (m->actionTimer++ < 11) { + marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + return FALSE; + } + + marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + + play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); + + if (gCurrLevelNum == LEVEL_THI) { +// if (gCurrAreaIndex == 2) { + play_sound_if_no_flag(m, SOUND_MENU_EXIT_PIPE, MARIO_ACTION_SOUND_PLAYED); +// } else { +// play_sound_if_no_flag(m, SOUND_MENU_ENTER_PIPE, MARIO_ACTION_SOUND_PLAYED); +// } + } + + if (launch_mario_until_land(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) { + mario_set_forward_vel(m, 0.0f); + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + return FALSE; +} + +s32 act_spawn_spin_airborne(struct MarioState *m) { + // entered water, exit action + if (m->pos[1] < m->waterLevel - 100) { + load_level_init_text(0); + return set_water_plunge_action(m); + } + + // updates all velocity variables based on m->forwardVel + mario_set_forward_vel(m, m->forwardVel); + + // landed on floor, play spawn land animation + if (perform_air_step(m, 0.0) == AIR_STEP_LANDED) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + set_mario_action(m, ACT_SPAWN_SPIN_LANDING, 0); + } + + // is 300 units above floor, spin and play woosh sounds + if (m->actionState == 0 && m->pos[1] - m->floorHeight > 300.0f) { + if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) { // first anim frame + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + } + + // under 300 units above floor, enter freefall animation + else { + m->actionState = 1; + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + } + + return FALSE; +} + +s32 act_spawn_spin_landing(struct MarioState *m) { + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_GENERAL_LAND); + if (is_anim_at_end(m)) { + load_level_init_text(0); + set_mario_action(m, ACT_IDLE, 0); + } + return FALSE; +} + +/** + * act_exit_airborne: Jump out of a level after collecting a Power Star (no + ** sparkles) + * Mario always faces a level entrance when he launches out of it, whether he + * died or he collected a star/key. Because of that, we need him to move away + * from the painting by setting his speed to -32.0f and have him face away from + * the painting by adding 0x8000 (180 deg) to his graphics angle. We also set + * his heal counter to 31 to restore 7.75 units of his health, and enable the + * particle flag that generates sparkles. + */ +s32 act_exit_airborne(struct MarioState *m) { + if (15 < m->actionTimer++ + && launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, -32.0f)) { + // heal Mario + m->healCounter = 31; + } + // rotate him to face away from the entrance + m->marioObj->header.gfx.angle[1] += 0x8000; + m->particleFlags |= PARTICLE_SPARKLES; + return FALSE; +} + +s32 act_falling_exit_airborne(struct MarioState *m) { + if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_GENERAL_FALL, 0.0f)) { + // heal Mario + m->healCounter = 31; + } + // rotate Mario to face away from the entrance + m->marioObj->header.gfx.angle[1] += 0x8000; + m->particleFlags |= PARTICLE_SPARKLES; + return FALSE; +} + +s32 act_exit_land_save_dialog(struct MarioState *m) { +// s32 animFrame; +// stationary_ground_step(m); +// play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING); +// switch (m->actionState) { +// // determine type of exit +// case 0: +// set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_GENERAL_LAND +// : MARIO_ANIM_LAND_FROM_SINGLE_JUMP); +// if (is_anim_past_end(m)) { +// if (gLastCompletedCourseNum != COURSE_BITDW +// && gLastCompletedCourseNum != COURSE_BITFS) { +// enable_time_stop(); +// } + +// set_menu_mode(RENDER_COURSE_DONE_SCREEN); +// gSaveOptSelectIndex = 0; + +// m->actionState = 3; // star exit with cap +// if (!(m->flags & MARIO_CAP_ON_HEAD)) { +// m->actionState = 2; // star exit without cap +// } +// if (gLastCompletedCourseNum == COURSE_BITDW +// || gLastCompletedCourseNum == COURSE_BITFS) { +// m->actionState = 1; // key exit +// } +// } +// break; +// // key exit +// case 1: +// animFrame = set_mario_animation(m, MARIO_ANIM_THROW_CATCH_KEY); +// switch (animFrame) { +// case -1: +// spawn_obj_at_mario_rel_yaw(m, MODEL_BOWSER_KEY_CUTSCENE, bhvBowserKeyCourseExit, -32768); +// //! fall through +// case 67: +// play_sound(SOUND_ACTION_KEY_SWISH, m->marioObj->header.gfx.cameraToObject); +// //! fall through +// case 83: +// play_sound(SOUND_ACTION_PAT_BACK, m->marioObj->header.gfx.cameraToObject); +// //! fall through +// case 111: +// play_sound(SOUND_ACTION_UNKNOWN45C, m->marioObj->header.gfx.cameraToObject); +// // no break +// } +// handle_save_menu(m); +// break; +// // exit without cap +// case 2: +// animFrame = set_mario_animation(m, MARIO_ANIM_MISSING_CAP); +// if ((animFrame >= 18 && animFrame < 55) || (animFrame >= 112 && animFrame < 134)) { +// m->marioBodyState->handState = MARIO_HAND_OPEN; +// } +// if (!(animFrame < 109) && animFrame < 154) { +// m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; +// } + +// handle_save_menu(m); +// break; +// // exit with cap +// case 3: +// animFrame = set_mario_animation(m, MARIO_ANIM_TAKE_CAP_OFF_THEN_ON); +// switch (animFrame) { +// case 12: +// cutscene_take_cap_off(m); +// break; +// case 37: +// // fall through +// case 53: +// play_sound(SOUND_ACTION_BRUSH_HAIR, m->marioObj->header.gfx.cameraToObject); +// break; +// case 82: +// cutscene_put_cap_on(m); +// break; +// } +// handle_save_menu(m); +// break; +// } + +// m->marioObj->header.gfx.angle[1] += 0x8000; +// return FALSE; +} + +s32 act_death_exit(struct MarioState *m) { + if (15 < m->actionTimer++ + && launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, -32.0f)) { +#ifdef VERSION_JP + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#else + play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); +#endif +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + m->numLives--; + // restore 7.75 units of health + m->healCounter = 31; + } + // one unit of health + m->health = 0x0100; + return FALSE; +} + +s32 act_unused_death_exit(struct MarioState *m) { + if (launch_mario_until_land(m, ACT_FREEFALL_LAND_STOP, MARIO_ANIM_GENERAL_FALL, 0.0f)) { +#ifdef VERSION_JP + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#else + play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); +#endif + m->numLives--; + // restore 7.75 units of health + m->healCounter = 31; + } + // one unit of health + m->health = 0x0100; + return FALSE; +} + +s32 act_falling_death_exit(struct MarioState *m) { + if (launch_mario_until_land(m, ACT_DEATH_EXIT_LAND, MARIO_ANIM_GENERAL_FALL, 0.0f)) { +#ifdef VERSION_JP + play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject); +#else + play_sound(SOUND_MARIO_OOOF2, m->marioObj->header.gfx.cameraToObject); +#endif +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + m->numLives--; + // restore 7.75 units of health + m->healCounter = 31; + } + // one unit of health + m->health = 0x0100; + return FALSE; +} + +// waits 11 frames before actually executing, also has reduced fvel +s32 act_special_exit_airborne(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); + + if (m->actionTimer++ < 11) { + marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + return FALSE; + } + + if (launch_mario_until_land(m, ACT_EXIT_LAND_SAVE_DIALOG, MARIO_ANIM_SINGLE_JUMP, -24.0f)) { + // heal Mario + m->healCounter = 31; + m->actionArg = 1; + } + + m->particleFlags |= PARTICLE_SPARKLES; + // rotate Mario to face away from the entrance + marioObj->header.gfx.angle[1] += 0x8000; + // show Mario + marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + + return FALSE; +} + +s32 act_special_death_exit(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + if (m->actionTimer++ < 11) { + marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + return FALSE; + } + + if (launch_mario_until_land(m, ACT_HARD_BACKWARD_GROUND_KB, MARIO_ANIM_BACKWARD_AIR_KB, -24.0f)) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + m->numLives--; + m->healCounter = 31; + } + // show Mario + marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; + // one unit of health + m->health = 0x0100; + + return FALSE; +} + +s32 act_spawn_no_spin_airborne(struct MarioState *m) { + launch_mario_until_land(m, ACT_SPAWN_NO_SPIN_LANDING, MARIO_ANIM_GENERAL_FALL, 0.0f); + if (m->pos[1] < m->waterLevel - 100) { + set_water_plunge_action(m); + } + return FALSE; +} + +s32 act_spawn_no_spin_landing(struct MarioState *m) { + play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING); + set_mario_animation(m, MARIO_ANIM_GENERAL_LAND); + stop_and_set_height_to_floor(m); + if (is_anim_at_end(m)) { + load_level_init_text(0); + set_mario_action(m, ACT_IDLE, 0); + } + return FALSE; +} + +s32 act_bbh_enter_spin(struct MarioState *m) { + f32 floorDist; + f32 scale; + f32 cageDX; + f32 cageDZ; + f32 cageDist; + f32 forwardVel; + + cageDX = m->usedObj->oPosX - m->pos[0]; + cageDZ = m->usedObj->oPosZ - m->pos[2]; + cageDist = sqrtf(cageDX * cageDX + cageDZ * cageDZ); + + if (cageDist > 20.0f) { + forwardVel = 10.0f; + } else { + forwardVel = cageDist / 2.0f; + } + if (forwardVel < 0.5f) { + forwardVel = 0.0f; + } + + switch (m->actionState) { + case 0: + floorDist = 512.0f - (m->pos[1] - m->floorHeight); + m->vel[1] = floorDist > 0 ? sqrtf(4.0f * floorDist + 1.0f) - 1.0f : 2.0f; + + m->actionState = 1; + m->actionTimer = 100; + // fall through + + case 1: + m->faceAngle[1] = atan2s(cageDZ, cageDX); + mario_set_forward_vel(m, forwardVel); + + if (set_mario_animation(m, MARIO_ANIM_FORWARD_SPINNING) == 0) { + play_sound(SOUND_ACTION_SPIN, m->marioObj->header.gfx.cameraToObject); + } + + m->flags &= ~MARIO_UNKNOWN_08; + perform_air_step(m, 0); + if (m->vel[1] <= 0) { + m->actionState = 2; + } + break; + + case 2: + // fall through + case 3: + m->faceAngle[1] = atan2s(cageDZ, cageDX); + mario_set_forward_vel(m, forwardVel); + m->flags &= ~MARIO_UNKNOWN_08; + if (perform_air_step(m, 0) == AIR_STEP_LANDED) { + level_trigger_warp(m, WARP_OP_UNKNOWN_02); +#ifdef VERSION_SH + queue_rumble_data(15, 80); +#endif + m->actionState = 4; + } + if (m->actionState == 2) { + if (m->marioObj->header.gfx.animInfo.animFrame == 0) { + m->actionState = 3; + } + } else { + play_sound_if_no_flag(m, SOUND_ACTION_SHRINK_INTO_BBH, MARIO_ACTION_SOUND_PLAYED); + set_mario_animation(m, MARIO_ANIM_DIVE); + m->marioObj->header.gfx.angle[0] = atan2s(m->forwardVel, -m->vel[1]); + } + m->squishTimer = 0xFF; + if (m->actionTimer >= 11) { + m->actionTimer -= 6; + scale = m->actionTimer / 100.0f; + vec3f_set(m->marioObj->header.gfx.scale, scale, scale, scale); + } + break; + + case 4: + stop_and_set_height_to_floor(m); + m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; + break; + } + + return FALSE; +} + +s32 act_bbh_enter_jump(struct MarioState *m) { + f32 cageDX; + f32 cageDZ; + f32 cageDist; + + play_mario_action_sound( + m, m->flags & MARIO_METAL_CAP ? SOUND_ACTION_METAL_JUMP : SOUND_ACTION_TERRAIN_JUMP, 1); + play_mario_jump_sound(m); + + if (m->actionState == 0) { + cageDX = m->usedObj->oPosX - m->pos[0]; + cageDZ = m->usedObj->oPosZ - m->pos[2]; + cageDist = sqrtf(cageDX * cageDX + cageDZ * cageDZ); + + m->vel[1] = 60.0f; + m->faceAngle[1] = atan2s(cageDZ, cageDX); + mario_set_forward_vel(m, cageDist / 20.0f); + + m->flags &= ~MARIO_UNKNOWN_08; + m->actionState = 1; + } + + set_mario_animation(m, MARIO_ANIM_DOUBLE_JUMP_RISE); + perform_air_step(m, 0); + + if (m->vel[1] <= 0.0f) { + set_mario_action(m, ACT_BBH_ENTER_SPIN, 0); + } + + return FALSE; +} + +s32 act_teleport_fade_out(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED); + set_mario_animation(m, m->prevAction == ACT_CROUCHING ? MARIO_ANIM_CROUCHING + : MARIO_ANIM_FIRST_PERSON); + +#ifdef VERSION_SH + if (m->actionTimer == 0) { + queue_rumble_data(30, 70); + func_sh_8024C89C(2); + } +#endif + + m->flags |= MARIO_TELEPORTING; + + if (m->actionTimer < 32) { + m->fadeWarpOpacity = (-m->actionTimer << 3) + 0xF8; + } + + if (m->actionTimer++ == 20) { + level_trigger_warp(m, WARP_OP_TELEPORT); + } + + stop_and_set_height_to_floor(m); + + return FALSE; +} + +s32 act_teleport_fade_in(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_ACTION_TELEPORT, MARIO_ACTION_SOUND_PLAYED); + set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); + +#ifdef VERSION_SH + if (m->actionTimer == 0) { + queue_rumble_data(30, 70); + func_sh_8024C89C(2); + } +#endif + + if (m->actionTimer < 32) { + m->flags |= MARIO_TELEPORTING; + m->fadeWarpOpacity = m->actionTimer << 3; + } else { + m->flags &= ~MARIO_TELEPORTING; + } + + if (m->actionTimer++ == 32) { + if (m->pos[1] < m->waterLevel - 100) { + // Check if the camera is not underwater. + if (m->area->camera->mode != CAMERA_MODE_WATER_SURFACE) { + set_camera_mode(m->area->camera, CAMERA_MODE_WATER_SURFACE, 1); + } + set_mario_action(m, ACT_WATER_IDLE, 0); + } else { + set_mario_action(m, ACT_IDLE, 0); + } + } + + stop_and_set_height_to_floor(m); + + return FALSE; +} + +s32 act_shocked(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED); + play_sound(SOUND_MOVING_SHOCKED, m->marioObj->header.gfx.cameraToObject); + set_camera_shake_from_hit(SHAKE_SHOCK); + + if (set_mario_animation(m, MARIO_ANIM_SHOCKED) == 0) { + m->actionTimer++; + m->flags |= MARIO_METAL_SHOCK; + } + + if (m->actionArg == 0) { + mario_set_forward_vel(m, 0.0f); + if (perform_air_step(m, 1) == AIR_STEP_LANDED) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + m->actionArg = 1; + } + } else { + if (m->actionTimer >= 6) { + m->invincTimer = 30; + set_mario_action(m, m->health < 0x0100 ? ACT_ELECTROCUTION : ACT_IDLE, 0); + } + stop_and_set_height_to_floor(m); + } + + return FALSE; +} + +s32 act_squished(struct MarioState *m) { + UNUSED s32 pad; + f32 squishAmount; + f32 spaceUnderCeil; + s16 surfAngle; + s32 underSteepSurf = FALSE; // seems to be responsible for setting velocity? + + if ((spaceUnderCeil = m->ceilHeight - m->floorHeight) < 0) { + spaceUnderCeil = 0; + } + + switch (m->actionState) { + case 0: + if (spaceUnderCeil > 160.0f) { + m->squishTimer = 0; + return set_mario_action(m, ACT_IDLE, 0); + } + + m->squishTimer = 0xFF; + + if (spaceUnderCeil >= 10.1f) { + // Mario becomes a pancake + squishAmount = spaceUnderCeil / 160.0f; + vec3f_set(m->marioObj->header.gfx.scale, 2.0f - squishAmount, squishAmount, + 2.0f - squishAmount); + } else { + if (!(m->flags & MARIO_METAL_CAP) && m->invincTimer == 0) { + // cap on: 3 units; cap off: 4.5 units + m->hurtCounter += m->flags & MARIO_CAP_ON_HEAD ? 12 : 18; + play_sound_if_no_flag(m, SOUND_MARIO_ATTACKED, MARIO_MARIO_SOUND_PLAYED); + } + + // Both of the 1.8's are really floats, but one of them has to + // be written as a double for this to match on -O2. + vec3f_set(m->marioObj->header.gfx.scale, 1.8, 0.05f, 1.8f); +#ifdef VERSION_SH + queue_rumble_data(10, 80); +#endif + m->actionState = 1; + } + break; + case 1: + if (spaceUnderCeil >= 30.0f) { + m->actionState = 2; + } + break; + case 2: + m->actionTimer++; + if (m->actionTimer >= 15) { + // 1 unit of health + if (m->health < 0x0100) { + level_trigger_warp(m, WARP_OP_DEATH); + // woosh, he's gone! + set_mario_action(m, ACT_DISAPPEARED, 0); + } else if (m->hurtCounter == 0) { + // un-squish animation + m->squishTimer = 30; + set_mario_action(m, ACT_IDLE, 0); + } + } + break; + } + + // steep floor + if (m->floor != NULL && m->floor->normal.y < 0.5f) { + surfAngle = atan2s(m->floor->normal.z, m->floor->normal.x); + underSteepSurf = TRUE; + } + // steep ceiling + if (m->ceil != NULL && -0.5f < m->ceil->normal.y) { + surfAngle = atan2s(m->ceil->normal.z, m->ceil->normal.x); + underSteepSurf = TRUE; + } + + if (underSteepSurf) { + m->vel[0] = sins(surfAngle) * 10.0f; + m->vel[2] = coss(surfAngle) * 10.0f; + m->vel[1] = 0; + + // check if there's no floor 10 units away from the surface + if (perform_ground_step(m) == GROUND_STEP_LEFT_GROUND) { + // instant un-squish + m->squishTimer = 0; + set_mario_action(m, ACT_IDLE, 0); + return FALSE; + } + } + + // squished for more than 10 seconds, so kill Mario + if (m->actionArg++ > 300) { + // 0 units of health + m->health = 0x00FF; + m->hurtCounter = 0; + level_trigger_warp(m, WARP_OP_DEATH); + // woosh, he's gone! + set_mario_action(m, ACT_DISAPPEARED, 0); + } + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_A_POSE); + return FALSE; +} + +s32 act_putting_on_cap(struct MarioState *m) { + s32 animFrame = set_mario_animation(m, MARIO_ANIM_PUT_CAP_ON); + + if (animFrame == 0) { + enable_time_stop(); + } + + if (animFrame == 28) { + cutscene_put_cap_on(m); + } + + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_IDLE, 0); + disable_time_stop(); + } + + stationary_ground_step(m); + return FALSE; +} + +void stuck_in_ground_handler(struct MarioState *m, s32 animation, s32 unstuckFrame, s32 target2, + s32 target3, s32 endAction) { + s32 animFrame = set_mario_animation(m, animation); + + if (m->input & INPUT_A_PRESSED) { + m->actionTimer++; + if (m->actionTimer >= 5 && animFrame < unstuckFrame - 1) { + animFrame = unstuckFrame - 1; + set_anim_to_frame(m, animFrame); + } + } + + stop_and_set_height_to_floor(m); + + if (animFrame == -1) { + play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_STUCK_IN_GROUND, 1); + } else if (animFrame == unstuckFrame) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + play_sound_and_spawn_particles(m, SOUND_ACTION_UNSTUCK_FROM_GROUND, 1); + } else if (animFrame == target2 || animFrame == target3) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + + if (is_anim_at_end(m)) { + set_mario_action(m, endAction, 0); + } +} + +s32 act_head_stuck_in_ground(struct MarioState *m) { + stuck_in_ground_handler(m, MARIO_ANIM_HEAD_STUCK_IN_GROUND, 96, 105, 135, ACT_IDLE); + return FALSE; +} + +s32 act_butt_stuck_in_ground(struct MarioState *m) { + stuck_in_ground_handler(m, MARIO_ANIM_BOTTOM_STUCK_IN_GROUND, 127, 136, -2, ACT_GROUND_POUND_LAND); + return FALSE; +} + +s32 act_feet_stuck_in_ground(struct MarioState *m) { + stuck_in_ground_handler(m, MARIO_ANIM_LEGS_STUCK_IN_GROUND, 116, 129, -2, ACT_IDLE); + return FALSE; +} + +/** + * advance_cutscene_step: Advances the current step in the current cutscene. + * Resets action state and action timer, adds 1 to the action arg (responsible + * for keeping track of what step of the cutscene Mario is in.) + */ +static void advance_cutscene_step(struct MarioState *m) { + m->actionState = 0; + m->actionTimer = 0; + m->actionArg++; +} + +static void intro_cutscene_hide_hud_and_mario(struct MarioState *m) { + gHudDisplay.flags = HUD_DISPLAY_NONE; + m->statusForCamera->cameraEvent = CAM_EVENT_START_INTRO; + m->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE; + advance_cutscene_step(m); +} + +#ifdef VERSION_EU + #define TIMER_SPAWN_PIPE 47 +#else + #define TIMER_SPAWN_PIPE 37 +#endif + +static void intro_cutscene_peach_lakitu_scene(struct MarioState *m) { +// if ((s16) m->statusForCamera->cameraEvent != CAM_EVENT_START_INTRO) { +// if (m->actionTimer++ == TIMER_SPAWN_PIPE) { +// sIntroWarpPipeObj = +// spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_CASTLE_GROUNDS_WARP_PIPE, +// bhvStaticObject, -1328, 60, 4664, 0, 180, 0); +// advance_cutscene_step(m); +// } +// } +} +#undef TIMER_SPAWN_PIPE + +#ifdef VERSION_EU + #define TIMER_RAISE_PIPE 28 +#else + #define TIMER_RAISE_PIPE 38 +#endif + +static void intro_cutscene_raise_pipe(struct MarioState *m) { +// sIntroWarpPipeObj->oPosY = camera_approach_f32_symmetric(sIntroWarpPipeObj->oPosY, 260.0f, 10.0f); + +// if (m->actionTimer == 0) { +// play_sound(SOUND_MENU_EXIT_PIPE, sIntroWarpPipeObj->header.gfx.cameraToObject); +// } + +// if (m->actionTimer++ == TIMER_RAISE_PIPE) { +// m->vel[1] = 60.0f; +// advance_cutscene_step(m); +// } +} +#undef TIMER_RAISE_PIPE + +static void intro_cutscene_jump_out_of_pipe(struct MarioState *m) { +// if (m->actionTimer == 25) { +// gHudDisplay.flags = HUD_DISPLAY_DEFAULT; +// } +// +// if (m->actionTimer++ >= 118) { +// m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE; +// +//#ifdef VERSION_EU +// // For some reason these calls were swapped. +// play_sound_if_no_flag(m, SOUND_ACTION_HIT_3, MARIO_ACTION_SOUND_PLAYED); +// play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); +//#else +// play_sound_if_no_flag(m, SOUND_MARIO_YAHOO, MARIO_MARIO_SOUND_PLAYED); +// #ifndef VERSION_JP +// play_sound_if_no_flag(m, SOUND_ACTION_HIT_3, MARIO_ACTION_SOUND_PLAYED); +// #endif +//#endif +// +// set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP); +// mario_set_forward_vel(m, 10.0f); +// if (perform_air_step(m, 0) == AIR_STEP_LANDED) { +// sound_banks_enable(2, 0x0330); +// play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); +//#ifndef VERSION_JP +// play_sound(SOUND_MARIO_HAHA, m->marioObj->header.gfx.cameraToObject); +//#endif +// advance_cutscene_step(m); +// } +// } +} + +static void intro_cutscene_land_outside_pipe(struct MarioState *m) { + set_mario_animation(m, MARIO_ANIM_LAND_FROM_SINGLE_JUMP); + + if (is_anim_at_end(m)) { + advance_cutscene_step(m); + } + + stop_and_set_height_to_floor(m); +} + +static void intro_cutscene_lower_pipe(struct MarioState *m) { + if (m->actionTimer++ == 0) { + play_sound(SOUND_MENU_ENTER_PIPE, sIntroWarpPipeObj->header.gfx.cameraToObject); + set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); + } + + sIntroWarpPipeObj->oPosY -= 5.0f; + if (sIntroWarpPipeObj->oPosY <= 50.0f) { +// obj_mark_for_deletion(sIntroWarpPipeObj); + advance_cutscene_step(m); + } + + stop_and_set_height_to_floor(m); +} + +static void intro_cutscene_set_mario_to_idle(struct MarioState *m) { +// if (gCamera->cutscene == 0) { + gCameraMovementFlags &= ~CAM_MOVE_C_UP_MODE; + set_mario_action(m, ACT_IDLE, 0); +// } + + stop_and_set_height_to_floor(m); +} + +enum { + INTRO_CUTSCENE_HIDE_HUD_AND_MARIO, + INTRO_CUTSCENE_PEACH_LAKITU_SCENE, + INTRO_CUTSCENE_RAISE_PIPE, + INTRO_CUTSCENE_JUMP_OUT_OF_PIPE, + INTRO_CUTSCENE_LAND_OUTSIDE_PIPE, + INTRO_CUTSCENE_LOWER_PIPE, + INTRO_CUTSCENE_SET_MARIO_TO_IDLE +}; + +static s32 act_intro_cutscene(struct MarioState *m) { + switch (m->actionArg) { + case INTRO_CUTSCENE_HIDE_HUD_AND_MARIO: + intro_cutscene_hide_hud_and_mario(m); + break; + case INTRO_CUTSCENE_PEACH_LAKITU_SCENE: + intro_cutscene_peach_lakitu_scene(m); + break; + case INTRO_CUTSCENE_RAISE_PIPE: + intro_cutscene_raise_pipe(m); + break; + case INTRO_CUTSCENE_JUMP_OUT_OF_PIPE: + intro_cutscene_jump_out_of_pipe(m); + break; + case INTRO_CUTSCENE_LAND_OUTSIDE_PIPE: + intro_cutscene_land_outside_pipe(m); + break; + case INTRO_CUTSCENE_LOWER_PIPE: + intro_cutscene_lower_pipe(m); + break; + case INTRO_CUTSCENE_SET_MARIO_TO_IDLE: + intro_cutscene_set_mario_to_idle(m); + break; + } + return FALSE; +} + +// jumbo star cutscene: Mario lands after grabbing the jumbo star +static void jumbo_star_cutscene_falling(struct MarioState *m) { + if (m->actionState == 0) { + m->input |= INPUT_A_DOWN; + m->flags |= (MARIO_WING_CAP | MARIO_CAP_ON_HEAD); + + m->faceAngle[1] = -0x8000; + m->pos[0] = 0.0f; + m->pos[2] = 0.0f; + + mario_set_forward_vel(m, 0.0f); + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + + if (perform_air_step(m, 1) == AIR_STEP_LANDED) { + play_cutscene_music(SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_VICTORY)); + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + m->actionState++; + } + } else { + set_mario_animation(m, MARIO_ANIM_GENERAL_LAND); + if (is_anim_at_end(m)) { + m->statusForCamera->cameraEvent = CAM_EVENT_START_GRAND_STAR; + advance_cutscene_step(m); + } + } +} + +// jumbo star cutscene: Mario takes off +static s32 jumbo_star_cutscene_taking_off(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + s32 animFrame; + + if (m->actionState == 0) { + set_mario_animation(m, MARIO_ANIM_FINAL_BOWSER_RAISE_HAND_SPIN); + marioObj->rawData.asF32[0x22] = 0.0f; + + if (is_anim_past_end(m)) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + m->actionState++; + } + } else { + animFrame = set_mario_animation(m, MARIO_ANIM_FINAL_BOWSER_WING_CAP_TAKE_OFF); + if (animFrame == 3 || animFrame == 28 || animFrame == 60) { + play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_JUMP, 1); + } + if (animFrame >= 3) { + marioObj->rawData.asF32[0x22] -= 32.0f; + } + + switch (animFrame) { + case 3: + play_sound(SOUND_MARIO_YAH_WAH_HOO + (gAudioRandom % 3 << 16), + m->marioObj->header.gfx.cameraToObject); + break; + + case 28: + play_sound(SOUND_MARIO_HOOHOO, m->marioObj->header.gfx.cameraToObject); + break; + + case 60: + play_sound(SOUND_MARIO_YAHOO, m->marioObj->header.gfx.cameraToObject); + break; + } + m->particleFlags |= PARTICLE_SPARKLES; + + if (is_anim_past_end(m)) { + advance_cutscene_step(m); + } + } + + vec3f_set(m->pos, 0.0f, 307.0, marioObj->rawData.asF32[0x22]); + update_mario_pos_for_anim(m); + vec3f_copy(marioObj->header.gfx.pos, m->pos); + vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + + // not sure why they did this, probably was from being used to action + // functions + return FALSE; +} + +// jumbo star cutscene: Mario flying +static s32 jumbo_star_cutscene_flying(struct MarioState *m) { + Vec3f targetPos; + UNUSED struct Object *marioObj = m->marioObj; + f32 targetDX; + f32 targetDY; + f32 targetDZ; + f32 targetHyp; + s16 targetAngle; + + switch (m->actionState) { + case 0: + set_mario_animation(m, MARIO_ANIM_WING_CAP_FLY); + anim_spline_init(sJumboStarKeyframes); + m->actionState++; + // fall through + case 1: + if (anim_spline_poll(targetPos)) { + // does this twice + set_mario_action(m, ACT_FREEFALL, 0); + m->actionState++; + } else { + targetDX = targetPos[0] - m->pos[0]; + targetDY = targetPos[1] - m->pos[1]; + targetDZ = targetPos[2] - m->pos[2]; + targetHyp = sqrtf(targetDX * targetDX + targetDZ * targetDZ); + targetAngle = atan2s(targetDZ, targetDX); + + vec3f_copy(m->pos, targetPos); + m->marioObj->header.gfx.angle[0] = -atan2s(targetHyp, targetDY); + m->marioObj->header.gfx.angle[1] = targetAngle; + m->marioObj->header.gfx.angle[2] = ((m->faceAngle[1] - targetAngle) << 16 >> 16) * 20; + m->faceAngle[1] = targetAngle; + } + break; + case 2: + set_mario_action(m, ACT_FREEFALL, 0); + break; + } + + m->marioBodyState->handState = MARIO_HAND_RIGHT_OPEN; + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + m->particleFlags |= PARTICLE_SPARKLES; + + if (m->actionTimer++ == 500) { + level_trigger_warp(m, WARP_OP_CREDITS_START); + } + + return FALSE; +} + +enum { JUMBO_STAR_CUTSCENE_FALLING, JUMBO_STAR_CUTSCENE_TAKING_OFF, JUMBO_STAR_CUTSCENE_FLYING }; + +static s32 act_jumbo_star_cutscene(struct MarioState *m) { + switch (m->actionArg) { + case JUMBO_STAR_CUTSCENE_FALLING: + jumbo_star_cutscene_falling(m); + break; + case JUMBO_STAR_CUTSCENE_TAKING_OFF: + jumbo_star_cutscene_taking_off(m); + break; + case JUMBO_STAR_CUTSCENE_FLYING: + jumbo_star_cutscene_flying(m); + break; + } + return FALSE; +} + +void generate_yellow_sparkles(s16 x, s16 y, s16 z, f32 radius) { +// static s32 sSparkleGenTheta = 0; +// static s32 sSparkleGenPhi = 0; + +// s16 offsetX = radius * coss(sSparkleGenTheta) * sins(sSparkleGenPhi); +// s16 offsetY = radius * sins(sSparkleGenTheta); +// s16 offsetZ = radius * coss(sSparkleGenTheta) * coss(sSparkleGenPhi); + +// spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_NONE, bhvSparkleSpawn, x + offsetX, y + offsetY, +// z + offsetZ, 0, 0, 0); + +// //! copy paste error +// offsetX = offsetX * 4 / 3; +// offsetX = offsetY * 4 / 3; +// offsetX = offsetZ * 4 / 3; + +// spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_NONE, bhvSparkleSpawn, x - offsetX, y - offsetY, +// z - offsetZ, 0, 0, 0); + +// sSparkleGenTheta += 0x3800; +// sSparkleGenPhi += 0x6000; +} + +// not sure what this does, returns the height of the floor. +// (animation related?) +static f32 end_obj_set_visual_pos(struct Object *o) { + struct Surface *surf; + Vec3s sp24; + f32 sp20; + f32 sp1C; + f32 sp18; + + find_mario_anim_flags_and_translation(o, o->header.gfx.angle[1], sp24); + + sp20 = o->header.gfx.pos[0] + sp24[0]; + sp1C = o->header.gfx.pos[1] + 10.0f; + sp18 = o->header.gfx.pos[2] + sp24[2]; + + return find_floor(sp20, sp1C, sp18, &surf); +} + +// make Mario fall and soften wing cap gravity +static void end_peach_cutscene_mario_falling(struct MarioState *m) { + if (m->actionTimer == 1) { + m->statusForCamera->cameraEvent = CAM_EVENT_START_ENDING; + } + + m->input |= INPUT_A_DOWN; + m->flags |= (MARIO_WING_CAP | MARIO_CAP_ON_HEAD); + + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + mario_set_forward_vel(m, 0.0f); + + if (perform_air_step(m, 0) == AIR_STEP_LANDED) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + advance_cutscene_step(m); + } +} + +// set Mario on the ground, wait and spawn the jumbo star outside the castle. +static void end_peach_cutscene_mario_landing(struct MarioState *m) { +// set_mario_animation(m, MARIO_ANIM_GENERAL_LAND); +// stop_and_set_height_to_floor(m); + +// if (is_anim_at_end(m)) { +// // make wing cap run out +// m->capTimer = 60; + +// sEndJumboStarObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_STAR, bhvStaticObject, 0, +// 2528, -1800, 0, 0, 0); +// obj_scale(sEndJumboStarObj, 3.0); +// advance_cutscene_step(m); +// } +} + +// raise hand animation, lower hand animation, do some special effects +static void end_peach_cutscene_summon_jumbo_star(struct MarioState *m) { + set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_CREDITS_RAISE_HAND + : MARIO_ANIM_CREDITS_LOWER_HAND); + + if (m->actionState == 0 && is_anim_past_end(m)) { + m->actionState++; + } + if (m->actionTimer == 90) { + play_cutscene_music(SEQUENCE_ARGS(0, SEQ_EVENT_CUTSCENE_ENDING)); + } + if (m->actionTimer == 255) { + advance_cutscene_step(m); + } + + sEndJumboStarObj->oFaceAngleYaw += 0x0400; + generate_yellow_sparkles(0, 2528, -1800, 250.0f); + play_sound(SOUND_AIR_PEACH_TWINKLE, sEndJumboStarObj->header.gfx.cameraToObject); +} + +#ifdef VERSION_EU + #define TIMER_FADE_IN_PEACH 201 + #define TIMER_DESCEND_PEACH 280 +#else + #define TIMER_FADE_IN_PEACH 276 + #define TIMER_DESCEND_PEACH 355 +#endif + +// free peach from the stained glass window +static void end_peach_cutscene_spawn_peach(struct MarioState *m) { +// if (m->actionTimer == 1) { +// play_transition(WARP_TRANSITION_FADE_INTO_COLOR, 14, 255, 255, 255); +// } +// if (m->actionTimer == 2) { +// play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs); +// } +// if (m->actionTimer == 44) { +// play_transition(WARP_TRANSITION_FADE_FROM_COLOR, 192, 255, 255, 255); +// } +// if (m->actionTimer == 40) { +// obj_mark_for_deletion(sEndJumboStarObj); + +// sEndPeachObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_PEACH, bhvEndPeach, 0, 2428, +// -1300, 0, 0, 0); +// gCutsceneFocus = sEndPeachObj; + +// sEndRightToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, 200, +// 906, -1290, 0, 0, 0); + +// sEndLeftToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, -200, +// 906, -1290, 0, 0, 0); + +// sEndPeachObj->oOpacity = 127; +// sEndRightToadObj->oOpacity = 255; +// sEndLeftToadObj->oOpacity = 255; + +// D_8032CBE4 = 4; +// sEndPeachAnimation = 4; + +// sEndToadAnims[0] = 4; +// sEndToadAnims[1] = 5; +// } + +// if (m->actionTimer >= TIMER_FADE_IN_PEACH) { +// sEndPeachObj->oOpacity = camera_approach_f32_symmetric(sEndPeachObj->oOpacity, 255.0f, 2.0f); +// } +// if (m->actionTimer >= 40) { +// generate_yellow_sparkles(0, 2628, -1300, 150.0f); +// } + +// if (m->actionTimer == TIMER_DESCEND_PEACH) { +// advance_cutscene_step(m); +// } +// // probably added sounds later and missed the previous >= 40 check +// if (m->actionTimer >= 40) { +// play_sound(SOUND_AIR_PEACH_TWINKLE, sEndPeachObj->header.gfx.cameraToObject); +// } +} + +#ifdef VERSION_EU + #define TIMER_RUN_TO_PEACH 531 +#else + #define TIMER_RUN_TO_PEACH 584 +#endif + +// descend peach +static void end_peach_cutscene_descend_peach(struct MarioState *m) { + generate_yellow_sparkles(0, sEndPeachObj->oPosY, -1300, 150.0f); + + if (sEndPeachObj->oPosY >= 1300.0f) { + if (m->actionState < 60) { + m->actionState += 5; + } + } else { + if (m->actionState >= 27) { + m->actionState -= 2; + } + set_mario_animation(m, MARIO_ANIM_CREDITS_RETURN_FROM_LOOK_UP); + } + + if ((sEndPeachObj->oPosY -= m->actionState / 10) <= 907.0f) { + sEndPeachObj->oPosY = 906.0f; + } + + play_sound(SOUND_AIR_PEACH_TWINKLE, sEndPeachObj->header.gfx.cameraToObject); + + if (m->actionTimer >= TIMER_RUN_TO_PEACH) { + advance_cutscene_step(m); + } +} + +#undef TIMER_RUN_TO_PEACH + +// Mario runs to peach +static void end_peach_cutscene_run_to_peach(struct MarioState *m) { + struct Surface *surf; + + if (m->actionTimer == 22) { + sEndPeachAnimation = 5; + } + + if ((m->pos[2] -= 20.0f) <= -1181.0f) { + m->pos[2] = -1180.0f; + advance_cutscene_step(m); + } + + m->pos[1] = find_floor(m->pos[0], m->pos[1], m->pos[2], &surf); + + set_mario_anim_with_accel(m, MARIO_ANIM_RUNNING, 0x00080000); + play_step_sound(m, 9, 45); + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + m->particleFlags |= PARTICLE_DUST; +} + +// dialog 1 +// "Mario!" +// "The power of the Stars is restored to the castle..." +static void end_peach_cutscene_dialog_1(struct MarioState *m) { +// s32 animFrame = set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_CREDITS_TAKE_OFF_CAP +// : MARIO_ANIM_CREDITS_LOOK_UP); +// +// if (m->actionState == 0) { +// if (animFrame == 8) { +// cutscene_take_cap_off(m); +// } +// +// if (is_anim_at_end(m)) { +// m->actionState++; +// } +// } +// +// switch (m->actionTimer) { +// case 80: +// sEndPeachAnimation = 6; +// break; +// +// case 81: +// D_8032CBE4 = 3; +// break; +// +// case 145: +// D_8032CBE4 = 2; +// break; +// +// case 228: +// D_8032CBE4 = 1; +// D_8032CBE8 = 1; +// break; +// +// case 230: +// set_cutscene_message(160, 227, 0, 30); +//#ifndef VERSION_JP +//// func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40); +// play_sound(SOUND_PEACH_MARIO, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// +// case 275: +// D_8032CBE4 = 0; +// D_8032CBE8 = 0; +// break; +// +// case 290: +// set_cutscene_message(160, 227, 1, 60); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_POWER_OF_THE_STARS, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// +// case 480: +// advance_cutscene_step(m); +// break; +// } +} + +#ifdef VERSION_EU + #define TIMER_SOMETHING_SPECIAL 150 + #define TIMER_PEACH_KISS 260 +#else + #define TIMER_SOMETHING_SPECIAL 130 + #define TIMER_PEACH_KISS 200 +#endif + +// dialog 2 +// "...and it's all thanks to you!" +// "Thank you Mario!" +// "We have to do something special for you..." +static void end_peach_cutscene_dialog_2(struct MarioState *m) { +// sEndPeachAnimation = 9; +// +// switch (m->actionTimer) { +// case 29: +// set_cutscene_message(160, 227, 2, 30); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_THANKS_TO_YOU, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// +// case 45: +// D_8032CBE8 = 1; +// break; +// +// case 75: +// set_cutscene_message(160, 227, 3, 30); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_THANK_YOU_MARIO, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// +// case TIMER_SOMETHING_SPECIAL: +// set_cutscene_message(160, 227, 4, 40); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_SOMETHING_SPECIAL, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// +// case TIMER_PEACH_KISS: +// advance_cutscene_step(m); +// break; +// } +} + +#undef TIMER_SOMETHING_SPECIAL +#undef TIMER_PEACH_KISS + +// blink twice then have half-shut eyes (see end_peach_cutscene_kiss_from_peach) +static u8 sMarioBlinkOverride[20] = { + MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED, + MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN, + MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED, + MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_OPEN, MARIO_EYES_OPEN, + MARIO_EYES_HALF_CLOSED, MARIO_EYES_HALF_CLOSED, MARIO_EYES_CLOSED, MARIO_EYES_CLOSED, +}; + +static void end_peach_cutscene_kiss_from_peach(struct MarioState *m) { +// sEndPeachAnimation = 10; +// +// if (m->actionTimer >= 90) { +// m->marioBodyState->eyeState = +// m->actionTimer < 110 ? sMarioBlinkOverride[m->actionTimer - 90] : MARIO_EYES_HALF_CLOSED; +// } +// +// switch (m->actionTimer) { +// case 8: +// D_8032CBE8 = 0; +// break; +// +// case 10: +// D_8032CBE4 = 3; +// break; +// +// case 50: +// D_8032CBE4 = 4; +// break; +// +// case 75: +// m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; +// break; +// +// case 76: +// m->marioBodyState->eyeState = MARIO_EYES_CLOSED; +// break; +// +// case 100: +// D_8032CBE4 = 3; +// break; +// +// case 136: +// D_8032CBE4 = 0; +// break; +// +// case 140: +// advance_cutscene_step(m); +// break; +// } +} + +static void end_peach_cutscene_star_dance(struct MarioState *m) { + s32 animFrame = set_mario_animation(m, MARIO_ANIM_CREDITS_PEACE_SIGN); + + if (animFrame == 77) { + cutscene_put_cap_on(m); + } + if (animFrame == 88) { + play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); + } + if (animFrame >= 98) { + m->marioBodyState->handState = MARIO_HAND_PEACE_SIGN; + } + + if (m->actionTimer < 52) { + m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; + } + + switch (m->actionTimer) { + case 70: + D_8032CBE4 = 1; + break; + + case 86: + D_8032CBE4 = 2; + break; + + case 90: + D_8032CBE4 = 3; + break; + + case 120: + D_8032CBE4 = 0; + break; + + case 140: +#ifndef VERSION_JP +// sequence_player_unlower(SEQ_PLAYER_LEVEL, 60); +#endif + play_cutscene_music(SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_CREDITS)); + break; + + case 142: + advance_cutscene_step(m); + break; + } +} + +// dialog 3 +// "Listen everybody" +// "let's bake a delicious cake..." +// "...for Mario..." +static void end_peach_cutscene_dialog_3(struct MarioState *m) { +// set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); +// +// sEndPeachObj->oPosY = end_obj_set_visual_pos(sEndPeachObj); +// sEndRightToadObj->oPosY = end_obj_set_visual_pos(sEndRightToadObj); +// sEndLeftToadObj->oPosY = end_obj_set_visual_pos(sEndLeftToadObj); +// +// switch (m->actionTimer) { +// case 1: +// sEndPeachAnimation = 0; +// sEndToadAnims[0] = 0; +// sEndToadAnims[1] = 2; +// D_8032CBE8 = 1; +// set_cutscene_message(160, 227, 5, 30); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_BAKE_A_CAKE, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// +// case 55: +// set_cutscene_message(160, 227, 6, 40); +// break; +// +// case 130: +// set_cutscene_message(160, 227, 7, 50); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_FOR_MARIO, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// break; +// } +// +// if (m->actionTimer == 350) { +// advance_cutscene_step(m); +// } +} + +// "Mario!" +static void end_peach_cutscene_run_to_castle(struct MarioState *m) { +// set_mario_animation(m, m->actionState == 0 ? MARIO_ANIM_CREDITS_START_WALK_LOOK_UP +// : MARIO_ANIM_CREDITS_LOOK_BACK_THEN_RUN); +// +// m->marioObj->header.gfx.pos[1] = end_obj_set_visual_pos(m->marioObj); +// +// if (m->actionState == 0 && is_anim_past_end(m)) { +// m->actionState = 1; +// } +// +// if (m->actionTimer == 95) { +// set_cutscene_message(160, 227, 0, 40); +//#ifndef VERSION_JP +// play_sound(SOUND_PEACH_MARIO2, sEndPeachObj->header.gfx.cameraToObject); +//#endif +// } +// if (m->actionTimer == 389) { +// advance_cutscene_step(m); +// } +} + +static void end_peach_cutscene_fade_out(struct MarioState *m) { + if (m->actionState == 0) { + level_trigger_warp(m, WARP_OP_CREDITS_NEXT); +// gPaintingMarioYEntry = 1500.0f; // ensure medium water level in WDW credits cutscene + m->actionState = 1; + } +} + +enum { + END_PEACH_CUTSCENE_MARIO_FALLING, + END_PEACH_CUTSCENE_MARIO_LANDING, + END_PEACH_CUTSCENE_SUMMON_JUMBO_STAR, + END_PEACH_CUTSCENE_SPAWN_PEACH, + END_PEACH_CUTSCENE_DESCEND_PEACH, + END_PEACH_CUTSCENE_RUN_TO_PEACH, + END_PEACH_CUTSCENE_DIALOG_1, + END_PEACH_CUTSCENE_DIALOG_2, + END_PEACH_CUTSCENE_KISS_FROM_PEACH, + END_PEACH_CUTSCENE_STAR_DANCE, + END_PEACH_CUTSCENE_DIALOG_3, + END_PEACH_CUTSCENE_RUN_TO_CASTLE, + END_PEACH_CUTSCENE_FADE_OUT +}; + +static s32 act_end_peach_cutscene(struct MarioState *m) { + switch (m->actionArg) { + case END_PEACH_CUTSCENE_MARIO_FALLING: + end_peach_cutscene_mario_falling(m); + break; + case END_PEACH_CUTSCENE_MARIO_LANDING: + end_peach_cutscene_mario_landing(m); + break; + case END_PEACH_CUTSCENE_SUMMON_JUMBO_STAR: + end_peach_cutscene_summon_jumbo_star(m); + break; + case END_PEACH_CUTSCENE_SPAWN_PEACH: + end_peach_cutscene_spawn_peach(m); + break; + case END_PEACH_CUTSCENE_DESCEND_PEACH: + end_peach_cutscene_descend_peach(m); + break; + case END_PEACH_CUTSCENE_RUN_TO_PEACH: + end_peach_cutscene_run_to_peach(m); + break; + case END_PEACH_CUTSCENE_DIALOG_1: + end_peach_cutscene_dialog_1(m); + break; + case END_PEACH_CUTSCENE_DIALOG_2: + end_peach_cutscene_dialog_2(m); + break; + case END_PEACH_CUTSCENE_KISS_FROM_PEACH: + end_peach_cutscene_kiss_from_peach(m); + break; + case END_PEACH_CUTSCENE_STAR_DANCE: + end_peach_cutscene_star_dance(m); + break; + case END_PEACH_CUTSCENE_DIALOG_3: + end_peach_cutscene_dialog_3(m); + break; + case END_PEACH_CUTSCENE_RUN_TO_CASTLE: + end_peach_cutscene_run_to_castle(m); + break; + case END_PEACH_CUTSCENE_FADE_OUT: + end_peach_cutscene_fade_out(m); + break; + } + + m->actionTimer++; + + sEndCutsceneVp.vp.vscale[0] = 640; + sEndCutsceneVp.vp.vscale[1] = 360; + sEndCutsceneVp.vp.vtrans[0] = 640; + sEndCutsceneVp.vp.vtrans[1] = 480; +// override_viewport_and_clip(NULL, &sEndCutsceneVp, 0, 0, 0); + + return FALSE; +} + +#ifdef VERSION_EU + #define TIMER_CREDITS_SHOW 51 + #define TIMER_CREDITS_PROGRESS 80 + #define TIMER_CREDITS_WARP 160 +#else + #define TIMER_CREDITS_SHOW 61 + #define TIMER_CREDITS_PROGRESS 90 + #define TIMER_CREDITS_WARP 200 +#endif + +static s32 act_credits_cutscene(struct MarioState *m) { +// s32 width; +// s32 height; +// +// m->statusForCamera->cameraEvent = CAM_EVENT_START_CREDITS; +// // checks if Mario is underwater (JRB, DDD, SA, etc.) +// if (m->pos[1] < m->waterLevel - 100) { +// if (m->area->camera->mode != CAMERA_MODE_BEHIND_MARIO) { +// set_camera_mode(m->area->camera, CAMERA_MODE_BEHIND_MARIO, 1); +// } +// set_mario_animation(m, MARIO_ANIM_WATER_IDLE); +// vec3f_copy(m->marioObj->header.gfx.pos, m->pos); +// // will copy over roll and pitch, if set +// vec3s_copy(m->marioObj->header.gfx.angle, m->faceAngle); +// m->particleFlags |= PARTICLE_BUBBLE; +// } else { +// set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); +// if (m->actionTimer > 0) { +// stop_and_set_height_to_floor(m); +// } +// } +// +// if (m->actionTimer >= TIMER_CREDITS_SHOW) { +// if (m->actionState < 40) { +// m->actionState += 2; +// } +// +// width = m->actionState * 640 / 100; +// height = m->actionState * 480 / 100; +// +// sEndCutsceneVp.vp.vscale[0] = 640 - width; +// sEndCutsceneVp.vp.vscale[1] = 480 - height; +// sEndCutsceneVp.vp.vtrans[0] = +// (gCurrCreditsEntry->unk02 & 0x10 ? width : -width) * 56 / 100 + 640; +// sEndCutsceneVp.vp.vtrans[1] = +// (gCurrCreditsEntry->unk02 & 0x20 ? height : -height) * 66 / 100 + 480; +// +// override_viewport_and_clip(&sEndCutsceneVp, 0, 0, 0, 0); +// } +// +// if (m->actionTimer == TIMER_CREDITS_PROGRESS) { +// reset_cutscene_msg_fade(); +// } +// +// if (m->actionTimer >= TIMER_CREDITS_PROGRESS) { +// sDispCreditsEntry = gCurrCreditsEntry; +// } +// +// if (m->actionTimer++ == TIMER_CREDITS_WARP) { +// level_trigger_warp(m, WARP_OP_CREDITS_NEXT); +// } +// +// m->marioObj->header.gfx.angle[1] += (gCurrCreditsEntry->unk02 & 0xC0) << 8; +// +// return FALSE; +} + +static s32 act_end_waving_cutscene(struct MarioState *m) { +// if (m->actionState == 0) { +// m->statusForCamera->cameraEvent = CAM_EVENT_START_END_WAVING; + +// sEndPeachObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_PEACH, bhvEndPeach, 60, 906, +// -1180, 0, 0, 0); + +// sEndRightToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, 180, +// 906, -1170, 0, 0, 0); + +// sEndLeftToadObj = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TOAD, bhvEndToad, -180, +// 906, -1170, 0, 0, 0); + +// sEndPeachObj->oOpacity = 255; +// sEndRightToadObj->oOpacity = 255; +// sEndLeftToadObj->oOpacity = 255; + +// sEndPeachAnimation = 11; +// sEndToadAnims[0] = 6; +// sEndToadAnims[1] = 7; + +// m->actionState = 1; +// } + +// set_mario_animation(m, MARIO_ANIM_CREDITS_WAVING); +// stop_and_set_height_to_floor(m); + +// m->marioObj->header.gfx.angle[1] += 0x8000; +// m->marioObj->header.gfx.pos[0] -= 60.0f; +// m->marioBodyState->handState = MARIO_HAND_RIGHT_OPEN; + +// if (m->actionTimer++ == 300) { +// level_trigger_warp(m, WARP_OP_CREDITS_END); +// } + +// return FALSE; +} + +static s32 check_for_instant_quicksand(struct MarioState *m) { + if (m->floor->type == SURFACE_INSTANT_QUICKSAND && m->action & ACT_FLAG_INVULNERABLE + && m->action != ACT_QUICKSAND_DEATH) { + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0); + } + return FALSE; +} + +s32 mario_execute_cutscene_action(struct MarioState *m) { + s32 cancel; + + if (check_for_instant_quicksand(m)) { + return TRUE; + } + + /* clang-format off */ + switch (m->action) { + case ACT_DISAPPEARED: cancel = act_disappeared(m); break; + case ACT_INTRO_CUTSCENE: cancel = act_intro_cutscene(m); break; + case ACT_STAR_DANCE_EXIT: cancel = act_star_dance(m); break; + case ACT_STAR_DANCE_NO_EXIT: cancel = act_star_dance(m); break; + case ACT_STAR_DANCE_WATER: cancel = act_star_dance_water(m); break; + case ACT_FALL_AFTER_STAR_GRAB: cancel = act_fall_after_star_grab(m); break; + case ACT_READING_AUTOMATIC_DIALOG: cancel = act_reading_automatic_dialog(m); break; + case ACT_READING_NPC_DIALOG: cancel = act_reading_npc_dialog(m); break; + case ACT_DEBUG_FREE_MOVE: cancel = act_debug_free_move(m); break; + case ACT_READING_SIGN: cancel = act_reading_sign(m); break; + case ACT_JUMBO_STAR_CUTSCENE: cancel = act_jumbo_star_cutscene(m); break; + case ACT_WAITING_FOR_DIALOG: cancel = act_waiting_for_dialog(m); break; + case ACT_STANDING_DEATH: cancel = act_standing_death(m); break; + case ACT_QUICKSAND_DEATH: cancel = act_quicksand_death(m); break; + case ACT_ELECTROCUTION: cancel = act_electrocution(m); break; + case ACT_SUFFOCATION: cancel = act_suffocation(m); break; + case ACT_DEATH_ON_STOMACH: cancel = act_death_on_stomach(m); break; + case ACT_DEATH_ON_BACK: cancel = act_death_on_back(m); break; + case ACT_EATEN_BY_BUBBA: cancel = act_eaten_by_bubba(m); break; + case ACT_END_PEACH_CUTSCENE: cancel = act_end_peach_cutscene(m); break; + case ACT_CREDITS_CUTSCENE: cancel = act_credits_cutscene(m); break; + case ACT_END_WAVING_CUTSCENE: cancel = act_end_waving_cutscene(m); break; + case ACT_PULLING_DOOR: + case ACT_PUSHING_DOOR: cancel = act_going_through_door(m); break; + case ACT_WARP_DOOR_SPAWN: cancel = act_warp_door_spawn(m); break; + case ACT_EMERGE_FROM_PIPE: cancel = act_emerge_from_pipe(m); break; + case ACT_SPAWN_SPIN_AIRBORNE: cancel = act_spawn_spin_airborne(m); break; + case ACT_SPAWN_SPIN_LANDING: cancel = act_spawn_spin_landing(m); break; + case ACT_EXIT_AIRBORNE: cancel = act_exit_airborne(m); break; + case ACT_EXIT_LAND_SAVE_DIALOG: cancel = act_exit_land_save_dialog(m); break; + case ACT_DEATH_EXIT: cancel = act_death_exit(m); break; + case ACT_UNUSED_DEATH_EXIT: cancel = act_unused_death_exit(m); break; + case ACT_FALLING_DEATH_EXIT: cancel = act_falling_death_exit(m); break; + case ACT_SPECIAL_EXIT_AIRBORNE: cancel = act_special_exit_airborne(m); break; + case ACT_SPECIAL_DEATH_EXIT: cancel = act_special_death_exit(m); break; + case ACT_FALLING_EXIT_AIRBORNE: cancel = act_falling_exit_airborne(m); break; + case ACT_UNLOCKING_KEY_DOOR: cancel = act_unlocking_key_door(m); break; + case ACT_UNLOCKING_STAR_DOOR: cancel = act_unlocking_star_door(m); break; + case ACT_ENTERING_STAR_DOOR: cancel = act_entering_star_door(m); break; + case ACT_SPAWN_NO_SPIN_AIRBORNE: cancel = act_spawn_no_spin_airborne(m); break; + case ACT_SPAWN_NO_SPIN_LANDING: cancel = act_spawn_no_spin_landing(m); break; + case ACT_BBH_ENTER_JUMP: cancel = act_bbh_enter_jump(m); break; + case ACT_BBH_ENTER_SPIN: cancel = act_bbh_enter_spin(m); break; + case ACT_TELEPORT_FADE_OUT: cancel = act_teleport_fade_out(m); break; + case ACT_TELEPORT_FADE_IN: cancel = act_teleport_fade_in(m); break; + case ACT_SHOCKED: cancel = act_shocked(m); break; + case ACT_SQUISHED: cancel = act_squished(m); break; + case ACT_HEAD_STUCK_IN_GROUND: cancel = act_head_stuck_in_ground(m); break; + case ACT_BUTT_STUCK_IN_GROUND: cancel = act_butt_stuck_in_ground(m); break; + case ACT_FEET_STUCK_IN_GROUND: cancel = act_feet_stuck_in_ground(m); break; + case ACT_PUTTING_ON_CAP: cancel = act_putting_on_cap(m); break; + } + /* clang-format on */ + + if (!cancel && (m->input & INPUT_IN_WATER)) { + m->particleFlags |= PARTICLE_IDLE_WATER_WAVE; + } + + return cancel; +} diff --git a/src/game/mario_actions_cutscene.h b/src/game/mario_actions_cutscene.h new file mode 100644 index 0000000..e84601a --- /dev/null +++ b/src/game/mario_actions_cutscene.h @@ -0,0 +1,17 @@ +#ifndef MARIO_ACTIONS_CUTSCENE_H +#define MARIO_ACTIONS_CUTSCENE_H + +#include "../include/PR/ultratypes.h" + +#include "../include/macros.h" +#include "../include/types.h" + +void print_displaying_credits_entry(void); +void bhv_end_peach_loop(void); +void bhv_end_toad_loop(void); +s32 geo_switch_peach_eyes(s32 run, struct GraphNode *node, UNUSED s32 a2); +s32 mario_ready_to_speak(void); +s32 set_mario_npc_dialog(s32 actionArg); +s32 mario_execute_cutscene_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_CUTSCENE_H diff --git a/src/game/mario_actions_moving.c b/src/game/mario_actions_moving.c new file mode 100644 index 0000000..82d2af4 --- /dev/null +++ b/src/game/mario_actions_moving.c @@ -0,0 +1,2045 @@ +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "../include/sm64.h" +#include "mario.h" +//#include "audio/external.h" +#include "../engine/math_util.h" +#include "../engine/surface_collision.h" +#include "mario_step.h" +#include "area.h" +#include "interaction.h" +#include "mario_actions_object.h" +//#include "memory.h" +//#include "behavior_data.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + +struct LandingAction { + s16 numFrames; + s16 unk02; + u32 verySteepAction; + u32 endAction; + u32 aPressedAction; + u32 offFloorAction; + u32 slideAction; +}; + +struct LandingAction sJumpLandAction = { + 4, 5, ACT_FREEFALL, ACT_JUMP_LAND_STOP, ACT_DOUBLE_JUMP, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +struct LandingAction sFreefallLandAction = { + 4, 5, ACT_FREEFALL, ACT_FREEFALL_LAND_STOP, ACT_DOUBLE_JUMP, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +struct LandingAction sSideFlipLandAction = { + 4, 5, ACT_FREEFALL, ACT_SIDE_FLIP_LAND_STOP, ACT_DOUBLE_JUMP, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +struct LandingAction sHoldJumpLandAction = { + 4, 5, ACT_HOLD_FREEFALL, ACT_HOLD_JUMP_LAND_STOP, ACT_HOLD_JUMP, ACT_HOLD_FREEFALL, ACT_HOLD_BEGIN_SLIDING, +}; + +struct LandingAction sHoldFreefallLandAction = { + 4, 5, ACT_HOLD_FREEFALL, ACT_HOLD_FREEFALL_LAND_STOP, ACT_HOLD_JUMP, ACT_HOLD_FREEFALL, ACT_HOLD_BEGIN_SLIDING, +}; + +struct LandingAction sLongJumpLandAction = { + 6, 5, ACT_FREEFALL, ACT_LONG_JUMP_LAND_STOP, ACT_LONG_JUMP, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +struct LandingAction sDoubleJumpLandAction = { + 4, 5, ACT_FREEFALL, ACT_DOUBLE_JUMP_LAND_STOP, ACT_JUMP, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +struct LandingAction sTripleJumpLandAction = { + 4, 0, ACT_FREEFALL, ACT_TRIPLE_JUMP_LAND_STOP, ACT_UNINITIALIZED, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +struct LandingAction sBackflipLandAction = { + 4, 0, ACT_FREEFALL, ACT_BACKFLIP_LAND_STOP, ACT_BACKFLIP, ACT_FREEFALL, ACT_BEGIN_SLIDING, +}; + +Mat4 sFloorAlignMatrix[2]; + +s16 tilt_body_running(struct MarioState *m) { + s16 pitch = find_floor_slope(m, 0); + pitch = pitch * m->forwardVel / 40.0f; + return -pitch; +} + +void play_step_sound(struct MarioState *m, s16 frame1, s16 frame2) { + if (is_anim_past_frame(m, frame1) || is_anim_past_frame(m, frame2)) { + if (m->flags & MARIO_METAL_CAP) { + if (m->marioObj->header.gfx.animInfo.animID == MARIO_ANIM_TIPTOE) { + play_sound_and_spawn_particles(m, SOUND_ACTION_METAL_STEP_TIPTOE, 0); + } else { + play_sound_and_spawn_particles(m, SOUND_ACTION_METAL_STEP, 0); + } + } else if (m->quicksandDepth > 50.0f) { + play_sound(SOUND_ACTION_QUICKSAND_STEP, m->marioObj->header.gfx.cameraToObject); + } else if (m->marioObj->header.gfx.animInfo.animID == MARIO_ANIM_TIPTOE) { + play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_STEP_TIPTOE, 0); + } else { + play_sound_and_spawn_particles(m, SOUND_ACTION_TERRAIN_STEP, 0); + } + } +} + +void align_with_floor(struct MarioState *m) { + m->pos[1] = m->floorHeight; + mtxf_align_terrain_triangle(sFloorAlignMatrix[m->unk00], m->pos, m->faceAngle[1], 40.0f); + m->marioObj->header.gfx.throwMatrix = &sFloorAlignMatrix[m->unk00]; +} + +s32 begin_walking_action(struct MarioState *m, f32 forwardVel, u32 action, u32 actionArg) { + m->faceAngle[1] = m->intendedYaw; + mario_set_forward_vel(m, forwardVel); + return set_mario_action(m, action, actionArg); +} + +void check_ledge_climb_down(struct MarioState *m) { + struct WallCollisionData wallCols; + struct Surface *floor; + f32 floorHeight; + struct Surface *wall; + s16 wallAngle; + s16 wallDYaw; + + if (m->forwardVel < 10.0f) { + wallCols.x = m->pos[0]; + wallCols.y = m->pos[1]; + wallCols.z = m->pos[2]; + wallCols.radius = 10.0f; + wallCols.offsetY = -10.0f; + + if (find_wall_collisions(&wallCols) != 0) { + floorHeight = find_floor(wallCols.x, wallCols.y, wallCols.z, &floor); + if (floor != NULL && (wallCols.y - floorHeight > 160.0f)) { + wall = wallCols.walls[wallCols.numWalls - 1]; + wallAngle = atan2s(wall->normal.z, wall->normal.x); + wallDYaw = wallAngle - m->faceAngle[1]; + + if (wallDYaw > -0x4000 && wallDYaw < 0x4000) { + m->pos[0] = wallCols.x - 20.0f * wall->normal.x; + m->pos[2] = wallCols.z - 20.0f * wall->normal.z; + + m->faceAngle[0] = 0; + m->faceAngle[1] = wallAngle + 0x8000; + + set_mario_action(m, ACT_LEDGE_CLIMB_DOWN, 0); + set_mario_animation(m, MARIO_ANIM_CLIMB_DOWN_LEDGE); + } + } + } + } +} + +void slide_bonk(struct MarioState *m, u32 fastAction, u32 slowAction) { + if (m->forwardVel > 16.0f) { + mario_bonk_reflection(m, TRUE); + drop_and_set_mario_action(m, fastAction, 0); + } else { + mario_set_forward_vel(m, 0.0f); + set_mario_action(m, slowAction, 0); + } +} + +s32 set_triple_jump_action(struct MarioState *m, UNUSED u32 action, UNUSED u32 actionArg) { + if (m->flags & MARIO_WING_CAP) { + return set_mario_action(m, ACT_FLYING_TRIPLE_JUMP, 0); + } else if (m->forwardVel > 20.0f) { + return set_mario_action(m, ACT_TRIPLE_JUMP, 0); + } else { + return set_mario_action(m, ACT_JUMP, 0); + } + + return FALSE; +} + +void update_sliding_angle(struct MarioState *m, f32 accel, f32 lossFactor) { + s32 newFacingDYaw; + s16 facingDYaw; + + struct Surface *floor = m->floor; + s16 slopeAngle = atan2s(floor->normal.z, floor->normal.x); + f32 steepness = sqrtf(floor->normal.x * floor->normal.x + floor->normal.z * floor->normal.z); + UNUSED f32 normalY = floor->normal.y; + + m->slideVelX += accel * steepness * sins(slopeAngle); + m->slideVelZ += accel * steepness * coss(slopeAngle); + + m->slideVelX *= lossFactor; + m->slideVelZ *= lossFactor; + + m->slideYaw = atan2s(m->slideVelZ, m->slideVelX); + + facingDYaw = m->faceAngle[1] - m->slideYaw; + newFacingDYaw = facingDYaw; + + //! -0x4000 not handled - can slide down a slope while facing perpendicular to it + if (newFacingDYaw > 0 && newFacingDYaw <= 0x4000) { + if ((newFacingDYaw -= 0x200) < 0) { + newFacingDYaw = 0; + } + } else if (newFacingDYaw > -0x4000 && newFacingDYaw < 0) { + if ((newFacingDYaw += 0x200) > 0) { + newFacingDYaw = 0; + } + } else if (newFacingDYaw > 0x4000 && newFacingDYaw < 0x8000) { + if ((newFacingDYaw += 0x200) > 0x8000) { + newFacingDYaw = 0x8000; + } + } else if (newFacingDYaw > -0x8000 && newFacingDYaw < -0x4000) { + if ((newFacingDYaw -= 0x200) < -0x8000) { + newFacingDYaw = -0x8000; + } + } + + m->faceAngle[1] = m->slideYaw + newFacingDYaw; + + m->vel[0] = m->slideVelX; + m->vel[1] = 0.0f; + m->vel[2] = m->slideVelZ; + + mario_update_moving_sand(m); + mario_update_windy_ground(m); + + //! Speed is capped a frame late (butt slide HSG) + m->forwardVel = sqrtf(m->slideVelX * m->slideVelX + m->slideVelZ * m->slideVelZ); + if (m->forwardVel > 100.0f) { + m->slideVelX = m->slideVelX * 100.0f / m->forwardVel; + m->slideVelZ = m->slideVelZ * 100.0f / m->forwardVel; + } + + if (newFacingDYaw < -0x4000 || newFacingDYaw > 0x4000) { + m->forwardVel *= -1.0f; + } +} + +s32 update_sliding(struct MarioState *m, f32 stopSpeed) { + f32 lossFactor; + f32 accel; + f32 oldSpeed; + f32 newSpeed; + + s32 stopped = FALSE; + + s16 intendedDYaw = m->intendedYaw - m->slideYaw; + f32 forward = coss(intendedDYaw); + f32 sideward = sins(intendedDYaw); + + //! 10k glitch + if (forward < 0.0f && m->forwardVel >= 0.0f) { + forward *= 0.5f + 0.5f * m->forwardVel / 100.0f; + } + + switch (mario_get_floor_class(m)) { + case SURFACE_CLASS_VERY_SLIPPERY: + accel = 10.0f; + lossFactor = m->intendedMag / 32.0f * forward * 0.02f + 0.98f; + break; + + case SURFACE_CLASS_SLIPPERY: + accel = 8.0f; + lossFactor = m->intendedMag / 32.0f * forward * 0.02f + 0.96f; + break; + + default: + accel = 7.0f; + lossFactor = m->intendedMag / 32.0f * forward * 0.02f + 0.92f; + break; + + case SURFACE_CLASS_NOT_SLIPPERY: + accel = 5.0f; + lossFactor = m->intendedMag / 32.0f * forward * 0.02f + 0.92f; + break; + } + + oldSpeed = sqrtf(m->slideVelX * m->slideVelX + m->slideVelZ * m->slideVelZ); + + //! This is attempting to use trig derivatives to rotate Mario's speed. + // It is slightly off/asymmetric since it uses the new X speed, but the old + // Z speed. + m->slideVelX += m->slideVelZ * (m->intendedMag / 32.0f) * sideward * 0.05f; + m->slideVelZ -= m->slideVelX * (m->intendedMag / 32.0f) * sideward * 0.05f; + + newSpeed = sqrtf(m->slideVelX * m->slideVelX + m->slideVelZ * m->slideVelZ); + + if (oldSpeed > 0.0f && newSpeed > 0.0f) { + m->slideVelX = m->slideVelX * oldSpeed / newSpeed; + m->slideVelZ = m->slideVelZ * oldSpeed / newSpeed; + } + + update_sliding_angle(m, accel, lossFactor); + + if (!mario_floor_is_slope(m) && m->forwardVel * m->forwardVel < stopSpeed * stopSpeed) { + mario_set_forward_vel(m, 0.0f); + stopped = TRUE; + } + + return stopped; +} + +void apply_slope_accel(struct MarioState *m) { + f32 slopeAccel; + + struct Surface *floor = m->floor; + f32 steepness = sqrtf(floor->normal.x * floor->normal.x + floor->normal.z * floor->normal.z); + + UNUSED f32 normalY = floor->normal.y; + s16 floorDYaw = m->floorAngle - m->faceAngle[1]; + + if (mario_floor_is_slope(m)) { + s16 slopeClass = 0; + + if (m->action != ACT_SOFT_BACKWARD_GROUND_KB && m->action != ACT_SOFT_FORWARD_GROUND_KB) { + slopeClass = mario_get_floor_class(m); + } + + switch (slopeClass) { + case SURFACE_CLASS_VERY_SLIPPERY: + slopeAccel = 5.3f; + break; + case SURFACE_CLASS_SLIPPERY: + slopeAccel = 2.7f; + break; + default: + slopeAccel = 1.7f; + break; + case SURFACE_CLASS_NOT_SLIPPERY: + slopeAccel = 0.0f; + break; + } + + if (floorDYaw > -0x4000 && floorDYaw < 0x4000) { + m->forwardVel += slopeAccel * steepness; + } else { + m->forwardVel -= slopeAccel * steepness; + } + } + + m->slideYaw = m->faceAngle[1]; + + m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); + + m->vel[0] = m->slideVelX; + m->vel[1] = 0.0f; + m->vel[2] = m->slideVelZ; + + mario_update_moving_sand(m); + mario_update_windy_ground(m); +} + +s32 apply_landing_accel(struct MarioState *m, f32 frictionFactor) { + s32 stopped = FALSE; + + apply_slope_accel(m); + + if (!mario_floor_is_slope(m)) { + m->forwardVel *= frictionFactor; + if (m->forwardVel * m->forwardVel < 1.0f) { + mario_set_forward_vel(m, 0.0f); + stopped = TRUE; + } + } + + return stopped; +} + +void update_shell_speed(struct MarioState *m) { + f32 maxTargetSpeed; + f32 targetSpeed; + + if (m->floorHeight < m->waterLevel) { + m->floorHeight = m->waterLevel; + m->floor = &gWaterSurfacePseudoFloor; + m->floor->originOffset = m->waterLevel; //! Negative origin offset + } + + if (m->floor != NULL && m->floor->type == SURFACE_SLOW) { + maxTargetSpeed = 48.0f; + } else { + maxTargetSpeed = 64.0f; + } + + targetSpeed = m->intendedMag * 2.0f; + if (targetSpeed > maxTargetSpeed) { + targetSpeed = maxTargetSpeed; + } + if (targetSpeed < 24.0f) { + targetSpeed = 24.0f; + } + + if (m->forwardVel <= 0.0f) { + m->forwardVel += 1.1f; + } else if (m->forwardVel <= targetSpeed) { + m->forwardVel += 1.1f - m->forwardVel / 58.0f; + } else if (m->floor->normal.y >= 0.95f) { + m->forwardVel -= 1.0f; + } + + //! No backward speed cap (shell hyperspeed) + if (m->forwardVel > 64.0f) { + m->forwardVel = 64.0f; + } + + m->faceAngle[1] = + m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + + apply_slope_accel(m); +} + +s32 apply_slope_decel(struct MarioState *m, f32 decelCoef) { + f32 decel; + s32 stopped = FALSE; + + switch (mario_get_floor_class(m)) { + case SURFACE_CLASS_VERY_SLIPPERY: + decel = decelCoef * 0.2f; + break; + case SURFACE_CLASS_SLIPPERY: + decel = decelCoef * 0.7f; + break; + default: + decel = decelCoef * 2.0f; + break; + case SURFACE_CLASS_NOT_SLIPPERY: + decel = decelCoef * 3.0f; + break; + } + + if ((m->forwardVel = approach_f32(m->forwardVel, 0.0f, decel, decel)) == 0.0f) { + stopped = TRUE; + } + + apply_slope_accel(m); + return stopped; +} + +s32 update_decelerating_speed(struct MarioState *m) { + s32 stopped = FALSE; + + if ((m->forwardVel = approach_f32(m->forwardVel, 0.0f, 1.0f, 1.0f)) == 0.0f) { + stopped = TRUE; + } + + mario_set_forward_vel(m, m->forwardVel); + mario_update_moving_sand(m); + mario_update_windy_ground(m); + + return stopped; +} + +void update_walking_speed(struct MarioState *m) { + f32 maxTargetSpeed; + f32 targetSpeed; + + if (m->floor != NULL && m->floor->type == SURFACE_SLOW) { + maxTargetSpeed = 24.0f; + } else { + maxTargetSpeed = 32.0f; + } + + targetSpeed = m->intendedMag < maxTargetSpeed ? m->intendedMag : maxTargetSpeed; + + if (m->quicksandDepth > 10.0f) { + targetSpeed *= 6.25 / m->quicksandDepth; + } + + if (m->forwardVel <= 0.0f) { + m->forwardVel += 1.1f; + } else if (m->forwardVel <= targetSpeed) { + m->forwardVel += 1.1f - m->forwardVel / 43.0f; + } else if (m->floor->normal.y >= 0.95f) { + m->forwardVel -= 1.0f; + } + + if (m->forwardVel > 48.0f) { + m->forwardVel = 48.0f; + } + + m->faceAngle[1] = + m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + apply_slope_accel(m); +} + +s32 should_begin_sliding(struct MarioState *m) { + if (m->input & INPUT_ABOVE_SLIDE) { + s32 slideLevel = (m->area->terrainType & TERRAIN_MASK) == TERRAIN_SLIDE; + s32 movingBackward = m->forwardVel <= -1.0f; + + if (slideLevel || movingBackward || mario_facing_downhill(m, FALSE)) { + return TRUE; + } + } + + return FALSE; +} + +s32 analog_stick_held_back(struct MarioState *m) { + s16 intendedDYaw = m->intendedYaw - m->faceAngle[1]; + return intendedDYaw < -0x471C || intendedDYaw > 0x471C; +} + +s32 check_ground_dive_or_punch(struct MarioState *m) { + UNUSED s32 unused; + + if (m->input & INPUT_B_PRESSED) { + //! Speed kick (shoutouts to SimpleFlips) + if (m->forwardVel >= 29.0f && m->controller->stickMag > 48.0f) { + m->vel[1] = 20.0f; + return set_mario_action(m, ACT_DIVE, 1); + } + + return set_mario_action(m, ACT_MOVE_PUNCHING, 0); + } + + return FALSE; +} + +s32 begin_braking_action(struct MarioState *m) { + mario_drop_held_object(m); + + if (m->actionState == 1) { + m->faceAngle[1] = m->actionArg; + return set_mario_action(m, ACT_STANDING_AGAINST_WALL, 0); + } + + if (m->forwardVel >= 16.0f && m->floor->normal.y >= 0.17364818f) { + return set_mario_action(m, ACT_BRAKING, 0); + } + + return set_mario_action(m, ACT_DECELERATING, 0); +} + +void anim_and_audio_for_walk(struct MarioState *m) { + s32 val14; + struct Object *marioObj = m->marioObj; + s32 val0C = TRUE; + s16 targetPitch = 0; + f32 val04; + + val04 = m->intendedMag > m->forwardVel ? m->intendedMag : m->forwardVel; + + if (val04 < 4.0f) { + val04 = 4.0f; + } + + if (m->quicksandDepth > 50.0f) { + val14 = (s32)(val04 / 4.0f * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_MOVE_IN_QUICKSAND, val14); + play_step_sound(m, 19, 93); + m->actionTimer = 0; + } else { + while (val0C) { + switch (m->actionTimer) { + case 0: + if (val04 > 8.0f) { + m->actionTimer = 2; + } else { + //! (Speed Crash) If Mario's speed is more than 2^17. + if ((val14 = (s32)(val04 / 4.0f * 0x10000)) < 0x1000) { + val14 = 0x1000; + } + set_mario_anim_with_accel(m, MARIO_ANIM_START_TIPTOE, val14); + play_step_sound(m, 7, 22); + if (is_anim_past_frame(m, 23)) { + m->actionTimer = 2; + } + + val0C = FALSE; + } + break; + + case 1: + if (val04 > 8.0f) { + m->actionTimer = 2; + } else { + //! (Speed Crash) If Mario's speed is more than 2^17. + if ((val14 = (s32)(val04 * 0x10000)) < 0x1000) { + val14 = 0x1000; + } + set_mario_anim_with_accel(m, MARIO_ANIM_TIPTOE, val14); + play_step_sound(m, 14, 72); + + val0C = FALSE; + } + break; + + case 2: + if (val04 < 5.0f) { + m->actionTimer = 1; + } else if (val04 > 22.0f) { + m->actionTimer = 3; + } else { + //! (Speed Crash) If Mario's speed is more than 2^17. + val14 = (s32)(val04 / 4.0f * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, val14); + play_step_sound(m, 10, 49); + + val0C = FALSE; + } + break; + + case 3: + if (val04 < 18.0f) { + m->actionTimer = 2; + } else { + //! (Speed Crash) If Mario's speed is more than 2^17. + val14 = (s32)(val04 / 4.0f * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_RUNNING, val14); + play_step_sound(m, 9, 45); + targetPitch = tilt_body_running(m); + + val0C = FALSE; + } + break; + } + } + } + + marioObj->oMarioWalkingPitch = + (s16) approach_s32(marioObj->oMarioWalkingPitch, targetPitch, 0x800, 0x800); + marioObj->header.gfx.angle[0] = marioObj->oMarioWalkingPitch; +} + +void anim_and_audio_for_hold_walk(struct MarioState *m) { + s32 val0C; + s32 val08 = TRUE; + f32 val04; + + val04 = m->intendedMag > m->forwardVel ? m->intendedMag : m->forwardVel; + + if (val04 < 2.0f) { + val04 = 2.0f; + } + + while (val08) { + switch (m->actionTimer) { + case 0: + if (val04 > 6.0f) { + m->actionTimer = 1; + } else { + //! (Speed Crash) Crashes if Mario's speed exceeds or equals 2^15. + val0C = (s32)(val04 * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_SLOW_WALK_WITH_LIGHT_OBJ, val0C); + play_step_sound(m, 12, 62); + + val08 = FALSE; + } + break; + + case 1: + if (val04 < 3.0f) { + m->actionTimer = 0; + } else if (val04 > 11.0f) { + m->actionTimer = 2; + } else { + //! (Speed Crash) Crashes if Mario's speed exceeds or equals 2^15. + val0C = (s32)(val04 * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_WALK_WITH_LIGHT_OBJ, val0C); + play_step_sound(m, 12, 62); + + val08 = FALSE; + } + break; + + case 2: + if (val04 < 8.0f) { + m->actionTimer = 1; + } else { + //! (Speed Crash) Crashes if Mario's speed exceeds or equals 2^16. + val0C = (s32)(val04 / 2.0f * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_RUN_WITH_LIGHT_OBJ, val0C); + play_step_sound(m, 10, 49); + + val08 = FALSE; + } + break; + } + } +} + +void anim_and_audio_for_heavy_walk(struct MarioState *m) { + s32 val04 = (s32)(m->intendedMag * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_WALK_WITH_HEAVY_OBJ, val04); + play_step_sound(m, 26, 79); +} + +void push_or_sidle_wall(struct MarioState *m, Vec3f startPos) { + s16 wallAngle; + s16 dWallAngle; + f32 dx = m->pos[0] - startPos[0]; + f32 dz = m->pos[2] - startPos[2]; + f32 movedDistance = sqrtf(dx * dx + dz * dz); + //! (Speed Crash) If a wall is after moving 16384 distance, this crashes. + s32 val04 = (s32)(movedDistance * 2.0f * 0x10000); + + if (m->forwardVel > 6.0f) { + mario_set_forward_vel(m, 6.0f); + } + + if (m->wall != NULL) { + wallAngle = atan2s(m->wall->normal.z, m->wall->normal.x); + dWallAngle = wallAngle - m->faceAngle[1]; + } + + if (m->wall == NULL || dWallAngle <= -0x71C8 || dWallAngle >= 0x71C8) { + m->flags |= MARIO_UNKNOWN_31; + set_mario_animation(m, MARIO_ANIM_PUSHING); + play_step_sound(m, 6, 18); + } else { + if (dWallAngle < 0) { + set_mario_anim_with_accel(m, MARIO_ANIM_SIDESTEP_RIGHT, val04); + } else { + set_mario_anim_with_accel(m, MARIO_ANIM_SIDESTEP_LEFT, val04); + } + + if (m->marioObj->header.gfx.animInfo.animFrame < 20) { + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_DUST; + } + + m->actionState = 1; + m->actionArg = wallAngle + 0x8000; + m->marioObj->header.gfx.angle[1] = wallAngle + 0x8000; + m->marioObj->header.gfx.angle[2] = find_floor_slope(m, 0x4000); + } +} + +void tilt_body_walking(struct MarioState *m, s16 startYaw) { + struct MarioBodyState *val0C = m->marioBodyState; + UNUSED struct Object *marioObj = m->marioObj; + s16 animID = m->marioObj->header.gfx.animInfo.animID; + s16 dYaw; + s16 val02; + s16 val00; + + if (animID == MARIO_ANIM_WALKING || animID == MARIO_ANIM_RUNNING) { + dYaw = m->faceAngle[1] - startYaw; + //! (Speed Crash) These casts can cause a crash if (dYaw * forwardVel / 12) or + //! (forwardVel * 170) exceed or equal 2^31. + val02 = -(s16)(dYaw * m->forwardVel / 12.0f); + val00 = (s16)(m->forwardVel * 170.0f); + + if (val02 > 0x1555) { + val02 = 0x1555; + } + if (val02 < -0x1555) { + val02 = -0x1555; + } + + if (val00 > 0x1555) { + val00 = 0x1555; + } + if (val00 < 0) { + val00 = 0; + } + + val0C->torsoAngle[2] = approach_s32(val0C->torsoAngle[2], val02, 0x400, 0x400); + val0C->torsoAngle[0] = approach_s32(val0C->torsoAngle[0], val00, 0x400, 0x400); + ; + } else { + val0C->torsoAngle[2] = 0; + val0C->torsoAngle[0] = 0; + } +} + +void tilt_body_ground_shell(struct MarioState *m, s16 startYaw) { + struct MarioBodyState *val0C = m->marioBodyState; + struct Object *marioObj = m->marioObj; + s16 dYaw = m->faceAngle[1] - startYaw; + //! (Speed Crash) These casts can cause a crash if (dYaw * forwardVel / 12) or + //! (forwardVel * 170) exceed or equal 2^31. Harder (if not impossible to do) + //! while on a Koopa Shell making this less of an issue. + s16 val04 = -(s16)(dYaw * m->forwardVel / 12.0f); + s16 val02 = (s16)(m->forwardVel * 170.0f); + + if (val04 > 0x1800) { + val04 = 0x1800; + } + if (val04 < -0x1800) { + val04 = -0x1800; + } + + if (val02 > 0x1000) { + val02 = 0x1000; + } + if (val02 < 0) { + val02 = 0; + } + + val0C->torsoAngle[2] = approach_s32(val0C->torsoAngle[2], val04, 0x200, 0x200); + val0C->torsoAngle[0] = approach_s32(val0C->torsoAngle[0], val02, 0x200, 0x200); + val0C->headAngle[2] = -val0C->torsoAngle[2]; + + marioObj->header.gfx.angle[2] = val0C->torsoAngle[2]; + marioObj->header.gfx.pos[1] += 45.0f; +} + +s32 act_walking(struct MarioState *m) { + Vec3f startPos; + s16 startYaw = m->faceAngle[1]; + + mario_drop_held_object(m); + + if (should_begin_sliding(m)) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return begin_braking_action(m); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jump_from_landing(m); + } + + if (check_ground_dive_or_punch(m)) { + return TRUE; + } + + if (m->input & INPUT_UNKNOWN_5) { + return begin_braking_action(m); + } + + if (analog_stick_held_back(m) && m->forwardVel >= 16.0f) { + return set_mario_action(m, ACT_TURNING_AROUND, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_CROUCH_SLIDE, 0); + } + + m->actionState = 0; + + vec3f_copy(startPos, m->pos); + update_walking_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 0); + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + break; + + case GROUND_STEP_NONE: + anim_and_audio_for_walk(m); + if (m->intendedMag - m->forwardVel > 16.0f) { + m->particleFlags |= PARTICLE_DUST; + } + break; + + case GROUND_STEP_HIT_WALL: + push_or_sidle_wall(m, startPos); + m->actionTimer = 0; + break; + } + + check_ledge_climb_down(m); + tilt_body_walking(m, startYaw); + return FALSE; +} + +s32 act_move_punching(struct MarioState *m) { + if (should_begin_sliding(m)) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->actionState == 0 && (m->input & INPUT_A_DOWN)) { + return set_mario_action(m, ACT_JUMP_KICK, 0); + } + + m->actionState = 1; + + mario_update_punch_sequence(m); + + if (m->forwardVel >= 0.0f) { + apply_slope_decel(m, 0.5f); + } else { + if ((m->forwardVel += 8.0f) >= 0.0f) { + m->forwardVel = 0.0f; + } + apply_slope_accel(m); + } + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 0); + break; + + case GROUND_STEP_NONE: + m->particleFlags |= PARTICLE_DUST; + break; + } + + return FALSE; +} + +s32 act_hold_walking(struct MarioState *m) { +// if (m->heldObj->behavior == segmented_to_virtual(bhvJumpingBox)) { +// return set_mario_action(m, ACT_CRAZY_BOX_BOUNCE, 0); +// } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WALKING, 0); + } + + if (should_begin_sliding(m)) { + return set_mario_action(m, ACT_HOLD_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_THROWING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_HOLD_JUMP, 0); + } + + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_HOLD_DECELERATING, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return drop_and_set_mario_action(m, ACT_CROUCH_SLIDE, 0); + } + + m->intendedMag *= 0.4f; + + update_walking_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_HOLD_FREEFALL, 0); + break; + + case GROUND_STEP_HIT_WALL: + if (m->forwardVel > 16.0f) { + mario_set_forward_vel(m, 16.0f); + } + break; + } + + anim_and_audio_for_hold_walk(m); + + if (0.4f * m->intendedMag - m->forwardVel > 10.0f) { + m->particleFlags |= PARTICLE_DUST; + } + + return FALSE; +} + +s32 act_hold_heavy_walking(struct MarioState *m) { + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_HEAVY_THROW, 0); + } + + if (should_begin_sliding(m)) { + return drop_and_set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_HOLD_HEAVY_IDLE, 0); + } + + m->intendedMag *= 0.1f; + + update_walking_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + drop_and_set_mario_action(m, ACT_FREEFALL, 0); + break; + + case GROUND_STEP_HIT_WALL: + if (m->forwardVel > 10.0f) { + mario_set_forward_vel(m, 10.0f); + } + break; + } + + anim_and_audio_for_heavy_walk(m); + return FALSE; +} + +s32 act_turning_around(struct MarioState *m) { + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_SIDE_FLIP, 0); + } + + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_BRAKING, 0); + } + + if (!analog_stick_held_back(m)) { + return set_mario_action(m, ACT_WALKING, 0); + } + + if (apply_slope_decel(m, 2.0f)) { + return begin_walking_action(m, 8.0f, ACT_FINISH_TURNING_AROUND, 0); + } + + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + + adjust_sound_for_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 0); + break; + + case GROUND_STEP_NONE: + m->particleFlags |= PARTICLE_DUST; + break; + } + + if (m->forwardVel >= 18.0f) { + set_mario_animation(m, MARIO_ANIM_TURNING_PART1); + } else { + set_mario_animation(m, MARIO_ANIM_TURNING_PART2); + if (is_anim_at_end(m)) { + if (m->forwardVel > 0.0f) { + begin_walking_action(m, -m->forwardVel, ACT_WALKING, 0); + } else { + begin_walking_action(m, 8.0f, ACT_WALKING, 0); + } + } + } + + return FALSE; +} + +s32 act_finish_turning_around(struct MarioState *m) { + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_SIDE_FLIP, 0); + } + + update_walking_speed(m); + set_mario_animation(m, MARIO_ANIM_TURNING_PART2); + + if (perform_ground_step(m) == GROUND_STEP_LEFT_GROUND) { + set_mario_action(m, ACT_FREEFALL, 0); + } + + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_WALKING, 0); + } + + m->marioObj->header.gfx.angle[1] += 0x8000; + return FALSE; +} + +s32 act_braking(struct MarioState *m) { + if (!(m->input & INPUT_FIRST_PERSON) + && (m->input + & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE))) { + return check_common_action_exits(m); + } + + if (apply_slope_decel(m, 2.0f)) { + return set_mario_action(m, ACT_BRAKING_STOP, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_MOVE_PUNCHING, 0); + } + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 0); + break; + + case GROUND_STEP_NONE: + m->particleFlags |= PARTICLE_DUST; + break; + + case GROUND_STEP_HIT_WALL: + slide_bonk(m, ACT_BACKWARD_GROUND_KB, ACT_BRAKING_STOP); + break; + } + + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + adjust_sound_for_speed(m); + set_mario_animation(m, MARIO_ANIM_SKID_ON_GROUND); + return FALSE; +} + +s32 act_decelerating(struct MarioState *m) { + s32 val0C; + s16 slopeClass = mario_get_floor_class(m); + + if (!(m->input & INPUT_FIRST_PERSON)) { + if (should_begin_sliding(m)) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jump_from_landing(m); + } + + if (check_ground_dive_or_punch(m)) { + return TRUE; + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_WALKING, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return set_mario_action(m, ACT_CROUCH_SLIDE, 0); + } + } + + if (update_decelerating_speed(m)) { + return set_mario_action(m, ACT_IDLE, 0); + } + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 0); + break; + + case GROUND_STEP_HIT_WALL: + if (slopeClass == SURFACE_CLASS_VERY_SLIPPERY) { + mario_bonk_reflection(m, TRUE); + } else { + mario_set_forward_vel(m, 0.0f); + } + break; + } + + if (slopeClass == SURFACE_CLASS_VERY_SLIPPERY) { + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_LEFT); + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + adjust_sound_for_speed(m); + m->particleFlags |= PARTICLE_DUST; + } else { + // (Speed Crash) Crashes if speed exceeds 2^17. + if ((val0C = (s32)(m->forwardVel / 4.0f * 0x10000)) < 0x1000) { + val0C = 0x1000; + } + + set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, val0C); + play_step_sound(m, 10, 49); + } + + return FALSE; +} + +s32 act_hold_decelerating(struct MarioState *m) { + s32 val0C; + s16 slopeClass = mario_get_floor_class(m); + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WALKING, 0); + } + + if (should_begin_sliding(m)) { + return set_mario_action(m, ACT_HOLD_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_THROWING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_HOLD_JUMP, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + return drop_and_set_mario_action(m, ACT_CROUCH_SLIDE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HOLD_WALKING, 0); + } + + if (update_decelerating_speed(m)) { + return set_mario_action(m, ACT_HOLD_IDLE, 0); + } + + m->intendedMag *= 0.4f; + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_HOLD_FREEFALL, 0); + break; + + case GROUND_STEP_HIT_WALL: + if (slopeClass == SURFACE_CLASS_VERY_SLIPPERY) { + mario_bonk_reflection(m, TRUE); + } else { + mario_set_forward_vel(m, 0.0f); + } + break; + } + + if (slopeClass == SURFACE_CLASS_VERY_SLIPPERY) { + set_mario_animation(m, MARIO_ANIM_IDLE_WITH_LIGHT_OBJ); + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + adjust_sound_for_speed(m); + m->particleFlags |= PARTICLE_DUST; + } else { + //! (Speed Crash) This crashes if Mario has more speed than 2^15 speed. + if ((val0C = (s32)(m->forwardVel * 0x10000)) < 0x1000) { + val0C = 0x1000; + } + + set_mario_anim_with_accel(m, MARIO_ANIM_WALK_WITH_LIGHT_OBJ, val0C); + play_step_sound(m, 12, 62); + } + + return FALSE; +} + +s32 act_riding_shell_ground(struct MarioState *m) { + s16 startYaw = m->faceAngle[1]; + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_RIDING_SHELL_JUMP, 0); + } + + if (m->input & INPUT_Z_PRESSED) { + mario_stop_riding_object(m); + if (m->forwardVel < 24.0f) { + mario_set_forward_vel(m, 24.0f); + } + return set_mario_action(m, ACT_CROUCH_SLIDE, 0); + } + + update_shell_speed(m); + set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_START_RIDING_SHELL : MARIO_ANIM_RIDING_SHELL); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_RIDING_SHELL_FALL, 0); + break; + + case GROUND_STEP_HIT_WALL: + mario_stop_riding_object(m); + play_sound(m->flags & MARIO_METAL_CAP ? SOUND_ACTION_METAL_BONK : SOUND_ACTION_BONK, + m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_GROUND_KB, 0); + break; + } + + tilt_body_ground_shell(m, startYaw); + if (m->floor->type == SURFACE_BURNING) { + play_sound(SOUND_MOVING_RIDING_SHELL_LAVA, m->marioObj->header.gfx.cameraToObject); + } else { + play_sound(SOUND_MOVING_TERRAIN_RIDING_SHELL + m->terrainSoundAddend, + m->marioObj->header.gfx.cameraToObject); + } + + adjust_sound_for_speed(m); +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + return FALSE; +} + +s32 act_crawling(struct MarioState *m) { + s32 val04; + + if (should_begin_sliding(m)) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_STOP_CRAWLING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_JUMP, 0); + } + + if (check_ground_dive_or_punch(m)) { + return TRUE; + } + + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_STOP_CRAWLING, 0); + } + + if (!(m->input & INPUT_Z_DOWN)) { + return set_mario_action(m, ACT_STOP_CRAWLING, 0); + } + + m->intendedMag *= 0.1f; + + update_walking_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 0); + break; + + case GROUND_STEP_HIT_WALL: + if (m->forwardVel > 10.0f) { + mario_set_forward_vel(m, 10.0f); + } + //! Possibly unintended missing break + + case GROUND_STEP_NONE: + align_with_floor(m); + break; + } + + val04 = (s32)(m->intendedMag * 2.0f * 0x10000); + set_mario_anim_with_accel(m, MARIO_ANIM_CRAWLING, val04); + play_step_sound(m, 26, 79); + return FALSE; +} + +s32 act_burning_ground(struct MarioState *m) { + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_BURNING_JUMP, 0); + } + + m->marioObj->oMarioBurnTimer += 2; + if (m->marioObj->oMarioBurnTimer > 160) { + return set_mario_action(m, ACT_WALKING, 0); + } + + if (m->waterLevel - m->floorHeight > 50.0f) { + play_sound(SOUND_GENERAL_FLAME_OUT, m->marioObj->header.gfx.cameraToObject); + return set_mario_action(m, ACT_WALKING, 0); + } + + if (m->forwardVel < 8.0f) { + m->forwardVel = 8.0f; + } + if (m->forwardVel > 48.0f) { + m->forwardVel = 48.0f; + } + + m->forwardVel = approach_f32(m->forwardVel, 32.0f, 4.0f, 1.0f); + + if (m->input & INPUT_NONZERO_ANALOG) { + m->faceAngle[1] = + m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x600, 0x600); + } + + apply_slope_accel(m); + + if (perform_ground_step(m) == GROUND_STEP_LEFT_GROUND) { + set_mario_action(m, ACT_BURNING_FALL, 0); + } + + set_mario_anim_with_accel(m, MARIO_ANIM_RUNNING, (s32)(m->forwardVel / 2.0f * 0x10000)); + play_step_sound(m, 9, 45); + + m->particleFlags |= PARTICLE_FIRE; + play_sound(SOUND_MOVING_LAVA_BURN, m->marioObj->header.gfx.cameraToObject); + + m->health -= 10; + if (m->health < 0x100) { + set_mario_action(m, ACT_STANDING_DEATH, 0); + } + + m->marioBodyState->eyeState = MARIO_EYES_DEAD; +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + return FALSE; +} + +void tilt_body_butt_slide(struct MarioState *m) { + s16 intendedDYaw = m->intendedYaw - m->faceAngle[1]; + m->marioBodyState->torsoAngle[0] = (s32)(5461.3335f * m->intendedMag / 32.0f * coss(intendedDYaw)); + m->marioBodyState->torsoAngle[2] = (s32)(-(5461.3335f * m->intendedMag / 32.0f * sins(intendedDYaw))); +} + +void common_slide_action(struct MarioState *m, u32 endAction, u32 airAction, s32 animation) { + Vec3f pos; + + vec3f_copy(pos, m->pos); + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + + adjust_sound_for_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, airAction, 0); + if (m->forwardVel < -50.0f || 50.0f < m->forwardVel) { + play_sound(SOUND_MARIO_HOOHOO, m->marioObj->header.gfx.cameraToObject); + } + break; + + case GROUND_STEP_NONE: + set_mario_animation(m, animation); + align_with_floor(m); + m->particleFlags |= PARTICLE_DUST; + break; + + case GROUND_STEP_HIT_WALL: + if (!mario_floor_is_slippery(m)) { +#ifdef VERSION_JP + m->particleFlags |= PARTICLE_VERTICAL_STAR; +#else + if (m->forwardVel > 16.0f) { + m->particleFlags |= PARTICLE_VERTICAL_STAR; + } +#endif + slide_bonk(m, ACT_GROUND_BONK, endAction); + } else if (m->wall != NULL) { + s16 wallAngle = atan2s(m->wall->normal.z, m->wall->normal.x); + f32 slideSpeed = sqrtf(m->slideVelX * m->slideVelX + m->slideVelZ * m->slideVelZ); + + if ((slideSpeed *= 0.9) < 4.0f) { + slideSpeed = 4.0f; + } + + m->slideYaw = wallAngle - (s16)(m->slideYaw - wallAngle) + 0x8000; + + m->vel[0] = m->slideVelX = slideSpeed * sins(m->slideYaw); + m->vel[2] = m->slideVelZ = slideSpeed * coss(m->slideYaw); + } + + align_with_floor(m); + break; + } +} + +s32 common_slide_action_with_jump(struct MarioState *m, u32 stopAction, u32 jumpAction, u32 airAction, + s32 animation) { + if (m->actionTimer == 5) { + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, jumpAction, 0); + } + } else { + m->actionTimer++; + } + + if (update_sliding(m, 4.0f)) { + return set_mario_action(m, stopAction, 0); + } + + common_slide_action(m, stopAction, airAction, animation); + return FALSE; +} + +s32 act_butt_slide(struct MarioState *m) { + s32 cancel = common_slide_action_with_jump(m, ACT_BUTT_SLIDE_STOP, ACT_JUMP, ACT_BUTT_SLIDE_AIR, + MARIO_ANIM_SLIDE); + tilt_body_butt_slide(m); + return cancel; +} + +s32 act_hold_butt_slide(struct MarioState *m) { + s32 cancel; + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_BUTT_SLIDE, 0); + } + + cancel = common_slide_action_with_jump(m, ACT_HOLD_BUTT_SLIDE_STOP, ACT_HOLD_JUMP, ACT_HOLD_BUTT_SLIDE_AIR, + MARIO_ANIM_SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ); + tilt_body_butt_slide(m); + return cancel; +} + +s32 act_crouch_slide(struct MarioState *m) { + s32 cancel; + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BUTT_SLIDE, 0); + } + + if (m->actionTimer < 30) { + m->actionTimer++; + if (m->input & INPUT_A_PRESSED) { + if (m->forwardVel > 10.0f) { + return set_jumping_action(m, ACT_LONG_JUMP, 0); + } + } + } + + if (m->input & INPUT_B_PRESSED) { + if (m->forwardVel >= 10.0f) { + return set_mario_action(m, ACT_SLIDE_KICK, 0); + } else { + return set_mario_action(m, ACT_MOVE_PUNCHING, 0x0009); + } + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_JUMP, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_BRAKING, 0); + } + + cancel = common_slide_action_with_jump(m, ACT_CROUCHING, ACT_JUMP, ACT_FREEFALL, + MARIO_ANIM_START_CROUCHING); + return cancel; +} + +s32 act_slide_kick_slide(struct MarioState *m) { + if (m->input & INPUT_A_PRESSED) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + return set_jumping_action(m, ACT_FORWARD_ROLLOUT, 0); + } + + set_mario_animation(m, MARIO_ANIM_SLIDE_KICK); + if (is_anim_at_end(m) && m->forwardVel < 1.0f) { + return set_mario_action(m, ACT_SLIDE_KICK_SLIDE_STOP, 0); + } + + update_sliding(m, 1.0f); + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_FREEFALL, 2); + break; + + case GROUND_STEP_HIT_WALL: + mario_bonk_reflection(m, TRUE); + m->particleFlags |= PARTICLE_VERTICAL_STAR; + set_mario_action(m, ACT_BACKWARD_GROUND_KB, 0); + break; + } + + play_sound(SOUND_MOVING_TERRAIN_SLIDE + m->terrainSoundAddend, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_DUST; + return FALSE; +} + +s32 stomach_slide_action(struct MarioState *m, u32 stopAction, u32 airAction, s32 animation) { + if (m->actionTimer == 5) { + if (!(m->input & INPUT_ABOVE_SLIDE) && (m->input & (INPUT_A_PRESSED | INPUT_B_PRESSED))) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + return drop_and_set_mario_action( + m, m->forwardVel >= 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, 0); + } + } else { + m->actionTimer++; + } + + if (update_sliding(m, 4.0f)) { + return set_mario_action(m, stopAction, 0); + } + + common_slide_action(m, stopAction, airAction, animation); + return FALSE; +} + +s32 act_stomach_slide(struct MarioState *m) { + s32 cancel = stomach_slide_action(m, ACT_STOMACH_SLIDE_STOP, ACT_FREEFALL, MARIO_ANIM_SLIDE_DIVE); + return cancel; +} + +s32 act_hold_stomach_slide(struct MarioState *m) { + s32 cancel; + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_STOMACH_SLIDE, 0); + } + + cancel = stomach_slide_action(m, ACT_DIVE_PICKING_UP, ACT_HOLD_FREEFALL, MARIO_ANIM_SLIDE_DIVE); + return cancel; +} + +s32 act_dive_slide(struct MarioState *m) { + if (!(m->input & INPUT_ABOVE_SLIDE) && (m->input & (INPUT_A_PRESSED | INPUT_B_PRESSED))) { +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + return set_mario_action(m, m->forwardVel > 0.0f ? ACT_FORWARD_ROLLOUT : ACT_BACKWARD_ROLLOUT, + 0); + } + + play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + + //! If the dive slide ends on the same frame that we pick up on object, + // Mario will not be in the dive slide action for the call to + // mario_check_object_grab, and so will end up in the regular picking action, + // rather than the picking up after dive action. + + if (update_sliding(m, 8.0f) && is_anim_at_end(m)) { + mario_set_forward_vel(m, 0.0f); + set_mario_action(m, ACT_STOMACH_SLIDE_STOP, 0); + } + + if (mario_check_object_grab(m)) { + mario_grab_used_object(m); + m->marioBodyState->grabPos = GRAB_POS_LIGHT_OBJ; + return TRUE; + } + + common_slide_action(m, ACT_STOMACH_SLIDE_STOP, ACT_FREEFALL, MARIO_ANIM_DIVE); + return FALSE; +} + +s32 common_ground_knockback_action(struct MarioState *m, s32 animation, s32 arg2, s32 arg3, s32 arg4) { + s32 animFrame; + + if (arg3) { + play_mario_heavy_landing_sound_once(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + } + + if (arg4 > 0) { + play_sound_if_no_flag(m, SOUND_MARIO_ATTACKED, MARIO_MARIO_SOUND_PLAYED); + } else { +#ifdef VERSION_JP + play_sound_if_no_flag(m, SOUND_MARIO_OOOF, MARIO_MARIO_SOUND_PLAYED); +#else + play_sound_if_no_flag(m, SOUND_MARIO_OOOF2, MARIO_MARIO_SOUND_PLAYED); +#endif + } + + if (m->forwardVel > 32.0f) { + m->forwardVel = 32.0f; + } + if (m->forwardVel < -32.0f) { + m->forwardVel = -32.0f; + } + + animFrame = set_mario_animation(m, animation); + if (animFrame < arg2) { + apply_landing_accel(m, 0.9f); + } else if (m->forwardVel >= 0.0f) { + mario_set_forward_vel(m, 0.1f); + } else { + mario_set_forward_vel(m, -0.1f); + } + + if (perform_ground_step(m) == GROUND_STEP_LEFT_GROUND) { + if (m->forwardVel >= 0.0f) { + set_mario_action(m, ACT_FORWARD_AIR_KB, arg4); + } else { + set_mario_action(m, ACT_BACKWARD_AIR_KB, arg4); + } + } else if (is_anim_at_end(m)) { + if (m->health < 0x100) { + set_mario_action(m, ACT_STANDING_DEATH, 0); + } else { + if (arg4 > 0) { + m->invincTimer = 30; + } + set_mario_action(m, ACT_IDLE, 0); + } + } + + return animFrame; +} + +s32 act_hard_backward_ground_kb(struct MarioState *m) { + s32 animFrame = + common_ground_knockback_action(m, MARIO_ANIM_FALL_OVER_BACKWARDS, 43, TRUE, m->actionArg); + if (animFrame == 43 && m->health < 0x100) { + set_mario_action(m, ACT_DEATH_ON_BACK, 0); + } + +#ifndef VERSION_JP + if (animFrame == 54 && m->prevAction == ACT_SPECIAL_DEATH_EXIT) { + play_sound(SOUND_MARIO_MAMA_MIA, m->marioObj->header.gfx.cameraToObject); + } +#endif + + if (animFrame == 69) { + play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING); + } + + return FALSE; +} + +s32 act_hard_forward_ground_kb(struct MarioState *m) { + s32 animFrame = + common_ground_knockback_action(m, MARIO_ANIM_LAND_ON_STOMACH, 21, TRUE, m->actionArg); + if (animFrame == 23 && m->health < 0x100) { + set_mario_action(m, ACT_DEATH_ON_STOMACH, 0); + } + + return FALSE; +} + +s32 act_backward_ground_kb(struct MarioState *m) { + common_ground_knockback_action(m, MARIO_ANIM_BACKWARD_KB, 22, TRUE, m->actionArg); + return FALSE; +} + +s32 act_forward_ground_kb(struct MarioState *m) { + common_ground_knockback_action(m, MARIO_ANIM_FORWARD_KB, 20, TRUE, m->actionArg); + return FALSE; +} + +s32 act_soft_backward_ground_kb(struct MarioState *m) { + common_ground_knockback_action(m, MARIO_ANIM_SOFT_BACK_KB, 100, FALSE, m->actionArg); + return FALSE; +} + +s32 act_soft_forward_ground_kb(struct MarioState *m) { + common_ground_knockback_action(m, MARIO_ANIM_SOFT_FRONT_KB, 100, FALSE, m->actionArg); + return FALSE; +} + +s32 act_ground_bonk(struct MarioState *m) { + s32 animFrame = + common_ground_knockback_action(m, MARIO_ANIM_GROUND_BONK, 32, TRUE, m->actionArg); + if (animFrame == 32) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + return FALSE; +} + +s32 act_death_exit_land(struct MarioState *m) { + s32 animFrame; + + apply_landing_accel(m, 0.9f); + play_mario_heavy_landing_sound_once(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + + animFrame = set_mario_animation(m, MARIO_ANIM_FALL_OVER_BACKWARDS); + + if (animFrame == 54) { + play_sound(SOUND_MARIO_MAMA_MIA, m->marioObj->header.gfx.cameraToObject); + } + if (animFrame == 68) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_IDLE, 0); + } + + return FALSE; +} + +u32 common_landing_action(struct MarioState *m, s16 animation, u32 airAction) { + u32 stepResult; + + if (m->input & INPUT_NONZERO_ANALOG) { + apply_landing_accel(m, 0.98f); + } else if (m->forwardVel >= 16.0f) { + apply_slope_decel(m, 2.0f); + } else { + m->vel[1] = 0.0f; + } + + stepResult = perform_ground_step(m); + switch (stepResult) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, airAction, 0); + break; + + case GROUND_STEP_HIT_WALL: + set_mario_animation(m, MARIO_ANIM_PUSHING); + break; + } + + if (m->forwardVel > 16.0f) { + m->particleFlags |= PARTICLE_DUST; + } + + set_mario_animation(m, animation); + play_mario_landing_sound_once(m, SOUND_ACTION_TERRAIN_LANDING); + + if (m->floor->type >= SURFACE_SHALLOW_QUICKSAND && m->floor->type <= SURFACE_MOVING_QUICKSAND) { + m->quicksandDepth += (4 - m->actionTimer) * 3.5f - 0.5f; + } + + return stepResult; +} + +s32 common_landing_cancels(struct MarioState *m, struct LandingAction *landingAction, + s32 (*setAPressAction)(struct MarioState *, u32, u32)) { + //! Everything here, including floor steepness, is checked before checking + // if Mario is actually on the floor. This leads to e.g. remote sliding. + + if (m->floor->normal.y < 0.2923717f) { + return mario_push_off_steep_floor(m, landingAction->verySteepAction, 0); + } + + m->doubleJumpTimer = landingAction->unk02; + + if (should_begin_sliding(m)) { + return set_mario_action(m, landingAction->slideAction, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, landingAction->endAction, 0); + } + + if (++m->actionTimer >= landingAction->numFrames) { + return set_mario_action(m, landingAction->endAction, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return setAPressAction(m, landingAction->aPressedAction, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, landingAction->offFloorAction, 0); + } + + return FALSE; +} + +s32 act_jump_land(struct MarioState *m) { + if (common_landing_cancels(m, &sJumpLandAction, set_jumping_action)) { + return TRUE; + } + + common_landing_action(m, MARIO_ANIM_LAND_FROM_SINGLE_JUMP, ACT_FREEFALL); + return FALSE; +} + +s32 act_freefall_land(struct MarioState *m) { + if (common_landing_cancels(m, &sFreefallLandAction, set_jumping_action)) { + return TRUE; + } + + common_landing_action(m, MARIO_ANIM_GENERAL_LAND, ACT_FREEFALL); + return FALSE; +} + +s32 act_side_flip_land(struct MarioState *m) { + if (common_landing_cancels(m, &sSideFlipLandAction, set_jumping_action)) { + return TRUE; + } + + if (common_landing_action(m, MARIO_ANIM_SLIDEFLIP_LAND, ACT_FREEFALL) != GROUND_STEP_HIT_WALL) { + m->marioObj->header.gfx.angle[1] += 0x8000; + } + return FALSE; +} + +s32 act_hold_jump_land(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_JUMP_LAND_STOP, 0); + } + + if (common_landing_cancels(m, &sHoldJumpLandAction, set_jumping_action)) { + return TRUE; + } + + common_landing_action(m, MARIO_ANIM_JUMP_LAND_WITH_LIGHT_OBJ, ACT_HOLD_FREEFALL); + return FALSE; +} + +s32 act_hold_freefall_land(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_FREEFALL_LAND_STOP, 0); + } + + if (common_landing_cancels(m, &sHoldFreefallLandAction, set_jumping_action)) { + return TRUE; + } + + common_landing_action(m, MARIO_ANIM_FALL_LAND_WITH_LIGHT_OBJ, ACT_HOLD_FREEFALL); + return FALSE; +} + +s32 act_long_jump_land(struct MarioState *m) { +#ifdef VERSION_SH + // BLJ (Backwards Long Jump) speed build up fix, crushing SimpleFlips's dreams since July 1997 + if (m->forwardVel < 0.0f) { + m->forwardVel = 0.0f; + } +#endif + + if (!(m->input & INPUT_Z_DOWN)) { + m->input &= ~INPUT_A_PRESSED; + } + + if (common_landing_cancels(m, &sLongJumpLandAction, set_jumping_action)) { + return TRUE; + } + + if (!(m->input & INPUT_NONZERO_ANALOG)) { + play_sound_if_no_flag(m, SOUND_MARIO_UH2_2, MARIO_MARIO_SOUND_PLAYED); + } + + common_landing_action(m, + !m->marioObj->oMarioLongJumpIsSlow ? MARIO_ANIM_CROUCH_FROM_FAST_LONGJUMP + : MARIO_ANIM_CROUCH_FROM_SLOW_LONGJUMP, + ACT_FREEFALL); + return FALSE; +} + +s32 act_double_jump_land(struct MarioState *m) { + if (common_landing_cancels(m, &sDoubleJumpLandAction, set_triple_jump_action)) { + return TRUE; + } + common_landing_action(m, MARIO_ANIM_LAND_FROM_DOUBLE_JUMP, ACT_FREEFALL); + return FALSE; +} + +s32 act_triple_jump_land(struct MarioState *m) { + m->input &= ~INPUT_A_PRESSED; + + if (common_landing_cancels(m, &sTripleJumpLandAction, set_jumping_action)) { + return TRUE; + } + + if (!(m->input & INPUT_NONZERO_ANALOG)) { + play_sound_if_no_flag(m, SOUND_MARIO_HAHA, MARIO_MARIO_SOUND_PLAYED); + } + + common_landing_action(m, MARIO_ANIM_TRIPLE_JUMP_LAND, ACT_FREEFALL); + return FALSE; +} + +s32 act_backflip_land(struct MarioState *m) { + if (!(m->input & INPUT_Z_DOWN)) { + m->input &= ~INPUT_A_PRESSED; + } + + if (common_landing_cancels(m, &sBackflipLandAction, set_jumping_action)) { + return TRUE; + } + + if (!(m->input & INPUT_NONZERO_ANALOG)) { + play_sound_if_no_flag(m, SOUND_MARIO_HAHA, MARIO_MARIO_SOUND_PLAYED); + } + + common_landing_action(m, MARIO_ANIM_TRIPLE_JUMP_LAND, ACT_FREEFALL); + return FALSE; +} + +s32 quicksand_jump_land_action(struct MarioState *m, s32 animation1, s32 animation2, u32 endAction, + u32 airAction) { + if (m->actionTimer++ < 6) { + m->quicksandDepth -= (7 - m->actionTimer) * 0.8f; + if (m->quicksandDepth < 1.0f) { + m->quicksandDepth = 1.1f; + } + + play_mario_jump_sound(m); + set_mario_animation(m, animation1); + } else { + if (m->actionTimer >= 13) { + return set_mario_action(m, endAction, 0); + } + + set_mario_animation(m, animation2); + } + + apply_landing_accel(m, 0.95f); + if (perform_ground_step(m) == GROUND_STEP_LEFT_GROUND) { + set_mario_action(m, airAction, 0); + } + + return FALSE; +} + +s32 act_quicksand_jump_land(struct MarioState *m) { + s32 cancel = quicksand_jump_land_action(m, MARIO_ANIM_SINGLE_JUMP, MARIO_ANIM_LAND_FROM_SINGLE_JUMP, + ACT_JUMP_LAND_STOP, ACT_FREEFALL); + return cancel; +} + +s32 act_hold_quicksand_jump_land(struct MarioState *m) { + s32 cancel = quicksand_jump_land_action(m, MARIO_ANIM_JUMP_WITH_LIGHT_OBJ, + MARIO_ANIM_JUMP_LAND_WITH_LIGHT_OBJ, ACT_HOLD_JUMP_LAND_STOP, + ACT_HOLD_FREEFALL); + return cancel; +} + +s32 check_common_moving_cancels(struct MarioState *m) { + if (m->pos[1] < m->waterLevel - 100) { + return set_water_plunge_action(m); + } + + if (!(m->action & ACT_FLAG_INVULNERABLE) && (m->input & INPUT_UNKNOWN_10)) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_SQUISHED) { + return drop_and_set_mario_action(m, ACT_SQUISHED, 0); + } + + if (!(m->action & ACT_FLAG_INVULNERABLE)) { + if (m->health < 0x100) { + return drop_and_set_mario_action(m, ACT_STANDING_DEATH, 0); + } + } + + return FALSE; +} + +s32 mario_execute_moving_action(struct MarioState *m) { + s32 cancel; + + if (check_common_moving_cancels(m)) { + return TRUE; + } + + if (mario_update_quicksand(m, 0.25f)) { + return TRUE; + } + + /* clang-format off */ + switch (m->action) { + case ACT_WALKING: cancel = act_walking(m); break; + case ACT_HOLD_WALKING: cancel = act_hold_walking(m); break; + case ACT_HOLD_HEAVY_WALKING: cancel = act_hold_heavy_walking(m); break; + case ACT_TURNING_AROUND: cancel = act_turning_around(m); break; + case ACT_FINISH_TURNING_AROUND: cancel = act_finish_turning_around(m); break; + case ACT_BRAKING: cancel = act_braking(m); break; + case ACT_RIDING_SHELL_GROUND: cancel = act_riding_shell_ground(m); break; + case ACT_CRAWLING: cancel = act_crawling(m); break; + case ACT_BURNING_GROUND: cancel = act_burning_ground(m); break; + case ACT_DECELERATING: cancel = act_decelerating(m); break; + case ACT_HOLD_DECELERATING: cancel = act_hold_decelerating(m); break; + case ACT_BUTT_SLIDE: cancel = act_butt_slide(m); break; + case ACT_STOMACH_SLIDE: cancel = act_stomach_slide(m); break; + case ACT_HOLD_BUTT_SLIDE: cancel = act_hold_butt_slide(m); break; + case ACT_HOLD_STOMACH_SLIDE: cancel = act_hold_stomach_slide(m); break; + case ACT_DIVE_SLIDE: cancel = act_dive_slide(m); break; + case ACT_MOVE_PUNCHING: cancel = act_move_punching(m); break; + case ACT_CROUCH_SLIDE: cancel = act_crouch_slide(m); break; + case ACT_SLIDE_KICK_SLIDE: cancel = act_slide_kick_slide(m); break; + case ACT_HARD_BACKWARD_GROUND_KB: cancel = act_hard_backward_ground_kb(m); break; + case ACT_HARD_FORWARD_GROUND_KB: cancel = act_hard_forward_ground_kb(m); break; + case ACT_BACKWARD_GROUND_KB: cancel = act_backward_ground_kb(m); break; + case ACT_FORWARD_GROUND_KB: cancel = act_forward_ground_kb(m); break; + case ACT_SOFT_BACKWARD_GROUND_KB: cancel = act_soft_backward_ground_kb(m); break; + case ACT_SOFT_FORWARD_GROUND_KB: cancel = act_soft_forward_ground_kb(m); break; + case ACT_GROUND_BONK: cancel = act_ground_bonk(m); break; + case ACT_DEATH_EXIT_LAND: cancel = act_death_exit_land(m); break; + case ACT_JUMP_LAND: cancel = act_jump_land(m); break; + case ACT_FREEFALL_LAND: cancel = act_freefall_land(m); break; + case ACT_DOUBLE_JUMP_LAND: cancel = act_double_jump_land(m); break; + case ACT_SIDE_FLIP_LAND: cancel = act_side_flip_land(m); break; + case ACT_HOLD_JUMP_LAND: cancel = act_hold_jump_land(m); break; + case ACT_HOLD_FREEFALL_LAND: cancel = act_hold_freefall_land(m); break; + case ACT_TRIPLE_JUMP_LAND: cancel = act_triple_jump_land(m); break; + case ACT_BACKFLIP_LAND: cancel = act_backflip_land(m); break; + case ACT_QUICKSAND_JUMP_LAND: cancel = act_quicksand_jump_land(m); break; + case ACT_HOLD_QUICKSAND_JUMP_LAND: cancel = act_hold_quicksand_jump_land(m); break; + case ACT_LONG_JUMP_LAND: cancel = act_long_jump_land(m); break; + } + /* clang-format on */ + + if (!cancel && (m->input & INPUT_IN_WATER)) { + m->particleFlags |= PARTICLE_WAVE_TRAIL; + m->particleFlags &= ~PARTICLE_DUST; + } + + return cancel; +} diff --git a/src/game/mario_actions_moving.h b/src/game/mario_actions_moving.h new file mode 100644 index 0000000..3cdbf0b --- /dev/null +++ b/src/game/mario_actions_moving.h @@ -0,0 +1,11 @@ +#ifndef MARIO_ACTIONS_MOVING +#define MARIO_ACTIONS_MOVING + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +void play_step_sound(struct MarioState *m, s16 frame1, s16 frame2); +s32 mario_execute_moving_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_MOVING diff --git a/src/game/mario_actions_object.c b/src/game/mario_actions_object.c new file mode 100644 index 0000000..638cb36 --- /dev/null +++ b/src/game/mario_actions_object.c @@ -0,0 +1,496 @@ +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "../include/sm64.h" +#include "mario_actions_object.h" +#include "../include/types.h" +#include "mario_step.h" +#include "mario.h" +//#include "audio/external.h" +#include "interaction.h" +//#include "audio_defines.h" +#include "../engine/math_util.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + +/** + * Used by act_punching() to determine Mario's forward velocity during each + * animation frame. + */ +s8 sPunchingForwardVelocities[8] = { 0, 1, 1, 2, 3, 5, 7, 10 }; + +void animated_stationary_ground_step(struct MarioState *m, s32 animation, u32 endAction) { + stationary_ground_step(m); + set_mario_animation(m, animation); + if (is_anim_at_end(m)) { + set_mario_action(m, endAction, 0); + } +} + +s32 mario_update_punch_sequence(struct MarioState *m) { + u32 endAction, crouchEndAction; + s32 animFrame; + + if (m->action & ACT_FLAG_MOVING) { + endAction = ACT_WALKING, crouchEndAction = ACT_CROUCH_SLIDE; + } else { + endAction = ACT_IDLE, crouchEndAction = ACT_CROUCHING; + } + + switch (m->actionArg) { + case 0: + play_sound(SOUND_MARIO_PUNCH_YAH, m->marioObj->header.gfx.cameraToObject); + // Fall-through: + case 1: + set_mario_animation(m, MARIO_ANIM_FIRST_PUNCH); + if (is_anim_past_end(m)) { + m->actionArg = 2; + } else { + m->actionArg = 1; + } + + if (m->marioObj->header.gfx.animInfo.animFrame >= 2) { + if (mario_check_object_grab(m)) { + return TRUE; + } + + m->flags |= MARIO_PUNCHING; + } + + if (m->actionArg == 2) { + m->marioBodyState->punchState = (0 << 6) | 4; + } + break; + + case 2: + set_mario_animation(m, MARIO_ANIM_FIRST_PUNCH_FAST); + + if (m->marioObj->header.gfx.animInfo.animFrame <= 0) { + m->flags |= MARIO_PUNCHING; + } + + if (m->input & INPUT_B_PRESSED) { + m->actionArg = 3; + } + + if (is_anim_at_end(m)) { + set_mario_action(m, endAction, 0); + } + break; + + case 3: + play_sound(SOUND_MARIO_PUNCH_WAH, m->marioObj->header.gfx.cameraToObject); + // Fall-through: + case 4: + set_mario_animation(m, MARIO_ANIM_SECOND_PUNCH); + if (is_anim_past_end(m)) { + m->actionArg = 5; + } else { + m->actionArg = 4; + } + + if (m->marioObj->header.gfx.animInfo.animFrame > 0) { + m->flags |= MARIO_PUNCHING; + } + + if (m->actionArg == 5) { + m->marioBodyState->punchState = (1 << 6) | 4; + } + break; + + case 5: + set_mario_animation(m, MARIO_ANIM_SECOND_PUNCH_FAST); + if (m->marioObj->header.gfx.animInfo.animFrame <= 0) { + m->flags |= MARIO_PUNCHING; + } + + if (m->input & INPUT_B_PRESSED) { + m->actionArg = 6; + } + + if (is_anim_at_end(m)) { + set_mario_action(m, endAction, 0); + } + break; + + case 6: + play_mario_action_sound(m, SOUND_MARIO_PUNCH_HOO, 1); + animFrame = set_mario_animation(m, MARIO_ANIM_GROUND_KICK); + if (animFrame == 0) { + m->marioBodyState->punchState = (2 << 6) | 6; + } + + if (animFrame >= 0 && animFrame < 8) { + m->flags |= MARIO_KICKING; + } + + if (is_anim_at_end(m)) { + set_mario_action(m, endAction, 0); + } + break; + + case 9: + play_mario_action_sound(m, SOUND_MARIO_PUNCH_HOO, 1); + set_mario_animation(m, MARIO_ANIM_BREAKDANCE); + animFrame = m->marioObj->header.gfx.animInfo.animFrame; + + if (animFrame >= 2 && animFrame < 8) { + m->flags |= MARIO_TRIPPING; + } + + if (is_anim_at_end(m)) { + set_mario_action(m, crouchEndAction, 0); + } + break; + } + + return FALSE; +} + +s32 act_punching(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_action_exits(m); + } + + if (m->actionState == 0 && (m->input & INPUT_A_DOWN)) { + return set_mario_action(m, ACT_JUMP_KICK, 0); + } + + m->actionState = 1; + if (m->actionArg == 0) { + m->actionTimer = 7; + } + + mario_set_forward_vel(m, sPunchingForwardVelocities[m->actionTimer]); + if (m->actionTimer > 0) { + m->actionTimer--; + } + + mario_update_punch_sequence(m); + perform_ground_step(m); + return FALSE; +} + +s32 act_picking_up(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->actionState == 0 && is_anim_at_end(m)) { + //! While the animation is playing, it is possible for the used object + // to unload. This allows you to pick up a vacant or newly loaded object + // slot (cloning via fake object). + mario_grab_used_object(m); + play_sound_if_no_flag(m, SOUND_MARIO_HRMM, MARIO_MARIO_SOUND_PLAYED); + m->actionState = 1; + } + + if (m->actionState == 1) { + if (m->heldObj->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO) { + m->marioBodyState->grabPos = GRAB_POS_HEAVY_OBJ; + set_mario_animation(m, MARIO_ANIM_GRAB_HEAVY_OBJECT); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_HOLD_HEAVY_IDLE, 0); + } + } else { + m->marioBodyState->grabPos = GRAB_POS_LIGHT_OBJ; + set_mario_animation(m, MARIO_ANIM_PICK_UP_LIGHT_OBJ); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_HOLD_IDLE, 0); + } + } + } + + stationary_ground_step(m); + return FALSE; +} + +s32 act_dive_picking_up(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + //! Hands-free holding. Landing on a slope or being pushed off a ledge while + // landing from a dive grab sets Mario's action to a non-holding action + // without dropping the object, causing the hands-free holding glitch. + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + animated_stationary_ground_step(m, MARIO_ANIM_STOP_SLIDE_LIGHT_OBJ, ACT_HOLD_IDLE); + return FALSE; +} + +s32 act_placing_down(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if (++m->actionTimer == 8) { + mario_drop_held_object(m); + } + + animated_stationary_ground_step(m, MARIO_ANIM_PLACE_LIGHT_OBJ, ACT_IDLE); + return FALSE; +} + +s32 act_throwing(struct MarioState *m) { + if (m->heldObj && (m->heldObj->oInteractionSubtype & INT_SUBTYPE_HOLDABLE_NPC)) { + return set_mario_action(m, ACT_PLACING_DOWN, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if (++m->actionTimer == 7) { + mario_throw_held_object(m); + play_sound_if_no_flag(m, SOUND_MARIO_WAH2, MARIO_MARIO_SOUND_PLAYED); + play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED); +#ifdef VERSION_SH + queue_rumble_data(3, 50); +#endif + } + + animated_stationary_ground_step(m, MARIO_ANIM_GROUND_THROW, ACT_IDLE); + return FALSE; +} + +s32 act_heavy_throw(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if (++m->actionTimer == 13) { + mario_drop_held_object(m); + play_sound_if_no_flag(m, SOUND_MARIO_WAH2, MARIO_MARIO_SOUND_PLAYED); + play_sound_if_no_flag(m, SOUND_ACTION_THROW, MARIO_ACTION_SOUND_PLAYED); +#ifdef VERSION_SH + queue_rumble_data(3, 50); +#endif + } + + animated_stationary_ground_step(m, MARIO_ANIM_HEAVY_THROW, ACT_IDLE); + return FALSE; +} + +s32 act_stomach_slide_stop(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + animated_stationary_ground_step(m, MARIO_ANIM_SLOW_LAND_FROM_DIVE, ACT_IDLE); + return FALSE; +} + +s32 act_picking_up_bowser(struct MarioState *m) { + if (m->actionState == 0) { + m->actionState = 1; + m->angleVel[1] = 0; + m->marioBodyState->grabPos = GRAB_POS_BOWSER; + mario_grab_used_object(m); +#ifdef VERSION_SH + queue_rumble_data(5, 80); +#endif + play_sound(SOUND_MARIO_HRMM, m->marioObj->header.gfx.cameraToObject); + } + + set_mario_animation(m, MARIO_ANIM_GRAB_BOWSER); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_HOLDING_BOWSER, 0); + } + + stationary_ground_step(m); + return FALSE; +} + +s32 act_holding_bowser(struct MarioState *m) { + s16 spin; + + if (m->input & INPUT_B_PRESSED) { +#ifndef VERSION_JP + if (m->angleVel[1] <= -0xE00 || m->angleVel[1] >= 0xE00) { + play_sound(SOUND_MARIO_SO_LONGA_BOWSER, m->marioObj->header.gfx.cameraToObject); + } else { + play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); + } +#else + play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject); +#endif + return set_mario_action(m, ACT_RELEASING_BOWSER, 0); + } + + if (m->angleVel[1] == 0) { + if (m->actionTimer++ > 120) { + return set_mario_action(m, ACT_RELEASING_BOWSER, 1); + } + + set_mario_animation(m, MARIO_ANIM_HOLDING_BOWSER); + } else { + m->actionTimer = 0; + set_mario_animation(m, MARIO_ANIM_SWINGING_BOWSER); + } + + if (m->intendedMag > 20.0f) { + if (m->actionArg == 0) { + m->actionArg = 1; + m->twirlYaw = m->intendedYaw; + } else { + // spin = acceleration + spin = (s16)(m->intendedYaw - m->twirlYaw) / 0x80; + + if (spin < -0x80) { + spin = -0x80; + } + if (spin > 0x80) { + spin = 0x80; + } + + m->twirlYaw = m->intendedYaw; + m->angleVel[1] += spin; + + if (m->angleVel[1] > 0x1000) { + m->angleVel[1] = 0x1000; + } + if (m->angleVel[1] < -0x1000) { + m->angleVel[1] = -0x1000; + } + } + } else { + m->actionArg = 0; + m->angleVel[1] = approach_s32(m->angleVel[1], 0, 64, 64); + } + + // spin = starting yaw + spin = m->faceAngle[1]; + m->faceAngle[1] += m->angleVel[1]; + + // play sound on overflow + if (m->angleVel[1] <= -0x100 && spin < m->faceAngle[1]) { +#ifdef VERSION_SH + queue_rumble_data(4, 20); +#endif + play_sound(SOUND_OBJ_BOWSER_SPINNING, m->marioObj->header.gfx.cameraToObject); + } + if (m->angleVel[1] >= 0x100 && spin > m->faceAngle[1]) { +#ifdef VERSION_SH + queue_rumble_data(4, 20); +#endif + play_sound(SOUND_OBJ_BOWSER_SPINNING, m->marioObj->header.gfx.cameraToObject); + } + + stationary_ground_step(m); + if (m->angleVel[1] >= 0) { + m->marioObj->header.gfx.angle[0] = -m->angleVel[1]; + } else { + m->marioObj->header.gfx.angle[0] = m->angleVel[1]; + } + + return FALSE; +} + +s32 act_releasing_bowser(struct MarioState *m) { + if (++m->actionTimer == 1) { + if (m->actionArg == 0) { +#ifdef VERSION_SH + queue_rumble_data(4, 50); +#endif + mario_throw_held_object(m); + } else { +#ifdef VERSION_SH + queue_rumble_data(4, 50); +#endif + mario_drop_held_object(m); + } + } + + m->angleVel[1] = 0; + animated_stationary_ground_step(m, MARIO_ANIM_RELEASE_BOWSER, ACT_IDLE); + return FALSE; +} + +s32 check_common_object_cancels(struct MarioState *m) { + f32 waterSurface = m->waterLevel - 100; + if (m->pos[1] < waterSurface) { + return set_water_plunge_action(m); + } + + if (m->input & INPUT_SQUISHED) { + return drop_and_set_mario_action(m, ACT_SQUISHED, 0); + } + + if (m->health < 0x100) { + return drop_and_set_mario_action(m, ACT_STANDING_DEATH, 0); + } + + return FALSE; +} + +s32 mario_execute_object_action(struct MarioState *m) { + s32 cancel; + + if (check_common_object_cancels(m)) { + return TRUE; + } + + if (mario_update_quicksand(m, 0.5f)) { + return TRUE; + } + + /* clang-format off */ + switch (m->action) { + case ACT_PUNCHING: cancel = act_punching(m); break; + case ACT_PICKING_UP: cancel = act_picking_up(m); break; + case ACT_DIVE_PICKING_UP: cancel = act_dive_picking_up(m); break; + case ACT_STOMACH_SLIDE_STOP: cancel = act_stomach_slide_stop(m); break; + case ACT_PLACING_DOWN: cancel = act_placing_down(m); break; + case ACT_THROWING: cancel = act_throwing(m); break; + case ACT_HEAVY_THROW: cancel = act_heavy_throw(m); break; + case ACT_PICKING_UP_BOWSER: cancel = act_picking_up_bowser(m); break; + case ACT_HOLDING_BOWSER: cancel = act_holding_bowser(m); break; + case ACT_RELEASING_BOWSER: cancel = act_releasing_bowser(m); break; + } + /* clang-format on */ + + if (!cancel && (m->input & INPUT_IN_WATER)) { + m->particleFlags |= PARTICLE_IDLE_WATER_WAVE; + } + + return cancel; +} diff --git a/src/game/mario_actions_object.h b/src/game/mario_actions_object.h new file mode 100644 index 0000000..b626b73 --- /dev/null +++ b/src/game/mario_actions_object.h @@ -0,0 +1,11 @@ +#ifndef MARIO_ACTIONS_OBJECT_H +#define MARIO_ACTIONS_OBJECT_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +s32 mario_update_punch_sequence(struct MarioState *m); +s32 mario_execute_object_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_OBJECT_H diff --git a/src/game/mario_actions_stationary.c b/src/game/mario_actions_stationary.c new file mode 100644 index 0000000..ee59ae5 --- /dev/null +++ b/src/game/mario_actions_stationary.c @@ -0,0 +1,1170 @@ +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "../include/sm64.h" +#include "area.h" +//#include "audio/external.h" +//#include "behavior_data.h" +#include "camera.h" +#include "../engine/math_util.h" +#include "interaction.h" +#include "level_update.h" +#include "mario.h" +#include "mario_actions_stationary.h" +#include "mario_step.h" +#include "memory.h" +#include "save_file.h" +//#include "sound_init.h" +#include "../include/surface_terrains.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + + +s32 check_common_idle_cancels(struct MarioState *m) { + mario_drop_held_object(m); + if (m->floor->normal.y < 0.29237169f) { + return mario_push_off_steep_floor(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_JUMP, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_FIRST_PERSON, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + m->faceAngle[1] = (s16) m->intendedYaw; + return set_mario_action(m, ACT_WALKING, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_PUNCHING, 0); + } + + if (m->input & INPUT_Z_DOWN) { + return set_mario_action(m, ACT_START_CROUCHING, 0); + } + + return FALSE; +} + +s32 check_common_hold_idle_cancels(struct MarioState *m) { + if (m->floor->normal.y < 0.29237169f) { + return mario_push_off_steep_floor(m, ACT_HOLD_FREEFALL, 0); + } + + if (m->heldObj->oInteractionSubtype & INT_SUBTYPE_DROP_IMMEDIATELY) { + m->heldObj->oInteractionSubtype = + (s32)(m->heldObj->oInteractionSubtype & ~INT_SUBTYPE_DROP_IMMEDIATELY); + return set_mario_action(m, ACT_PLACING_DOWN, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_HOLD_JUMP, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_HOLD_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_HOLD_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + m->faceAngle[1] = (s16) m->intendedYaw; + return set_mario_action(m, ACT_HOLD_WALKING, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_THROWING, 0); + } + + if (m->input & INPUT_Z_DOWN) { + return drop_and_set_mario_action(m, ACT_START_CROUCHING, 0); + } + + return FALSE; +} + +s32 act_idle(struct MarioState *m) { + if (m->quicksandDepth > 30.0f) { + return set_mario_action(m, ACT_IN_QUICKSAND, 0); + } + + if (m->input & INPUT_IN_POISON_GAS) { + return set_mario_action(m, ACT_COUGHING, 0); + } + + if (!(m->actionArg & 1) && m->health < 0x300) { + return set_mario_action(m, ACT_PANTING, 0); + } + + if (check_common_idle_cancels(m)) { + return TRUE; + } + + if (m->actionState == 3) { + if ((m->area->terrainType & TERRAIN_MASK) == TERRAIN_SNOW) { + return set_mario_action(m, ACT_SHIVERING, 0); + } else { + return set_mario_action(m, ACT_START_SLEEPING, 0); + } + } + + if (m->actionArg & 1) { + set_mario_animation(m, MARIO_ANIM_STAND_AGAINST_WALL); + } else { + switch (m->actionState) { + case 0: + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_LEFT); + break; + + case 1: + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_RIGHT); + break; + + case 2: + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_CENTER); + break; + } + + if (is_anim_at_end(m)) { + // Fall asleep after 10 head turning cycles. + // act_start_sleeping is triggered earlier in the function + // when actionState == 3. This happens when Mario's done + // turning his head back and forth. However, we do some checks + // here to make sure that Mario would be able to sleep here, + // and that he's gone through 10 cycles before sleeping. + // actionTimer is used to track how many cycles have passed. + if (++m->actionState == 3) { + f32 deltaYOfFloorBehindMario = m->pos[1] - find_floor_height_relative_polar(m, -0x8000, 60.0f); + if (deltaYOfFloorBehindMario < -24.0f || 24.0f < deltaYOfFloorBehindMario || m->floor->flags & SURFACE_FLAG_DYNAMIC) { + m->actionState = 0; + } else { + // If Mario hasn't turned his head 10 times yet, stay idle instead of going to sleep. + m->actionTimer++; + if (m->actionTimer < 10) { + m->actionState = 0; + } + } + } + } + } + + stationary_ground_step(m); + + return FALSE; +} + +void play_anim_sound(struct MarioState *m, u32 actionState, s32 animFrame, u32 sound) { + if (m->actionState == actionState && m->marioObj->header.gfx.animInfo.animFrame == animFrame) { + play_sound(sound, m->marioObj->header.gfx.cameraToObject); + } +} + +s32 act_start_sleeping(struct MarioState *m) { +#ifndef VERSION_JP + s32 animFrame; +#endif + + if (check_common_idle_cancels(m)) { + return TRUE; + } + + if (m->quicksandDepth > 30.0f) { + return set_mario_action(m, ACT_IN_QUICKSAND, 0); + } + + if (m->actionState == 4) { + return set_mario_action(m, ACT_SLEEPING, 0); + } + + switch (m->actionState) { + case 0: +#ifndef VERSION_JP + animFrame = +#endif + set_mario_animation(m, MARIO_ANIM_START_SLEEP_IDLE); + break; + + case 1: +#ifndef VERSION_JP + animFrame = +#endif + set_mario_animation(m, MARIO_ANIM_START_SLEEP_SCRATCH); + break; + + case 2: +#ifndef VERSION_JP + animFrame = +#endif + set_mario_animation(m, MARIO_ANIM_START_SLEEP_YAWN); + m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; + break; + + case 3: +#ifndef VERSION_JP + animFrame = +#endif + set_mario_animation(m, MARIO_ANIM_START_SLEEP_SITTING); + m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; + break; + } + + play_anim_sound(m, 1, 41, SOUND_ACTION_PAT_BACK); + play_anim_sound(m, 1, 49, SOUND_ACTION_PAT_BACK); + play_anim_sound(m, 3, 15, m->terrainSoundAddend + SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + + if (is_anim_at_end(m)) { + m->actionState++; + } + +#ifndef VERSION_JP + if (m->actionState == 2 && animFrame == -1) { + play_sound(SOUND_MARIO_YAWNING, m->marioObj->header.gfx.cameraToObject); + } + + if (m->actionState == 1 && animFrame == -1) { + play_sound(SOUND_MARIO_IMA_TIRED, m->marioObj->header.gfx.cameraToObject); + } +#else + if (m->actionState == 2) { + play_sound_if_no_flag(m, SOUND_MARIO_YAWNING, MARIO_MARIO_SOUND_PLAYED); + } +#endif + + stationary_ground_step(m); + return FALSE; +} + +s32 act_sleeping(struct MarioState *m) { + s32 animFrame; + if (m->input + & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE + | INPUT_FIRST_PERSON | INPUT_UNKNOWN_10 | INPUT_B_PRESSED | INPUT_Z_PRESSED)) { + return set_mario_action(m, ACT_WAKING_UP, m->actionState); + } + + if (m->quicksandDepth > 30.0f) { + return set_mario_action(m, ACT_WAKING_UP, m->actionState); + } + + if (m->pos[1] - find_floor_height_relative_polar(m, -0x8000, 60.0f) > 24.0f) { + return set_mario_action(m, ACT_WAKING_UP, m->actionState); + } + + m->marioBodyState->eyeState = MARIO_EYES_CLOSED; + stationary_ground_step(m); + switch (m->actionState) { + case 0: + animFrame = set_mario_animation(m, MARIO_ANIM_SLEEP_IDLE); + + if (animFrame == -1 && !m->actionTimer) { + lower_background_noise(2); + } + + if (animFrame == 2) { + play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); + } + + if (animFrame == 20) { + play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); + } + + if (is_anim_at_end(m)) { + m->actionTimer++; + if (m->actionTimer > 45) { + m->actionState++; + } + } + break; + + case 1: + if (set_mario_animation(m, MARIO_ANIM_SLEEP_START_LYING) == 18) { + play_mario_heavy_landing_sound(m, SOUND_ACTION_TERRAIN_BODY_HIT_GROUND); + } + + if (is_anim_at_end(m)) { + m->actionState++; + } + break; + + case 2: + animFrame = set_mario_animation(m, MARIO_ANIM_SLEEP_LYING); +#ifndef VERSION_JP + play_sound_if_no_flag(m, SOUND_MARIO_SNORING3, MARIO_ACTION_SOUND_PLAYED); +#else + if (animFrame == 2) { + play_sound(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); + } + + if (animFrame == 25) { + play_sound(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); + } +#endif + break; + } + return FALSE; +} + +s32 act_waking_up(struct MarioState *m) { +// if (!m->actionTimer) { +// func_803205E8(SOUND_MARIO_SNORING1, m->marioObj->header.gfx.cameraToObject); +// func_803205E8(SOUND_MARIO_SNORING2, m->marioObj->header.gfx.cameraToObject); +//#ifndef VERSION_JP +// func_803205E8(SOUND_MARIO_SNORING3, m->marioObj->header.gfx.cameraToObject); +//#endif +// raise_background_noise(2); +// } + + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + m->actionTimer++; + + if (m->actionTimer > 20) { + return set_mario_action(m, ACT_IDLE, 0); + } + + stationary_ground_step(m); + + set_mario_animation(m, !m->actionArg ? MARIO_ANIM_WAKE_FROM_SLEEP : MARIO_ANIM_WAKE_FROM_LYING); + + return FALSE; +} + +s32 act_shivering(struct MarioState *m) { + s32 animFrame; + + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input + & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE + | INPUT_FIRST_PERSON | INPUT_UNKNOWN_10 | INPUT_B_PRESSED | INPUT_Z_PRESSED)) { + m->actionState = 2; + } + + stationary_ground_step(m); + switch (m->actionState) { + case 0: + animFrame = set_mario_animation(m, MARIO_ANIM_SHIVERING_WARMING_HAND); + if (animFrame == 49) { + m->particleFlags |= PARTICLE_BREATH; + play_sound(SOUND_MARIO_PANTING_COLD, m->marioObj->header.gfx.cameraToObject); + } + if (animFrame == 7 || animFrame == 81) { + play_sound(SOUND_ACTION_CLAP_HANDS_COLD, m->marioObj->header.gfx.cameraToObject); + } + if (is_anim_past_end(m)) { + m->actionState = 1; + } + break; + + case 1: + animFrame = set_mario_animation(m, MARIO_ANIM_SHIVERING); + if (animFrame == 9 || animFrame == 25 || animFrame == 44) { + play_sound(SOUND_ACTION_CLAP_HANDS_COLD, m->marioObj->header.gfx.cameraToObject); + } + break; + + case 2: + set_mario_animation(m, MARIO_ANIM_SHIVERING_RETURN_TO_IDLE); + if (is_anim_past_end(m)) { + set_mario_action(m, ACT_IDLE, 0); + } + break; + } + return FALSE; +} + +s32 act_coughing(struct MarioState *m) { + s32 animFrame; + + if (check_common_idle_cancels(m)) { + return TRUE; + } + + stationary_ground_step(m); + animFrame = set_mario_animation(m, MARIO_ANIM_COUGHING); + if (animFrame == 25 || animFrame == 35) { + play_sound(SOUND_MARIO_COUGHING3, m->marioObj->header.gfx.cameraToObject); + } + + if (animFrame == 50 || animFrame == 58) { + play_sound(SOUND_MARIO_COUGHING2, m->marioObj->header.gfx.cameraToObject); + } + + if (animFrame == 71 || animFrame == 80) { + play_sound(SOUND_MARIO_COUGHING1, m->marioObj->header.gfx.cameraToObject); + } + + return FALSE; +} + +s32 act_hold_idle(struct MarioState *m) { +// if (segmented_to_virtual(&bhvJumpingBox) == m->heldObj->behavior) { +// return set_mario_action(m, ACT_CRAZY_BOX_BOUNCE, 0); +// } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_IDLE, 0); + } + + if (m->quicksandDepth > 30.0f) { + return drop_and_set_mario_action(m, ACT_IN_QUICKSAND, 0); + } + + if (check_common_hold_idle_cancels(m)) { + return TRUE; + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_IDLE_WITH_LIGHT_OBJ); + return FALSE; +} + +s32 act_hold_heavy_idle(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return drop_and_set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HOLD_HEAVY_WALKING, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_HEAVY_THROW, 0); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_IDLE_HEAVY_OBJ); + return FALSE; +} + +s32 act_standing_against_wall(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_action_exits(m); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_FIRST_PERSON, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_PUNCHING, 0); + } + + set_mario_animation(m, MARIO_ANIM_STAND_AGAINST_WALL); + stationary_ground_step(m); + return FALSE; +} + +s32 act_in_quicksand(struct MarioState *m) { + if (m->quicksandDepth < 30.0f) { + return set_mario_action(m, ACT_IDLE, 0); + } + + if (check_common_idle_cancels(m)) { + return TRUE; + } + + if (m->quicksandDepth > 70.0f) { + set_mario_animation(m, MARIO_ANIM_DYING_IN_QUICKSAND); + } else { + set_mario_animation(m, MARIO_ANIM_IDLE_IN_QUICKSAND); + } + + stationary_ground_step(m); + return FALSE; +} + +s32 act_crouching(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_BACKFLIP, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_STOP_CROUCHING, 0); + } + + if (!(m->input & INPUT_Z_DOWN)) { + return set_mario_action(m, ACT_STOP_CROUCHING, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_START_CRAWLING, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_PUNCHING, 9); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_CROUCHING); + return FALSE; +} + +s32 act_panting(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->health >= 0x500) { + return set_mario_action(m, ACT_IDLE, 0); + } + + if (check_common_idle_cancels(m)) { + return TRUE; + } + + if (set_mario_animation(m, MARIO_ANIM_WALK_PANTING) == 1) { + play_sound(SOUND_MARIO_PANTING + ((gAudioRandom % 3U) << 0x10), + m->marioObj->header.gfx.cameraToObject); + } + + stationary_ground_step(m); + m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; + return FALSE; +} + +s32 act_hold_panting_unused(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_PANTING, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->health >= 0x500) { + return set_mario_action(m, ACT_HOLD_IDLE, 0); + } + + if (check_common_hold_idle_cancels(m)) { + return TRUE; + } + + set_mario_animation(m, MARIO_ANIM_WALK_PANTING); + stationary_ground_step(m); + m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; + return FALSE; +} + +void stopping_step(struct MarioState *m, s32 animID, u32 action) { + stationary_ground_step(m); + set_mario_animation(m, animID); + if (is_anim_at_end(m)) { + set_mario_action(m, action, 0); + } +} + +s32 act_braking_stop(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_PUNCHING, 0); + } + + if (!(m->input & INPUT_FIRST_PERSON) + && m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_action_exits(m); + } + + stopping_step(m, MARIO_ANIM_STOP_SKID, ACT_IDLE); + return FALSE; +} + +s32 act_butt_slide_stop(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_action_exits(m); + } + + stopping_step(m, MARIO_ANIM_STOP_SLIDE, ACT_IDLE); + if (m->marioObj->header.gfx.animInfo.animFrame == 6) { + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + + return FALSE; +} + +s32 act_hold_butt_slide_stop(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_IDLE, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_hold_action_exits(m); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_THROWING, 0); + } + + stopping_step(m, MARIO_ANIM_STAND_UP_FROM_SLIDING_WITH_LIGHT_OBJ, ACT_HOLD_IDLE); + return FALSE; +} + +s32 act_slide_kick_slide_stop(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return drop_and_set_mario_action(m, ACT_FREEFALL, 0); + } + + stopping_step(m, MARIO_ANIM_CROUCH_FROM_SLIDE_KICK, ACT_CROUCHING); + return FALSE; +} + +s32 act_start_crouching(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_BACKFLIP, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_START_CROUCHING); + if (is_anim_past_end(m)) { + set_mario_action(m, ACT_CROUCHING, 0); + } + return FALSE; +} + +s32 act_stop_crouching(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_jumping_action(m, ACT_BACKFLIP, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_STOP_CROUCHING); + if (is_anim_past_end(m)) { + set_mario_action(m, ACT_IDLE, 0); + } + return FALSE; +} + +s32 act_start_crawling(struct MarioState *m) { + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_STOP_CROUCHING, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_START_CRAWLING); + if (is_anim_past_end(m)) { + set_mario_action(m, ACT_CRAWLING, 0); + } + + return FALSE; +} + +s32 act_stop_crawling(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BEGIN_SLIDING, 0); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_STOP_CRAWLING); + if (is_anim_past_end(m)) { + set_mario_action(m, ACT_CROUCHING, 0); + } + return FALSE; +} + +s32 act_shockwave_bounce(struct MarioState *m) { + s16 sp1E; + f32 sp18; + + if (m->marioObj->oInteractStatus & INT_STATUS_HIT_BY_SHOCKWAVE) { +#ifdef VERSION_SH + queue_rumble_data(70, 40); +#endif + return hurt_and_set_mario_action(m, ACT_SHOCKED, 0, 4); + } + + if (m->actionTimer == 0) { +#ifdef VERSION_SH + queue_rumble_data(70, 40); +#endif + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_UNK1) { + return hurt_and_set_mario_action(m, ACT_BACKWARD_GROUND_KB, 0, 0xc); + } + } + + if (++m->actionTimer == 48) { + return set_mario_action(m, ACT_IDLE, 0); + } + + sp1E = (m->actionTimer % 16) << 12; + sp18 = (f32)(((f32)(6 - m->actionTimer / 8) * 8.0f) + 4.0f); + mario_set_forward_vel(m, 0); + vec3f_set(m->vel, 0.0f, 0.0f, 0.0f); + if (sins(sp1E) >= 0.0f) { + m->pos[1] = sins(sp1E) * sp18 + m->floorHeight; + } else { + m->pos[1] = m->floorHeight - sins(sp1E) * sp18; + } + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + set_mario_animation(m, MARIO_ANIM_A_POSE); + return FALSE; +} + +s32 landing_step(struct MarioState *m, s32 arg1, u32 action) { + stationary_ground_step(m); + set_mario_animation(m, arg1); + if (is_anim_at_end(m)) { + return set_mario_action(m, action, 0); + } + return FALSE; +} + +s32 check_common_landing_cancels(struct MarioState *m, u32 action) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_IDLE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + if (!action) { + return set_jump_from_landing(m); + } else { + return set_jumping_action(m, action, 0); + } + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_action_exits(m); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_PUNCHING, 0); + } + + return FALSE; +} + +s32 act_jump_land_stop(struct MarioState *m) { + if (check_common_landing_cancels(m, 0)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_LAND_FROM_SINGLE_JUMP, ACT_IDLE); + return FALSE; +} + +s32 act_double_jump_land_stop(struct MarioState *m) { + if (check_common_landing_cancels(m, 0)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_LAND_FROM_DOUBLE_JUMP, ACT_IDLE); + return FALSE; +} + +s32 act_side_flip_land_stop(struct MarioState *m) { + if (check_common_landing_cancels(m, 0)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_SLIDEFLIP_LAND, ACT_IDLE); + m->marioObj->header.gfx.angle[1] += 0x8000; + return FALSE; +} + +s32 act_freefall_land_stop(struct MarioState *m) { + if (check_common_landing_cancels(m, 0)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_GENERAL_LAND, ACT_IDLE); + return FALSE; +} + +s32 act_triple_jump_land_stop(struct MarioState *m) { + if (check_common_landing_cancels(m, ACT_JUMP)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_TRIPLE_JUMP_LAND, ACT_IDLE); + return FALSE; +} + +s32 act_backflip_land_stop(struct MarioState *m) { + if (!(m->input & INPUT_Z_DOWN) || m->marioObj->header.gfx.animInfo.animFrame >= 6) { + m->input &= ~INPUT_A_PRESSED; + } + + if (check_common_landing_cancels(m, ACT_BACKFLIP)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_TRIPLE_JUMP_LAND, ACT_IDLE); + return FALSE; +} + +s32 act_lava_boost_land(struct MarioState *m) { + m->input &= ~(INPUT_FIRST_PERSON | INPUT_B_PRESSED); + + if (check_common_landing_cancels(m, 0)) { + return TRUE; + } + + landing_step(m, MARIO_ANIM_STAND_UP_FROM_LAVA_BOOST, ACT_IDLE); + return FALSE; +} + +s32 act_long_jump_land_stop(struct MarioState *m) { + m->input &= ~INPUT_B_PRESSED; + if (check_common_landing_cancels(m, ACT_JUMP)) { + return TRUE; + } + + landing_step(m, !m->marioObj->oMarioLongJumpIsSlow ? MARIO_ANIM_CROUCH_FROM_FAST_LONGJUMP + : MARIO_ANIM_CROUCH_FROM_SLOW_LONGJUMP, + ACT_CROUCHING); + return FALSE; +} + +s32 act_hold_jump_land_stop(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_IDLE, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_hold_action_exits(m); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_THROWING, 0); + } + + landing_step(m, MARIO_ANIM_JUMP_LAND_WITH_LIGHT_OBJ, ACT_HOLD_IDLE); + return FALSE; +} + +s32 act_hold_freefall_land_stop(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_IDLE, 0); + } + + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & (INPUT_NONZERO_ANALOG | INPUT_A_PRESSED | INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE)) { + return check_common_hold_action_exits(m); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_THROWING, 0); + } + landing_step(m, MARIO_ANIM_FALL_LAND_WITH_LIGHT_OBJ, ACT_HOLD_IDLE); + return FALSE; +} + +s32 act_air_throw_land(struct MarioState *m) { + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (++m->actionTimer == 4) { + mario_throw_held_object(m); + } + + landing_step(m, MARIO_ANIM_THROW_LIGHT_OBJECT, ACT_IDLE); + return FALSE; +} + +s32 act_twirl_land(struct MarioState *m) { + m->actionState = 1; + if (m->input & INPUT_UNKNOWN_10) { + return set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_TWIRL_LAND); + if (m->angleVel[1] > 0) { + m->angleVel[1] -= 0x400; + if (m->angleVel[1] < 0) { + m->angleVel[1] = 0; + } + + m->twirlYaw += m->angleVel[1]; + } + + m->marioObj->header.gfx.angle[1] += m->twirlYaw; + if (is_anim_at_end(m) && m->angleVel[1] == 0) { + m->faceAngle[1] += m->twirlYaw; + set_mario_action(m, ACT_IDLE, 0); + } + + return FALSE; +} + +s32 act_ground_pound_land(struct MarioState *m) { + m->actionState = 1; + if (m->input & INPUT_UNKNOWN_10) { + return drop_and_set_mario_action(m, ACT_SHOCKWAVE_BOUNCE, 0); + } + + if (m->input & INPUT_OFF_FLOOR) { + return set_mario_action(m, ACT_FREEFALL, 0); + } + + if (m->input & INPUT_ABOVE_SLIDE) { + return set_mario_action(m, ACT_BUTT_SLIDE, 0); + } + + landing_step(m, MARIO_ANIM_GROUND_POUND_LANDING, ACT_BUTT_SLIDE_STOP); + return FALSE; +} + +s32 act_first_person(struct MarioState *m) { + s32 sp1C = (m->input & (INPUT_OFF_FLOOR | INPUT_ABOVE_SLIDE | INPUT_UNKNOWN_10)) != 0; + + if (m->actionState == 0) { + lower_background_noise(2); + set_camera_mode(m->area->camera, CAMERA_MODE_C_UP, 0x10); + m->actionState = 1; + } else if (!(m->input & INPUT_FIRST_PERSON) || sp1C) { + raise_background_noise(2); + // Go back to the last camera mode + set_camera_mode(m->area->camera, -1, 1); + return set_mario_action(m, ACT_IDLE, 0); + } + + if (m->floor->type == SURFACE_LOOK_UP_WARP + && save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1) >= 10) { + s16 sp1A = m->statusForCamera->headRotation[0]; + s16 sp18 = ((m->statusForCamera->headRotation[1] * 4) / 3) + m->faceAngle[1]; + if (sp1A == -0x1800 && (sp18 < -0x6FFF || sp18 >= 0x7000)) { + level_trigger_warp(m, WARP_OP_UNKNOWN_01); + } + } + + stationary_ground_step(m); + set_mario_animation(m, MARIO_ANIM_FIRST_PERSON); + return FALSE; +} + +s32 check_common_stationary_cancels(struct MarioState *m) { + if (m->pos[1] < m->waterLevel - 100) { + if (m->action == ACT_SPAWN_SPIN_LANDING) { + load_level_init_text(0); + } + update_mario_sound_and_camera(m); + return set_water_plunge_action(m); + } + + if (m->input & INPUT_SQUISHED) { + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, ACT_SQUISHED, 0); + } + + if (m->action != ACT_UNKNOWN_0002020E) { + if (m->health < 0x100) { + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, ACT_STANDING_DEATH, 0); + } + } + return FALSE; +} + +s32 mario_execute_stationary_action(struct MarioState *m) { + s32 cancel; + + if (check_common_stationary_cancels(m)) { + return TRUE; + } + + if (mario_update_quicksand(m, 0.5f)) { + return TRUE; + } + + /* clang-format off */ + switch (m->action) { + case ACT_IDLE: cancel = act_idle(m); break; + case ACT_START_SLEEPING: cancel = act_start_sleeping(m); break; + case ACT_SLEEPING: cancel = act_sleeping(m); break; + case ACT_WAKING_UP: cancel = act_waking_up(m); break; + case ACT_PANTING: cancel = act_panting(m); break; + case ACT_HOLD_PANTING_UNUSED: cancel = act_hold_panting_unused(m); break; + case ACT_HOLD_IDLE: cancel = act_hold_idle(m); break; + case ACT_HOLD_HEAVY_IDLE: cancel = act_hold_heavy_idle(m); break; + case ACT_IN_QUICKSAND: cancel = act_in_quicksand(m); break; + case ACT_STANDING_AGAINST_WALL: cancel = act_standing_against_wall(m); break; + case ACT_COUGHING: cancel = act_coughing(m); break; + case ACT_SHIVERING: cancel = act_shivering(m); break; + case ACT_CROUCHING: cancel = act_crouching(m); break; + case ACT_START_CROUCHING: cancel = act_start_crouching(m); break; + case ACT_STOP_CROUCHING: cancel = act_stop_crouching(m); break; + case ACT_START_CRAWLING: cancel = act_start_crawling(m); break; + case ACT_STOP_CRAWLING: cancel = act_stop_crawling(m); break; + case ACT_SLIDE_KICK_SLIDE_STOP: cancel = act_slide_kick_slide_stop(m); break; + case ACT_SHOCKWAVE_BOUNCE: cancel = act_shockwave_bounce(m); break; + case ACT_FIRST_PERSON: cancel = act_first_person(m); break; + case ACT_JUMP_LAND_STOP: cancel = act_jump_land_stop(m); break; + case ACT_DOUBLE_JUMP_LAND_STOP: cancel = act_double_jump_land_stop(m); break; + case ACT_FREEFALL_LAND_STOP: cancel = act_freefall_land_stop(m); break; + case ACT_SIDE_FLIP_LAND_STOP: cancel = act_side_flip_land_stop(m); break; + case ACT_HOLD_JUMP_LAND_STOP: cancel = act_hold_jump_land_stop(m); break; + case ACT_HOLD_FREEFALL_LAND_STOP: cancel = act_hold_freefall_land_stop(m); break; + case ACT_AIR_THROW_LAND: cancel = act_air_throw_land(m); break; + case ACT_LAVA_BOOST_LAND: cancel = act_lava_boost_land(m); break; + case ACT_TWIRL_LAND: cancel = act_twirl_land(m); break; + case ACT_TRIPLE_JUMP_LAND_STOP: cancel = act_triple_jump_land_stop(m); break; + case ACT_BACKFLIP_LAND_STOP: cancel = act_backflip_land_stop(m); break; + case ACT_LONG_JUMP_LAND_STOP: cancel = act_long_jump_land_stop(m); break; + case ACT_GROUND_POUND_LAND: cancel = act_ground_pound_land(m); break; + case ACT_BRAKING_STOP: cancel = act_braking_stop(m); break; + case ACT_BUTT_SLIDE_STOP: cancel = act_butt_slide_stop(m); break; + case ACT_HOLD_BUTT_SLIDE_STOP: cancel = act_hold_butt_slide_stop(m); break; + } + /* clang-format on */ + + if (!cancel && (m->input & INPUT_IN_WATER)) { + m->particleFlags |= PARTICLE_IDLE_WATER_WAVE; + } + + return cancel; +} diff --git a/src/game/mario_actions_stationary.h b/src/game/mario_actions_stationary.h new file mode 100644 index 0000000..ccc1e00 --- /dev/null +++ b/src/game/mario_actions_stationary.h @@ -0,0 +1,50 @@ +#ifndef MARIO_ACTIONS_STATIONARY +#define MARIO_ACTIONS_STATIONARY + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +s32 check_common_idle_cancels(struct MarioState *m); +s32 check_common_hold_idle_cancels(struct MarioState *m); +s32 act_idle(struct MarioState *m); +void play_anim_sound(struct MarioState *m, u32 actionState, s32 animFrame, u32 sound); +s32 act_start_sleeping(struct MarioState *m); +s32 act_sleeping(struct MarioState *m); +s32 act_waking_up(struct MarioState *m); +s32 act_shivering(struct MarioState *m); +s32 act_coughing(struct MarioState *m); +s32 act_standing_against_wall(struct MarioState *m); +s32 act_in_quicksand(struct MarioState *m); +s32 act_crouching(struct MarioState *m); +s32 act_panting(struct MarioState *m); +void stopping_step(struct MarioState *m, s32 animID, u32 action); +s32 act_braking_stop(struct MarioState *m); +s32 act_butt_slide_stop(struct MarioState *m); +s32 act_hold_butt_slide_stop(struct MarioState *m); +s32 act_slide_kick_slide_stop(struct MarioState *m); +s32 act_start_crouching(struct MarioState *m); +s32 act_stop_crouching(struct MarioState *m); +s32 act_start_crawling(struct MarioState *m); +s32 act_stop_crawling(struct MarioState *m); +s32 act_shockwave_bounce(struct MarioState *m); +s32 landing_step(struct MarioState *m, s32 arg1, u32 action); +s32 check_common_landing_cancels(struct MarioState *m, u32 action); +s32 act_jump_land_stop(struct MarioState *m); +s32 act_double_jump_land_stop(struct MarioState *m); +s32 act_side_flip_land_stop(struct MarioState *m); +s32 act_freefall_land_stop(struct MarioState *m); +s32 act_triple_jump_land_stop(struct MarioState *m); +s32 act_backflip_land_stop(struct MarioState *m); +s32 act_lava_boost_land(struct MarioState *m); +s32 act_long_jump_land_stop(struct MarioState *m); +s32 act_hold_jump_land_stop(struct MarioState *m); +s32 act_hold_freefall_land_stop(struct MarioState *m); +s32 act_air_throw_land(struct MarioState *m); +s32 act_twirl_land(struct MarioState *m); +s32 act_ground_pound_land(struct MarioState *m); +s32 act_first_person(struct MarioState *m); +s32 check_common_stationary_cancels(struct MarioState *m); +s32 mario_execute_stationary_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_STATIONARY diff --git a/src/game/mario_actions_submerged.c b/src/game/mario_actions_submerged.c new file mode 100644 index 0000000..e5b7056 --- /dev/null +++ b/src/game/mario_actions_submerged.c @@ -0,0 +1,1579 @@ +#include + +#include "../include/PR/ultratypes.h" +#include "../shim.h" + +#include "../include/sm64.h" +#include "level_update.h" +#include "memory.h" +#include "../engine/math_util.h" +#include "area.h" +#include "save_file.h" +//#include "sound_init.h" +#include "../engine/surface_collision.h" +#include "interaction.h" +#include "mario.h" +#include "mario_step.h" +#include "camera.h" +//#include "audio/external.h" +//#include "behavior_data.h" +//#include "level_table.h" +//#include "thread6.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + +#define MIN_SWIM_STRENGTH 160 +#define MIN_SWIM_SPEED 16.0f + +static s16 sWasAtSurface = FALSE; +static s16 sSwimStrength = MIN_SWIM_STRENGTH; +static s16 sWaterCurrentSpeeds[] = { 28, 12, 8, 4 }; + +static s16 D_80339FD0; +static s16 D_80339FD2; +static f32 D_80339FD4; + +static void set_swimming_at_surface_particles(struct MarioState *m, u32 particleFlag) { + s16 atSurface = m->pos[1] >= m->waterLevel - 130; + + if (atSurface) { + m->particleFlags |= particleFlag; + if (atSurface ^ sWasAtSurface) { + play_sound(SOUND_ACTION_UNKNOWN431, m->marioObj->header.gfx.cameraToObject); + } + } + + sWasAtSurface = atSurface; +} + +static s32 swimming_near_surface(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return FALSE; + } + + return (m->waterLevel - 80) - m->pos[1] < 400.0f; +} + +static f32 get_buoyancy(struct MarioState *m) { + f32 buoyancy = 0.0f; + + if (m->flags & MARIO_METAL_CAP) { + if (m->action & ACT_FLAG_INVULNERABLE) { + buoyancy = -2.0f; + } else { + buoyancy = -18.0f; + } + } else if (swimming_near_surface(m)) { + buoyancy = 1.25f; + } else if (!(m->action & ACT_FLAG_MOVING)) { + buoyancy = -2.0f; + } + + return buoyancy; +} + +static u32 perform_water_full_step(struct MarioState *m, Vec3f nextPos) { + struct Surface *wall; + struct Surface *ceil; + struct Surface *floor; + f32 ceilHeight; + f32 floorHeight; + + wall = resolve_and_return_wall_collisions(nextPos, 10.0f, 110.0f); + floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); + ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + + if (floor == NULL) { + return WATER_STEP_CANCELLED; + } + + if (nextPos[1] >= floorHeight) { + if (ceilHeight - nextPos[1] >= 160.0f) { + vec3f_copy(m->pos, nextPos); + m->floor = floor; + m->floorHeight = floorHeight; + + if (wall != NULL) { + return WATER_STEP_HIT_WALL; + } else { + return WATER_STEP_NONE; + } + } + + if (ceilHeight - floorHeight < 160.0f) { + return WATER_STEP_CANCELLED; + } + + //! Water ceiling downwarp + vec3f_set(m->pos, nextPos[0], ceilHeight - 160.0f, nextPos[2]); + m->floor = floor; + m->floorHeight = floorHeight; + return WATER_STEP_HIT_CEILING; + } else { + if (ceilHeight - floorHeight < 160.0f) { + return WATER_STEP_CANCELLED; + } + + vec3f_set(m->pos, nextPos[0], floorHeight, nextPos[2]); + m->floor = floor; + m->floorHeight = floorHeight; + return WATER_STEP_HIT_FLOOR; + } +} + +static void apply_water_current(struct MarioState *m, Vec3f step) { + s32 i; + f32 whirlpoolRadius = 2000.0f; + + if (m->floor->type == SURFACE_FLOWING_WATER) { + s16 currentAngle = m->floor->force << 8; + f32 currentSpeed = sWaterCurrentSpeeds[m->floor->force >> 8]; + + step[0] += currentSpeed * sins(currentAngle); + step[2] += currentSpeed * coss(currentAngle); + } + + for (i = 0; i < 2; i++) { + struct Whirlpool *whirlpool = gCurrentArea->whirlpools[i]; + if (whirlpool != NULL) { + f32 strength = 0.0f; + + f32 dx = whirlpool->pos[0] - m->pos[0]; + f32 dy = whirlpool->pos[1] - m->pos[1]; + f32 dz = whirlpool->pos[2] - m->pos[2]; + + f32 lateralDist = sqrtf(dx * dx + dz * dz); + f32 distance = sqrtf(lateralDist * lateralDist + dy * dy); + + s16 pitchToWhirlpool = atan2s(lateralDist, dy); + s16 yawToWhirlpool = atan2s(dz, dx); + + yawToWhirlpool -= (s16)(0x2000 * 1000.0f / (distance + 1000.0f)); + + if (whirlpool->strength >= 0) { +// if (gCurrLevelNum == LEVEL_DDD && gCurrAreaIndex == 2) { +// whirlpoolRadius = 4000.0f; +// } + + if (distance >= 26.0f && distance < whirlpoolRadius) { + strength = whirlpool->strength * (1.0f - distance / whirlpoolRadius); + } + } else if (distance < 2000.0f) { + strength = whirlpool->strength * (1.0f - distance / 2000.0f); + } + + step[0] += strength * coss(pitchToWhirlpool) * sins(yawToWhirlpool); + step[1] += strength * sins(pitchToWhirlpool); + step[2] += strength * coss(pitchToWhirlpool) * coss(yawToWhirlpool); + } + } +} + +static u32 perform_water_step(struct MarioState *m) { + UNUSED u32 unused; + u32 stepResult; + Vec3f nextPos; + Vec3f step; + struct Object *marioObj = m->marioObj; + + vec3f_copy(step, m->vel); + + if (m->action & ACT_FLAG_SWIMMING) { + apply_water_current(m, step); + } + + nextPos[0] = m->pos[0] + step[0]; + nextPos[1] = m->pos[1] + step[1]; + nextPos[2] = m->pos[2] + step[2]; + + if (nextPos[1] > m->waterLevel - 80) { + nextPos[1] = m->waterLevel - 80; + m->vel[1] = 0.0f; + } + + stepResult = perform_water_full_step(m, nextPos); + + vec3f_copy(marioObj->header.gfx.pos, m->pos); + vec3s_set(marioObj->header.gfx.angle, -m->faceAngle[0], m->faceAngle[1], m->faceAngle[2]); + + return stepResult; +} + +static BAD_RETURN(u32) update_water_pitch(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + if (marioObj->header.gfx.angle[0] > 0) { + marioObj->header.gfx.pos[1] += + 60.0f * sins(marioObj->header.gfx.angle[0]) * sins(marioObj->header.gfx.angle[0]); + } + + if (marioObj->header.gfx.angle[0] < 0) { + marioObj->header.gfx.angle[0] = marioObj->header.gfx.angle[0] * 6 / 10; + } + + if (marioObj->header.gfx.angle[0] > 0) { + marioObj->header.gfx.angle[0] = marioObj->header.gfx.angle[0] * 10 / 8; + } +} + +static void stationary_slow_down(struct MarioState *m) { + f32 buoyancy = get_buoyancy(m); + + m->angleVel[0] = 0; + m->angleVel[1] = 0; + + m->forwardVel = approach_f32(m->forwardVel, 0.0f, 1.0f, 1.0f); + m->vel[1] = approach_f32(m->vel[1], buoyancy, 2.0f, 1.0f); + + m->faceAngle[0] = approach_s32(m->faceAngle[0], 0, 0x200, 0x200); + m->faceAngle[2] = approach_s32(m->faceAngle[2], 0, 0x100, 0x100); + + m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]); + m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]); +} + +static void update_swimming_speed(struct MarioState *m, f32 decelThreshold) { + f32 buoyancy = get_buoyancy(m); + f32 maxSpeed = 28.0f; + + if (m->action & ACT_FLAG_STATIONARY) { + m->forwardVel -= 2.0f; + } + + if (m->forwardVel < 0.0f) { + m->forwardVel = 0.0f; + } + + if (m->forwardVel > maxSpeed) { + m->forwardVel = maxSpeed; + } + + if (m->forwardVel > decelThreshold) { + m->forwardVel -= 0.5f; + } + + m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]); + m->vel[1] = m->forwardVel * sins(m->faceAngle[0]) + buoyancy; + m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]); +} + +static void update_swimming_yaw(struct MarioState *m) { + s16 targetYawVel = -(s16)(10.0f * m->controller->stickX); + + if (targetYawVel > 0) { + if (m->angleVel[1] < 0) { + m->angleVel[1] += 0x40; + if (m->angleVel[1] > 0x10) { + m->angleVel[1] = 0x10; + } + } else { + m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x10, 0x20); + } + } else if (targetYawVel < 0) { + if (m->angleVel[1] > 0) { + m->angleVel[1] -= 0x40; + if (m->angleVel[1] < -0x10) { + m->angleVel[1] = -0x10; + } + } else { + m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x20, 0x10); + } + } else { + m->angleVel[1] = approach_s32(m->angleVel[1], 0, 0x40, 0x40); + } + + m->faceAngle[1] += m->angleVel[1]; + m->faceAngle[2] = -m->angleVel[1] * 8; +} + +static void update_swimming_pitch(struct MarioState *m) { + s16 targetPitch = -(s16)(252.0f * m->controller->stickY); + + s16 pitchVel; + if (m->faceAngle[0] < 0) { + pitchVel = 0x100; + } else { + pitchVel = 0x200; + } + + if (m->faceAngle[0] < targetPitch) { + if ((m->faceAngle[0] += pitchVel) > targetPitch) { + m->faceAngle[0] = targetPitch; + } + } else if (m->faceAngle[0] > targetPitch) { + if ((m->faceAngle[0] -= pitchVel) < targetPitch) { + m->faceAngle[0] = targetPitch; + } + } +} + +static void common_idle_step(struct MarioState *m, s32 animation, s32 arg) { + s16 *val = &m->marioBodyState->headAngle[0]; + + update_swimming_yaw(m); + update_swimming_pitch(m); + update_swimming_speed(m, MIN_SWIM_SPEED); + perform_water_step(m); + update_water_pitch(m); + + if (m->faceAngle[0] > 0) { + *val = approach_s32(*val, m->faceAngle[0] / 2, 0x80, 0x200); + } else { + *val = approach_s32(*val, 0, 0x200, 0x200); + } + + if (arg == 0) { + set_mario_animation(m, animation); + } else { + set_mario_anim_with_accel(m, animation, arg); + } + + set_swimming_at_surface_particles(m, PARTICLE_IDLE_WATER_WAVE); +} + +static s32 act_water_idle(struct MarioState *m) { + u32 val = 0x10000; + + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_PUNCH, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_BREASTSTROKE, 0); + } + + if (m->faceAngle[0] < -0x1000) { + val = 0x30000; + } + + common_idle_step(m, MARIO_ANIM_WATER_IDLE, val); + return FALSE; +} + +static s32 act_hold_water_idle(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0); + } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_THROW, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_HOLD_BREASTSTROKE, 0); + } + + common_idle_step(m, MARIO_ANIM_WATER_IDLE_WITH_OBJ, 0); + return FALSE; +} + +static s32 act_water_action_end(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_PUNCH, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_BREASTSTROKE, 0); + } + + common_idle_step(m, MARIO_ANIM_WATER_ACTION_END, 0); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_WATER_IDLE, 0); + } + return FALSE; +} + +static s32 act_hold_water_action_end(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0); + } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_THROW, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_HOLD_BREASTSTROKE, 0); + } + + common_idle_step( + m, m->actionArg == 0 ? MARIO_ANIM_WATER_ACTION_END_WITH_OBJ : MARIO_ANIM_STOP_GRAB_OBJ_WATER, + 0); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + return FALSE; +} + +static void reset_float_globals(struct MarioState *m) { + D_80339FD0 = 0; + D_80339FD2 = 0x800; + D_80339FD4 = m->faceAngle[0] / 256.0f + 20.0f; +} + +static void float_surface_gfx(struct MarioState *m) { + if (D_80339FD2 != 0 && m->pos[1] > m->waterLevel - 85 && m->faceAngle[0] >= 0) { + if ((D_80339FD0 += D_80339FD2) >= 0) { + m->marioObj->header.gfx.pos[1] += D_80339FD4 * sins(D_80339FD0); + return; + } + } + + D_80339FD2 = 0; +} + +static void common_swimming_step(struct MarioState *m, s16 swimStrength) { + s16 floorPitch; + UNUSED struct Object *marioObj = m->marioObj; + + update_swimming_yaw(m); + update_swimming_pitch(m); + update_swimming_speed(m, swimStrength / 10.0f); + + switch (perform_water_step(m)) { + case WATER_STEP_HIT_FLOOR: + floorPitch = -find_floor_slope(m, -0x8000); + if (m->faceAngle[0] < floorPitch) { + m->faceAngle[0] = floorPitch; + } + break; + + case WATER_STEP_HIT_CEILING: + if (m->faceAngle[0] > -0x3000) { + m->faceAngle[0] -= 0x100; + } + break; + + case WATER_STEP_HIT_WALL: + if (m->controller->stickY == 0.0f) { + if (m->faceAngle[0] > 0.0f) { + m->faceAngle[0] += 0x200; + if (m->faceAngle[0] > 0x3F00) { + m->faceAngle[0] = 0x3F00; + } + } else { + m->faceAngle[0] -= 0x200; + if (m->faceAngle[0] < -0x3F00) { + m->faceAngle[0] = -0x3F00; + } + } + } + break; + } + + update_water_pitch(m); + m->marioBodyState->headAngle[0] = approach_s32(m->marioBodyState->headAngle[0], 0, 0x200, 0x200); + + float_surface_gfx(m); + set_swimming_at_surface_particles(m, PARTICLE_WAVE_TRAIL); +} + +static void play_swimming_noise(struct MarioState *m) { + s16 animFrame = m->marioObj->header.gfx.animInfo.animFrame; + + // This must be one line to match on -O2 + if (animFrame == 0 || animFrame == 12) play_sound(SOUND_ACTION_UNKNOWN434, m->marioObj->header.gfx.cameraToObject); +} + +static s32 check_water_jump(struct MarioState *m) { + s32 probe = (s32)(m->pos[1] + 1.5f); + + if (m->input & INPUT_A_PRESSED) { + if (probe >= m->waterLevel - 80 && m->faceAngle[0] >= 0 && m->controller->stickY < -60.0f) { + vec3s_set(m->angleVel, 0, 0, 0); + + m->vel[1] = 62.0f; + + if (m->heldObj == NULL) { + return set_mario_action(m, ACT_WATER_JUMP, 0); + } else { + return set_mario_action(m, ACT_HOLD_WATER_JUMP, 0); + } + } + } + + return FALSE; +} + +static s32 act_breaststroke(struct MarioState *m) { + if (m->actionArg == 0) { + sSwimStrength = MIN_SWIM_STRENGTH; + } + + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_PUNCH, 0); + } + + if (++m->actionTimer == 14) { + return set_mario_action(m, ACT_FLUTTER_KICK, 0); + } + + if (check_water_jump(m)) { + return TRUE; + } + + if (m->actionTimer < 6) { + m->forwardVel += 0.5f; + } + + if (m->actionTimer >= 9) { + m->forwardVel += 1.5f; + } + + if (m->actionTimer >= 2) { + if (m->actionTimer < 6 && (m->input & INPUT_A_PRESSED)) { + m->actionState = 1; + } + + if (m->actionTimer == 9 && m->actionState == 1) { + set_anim_to_frame(m, 0); + m->actionState = 0; + m->actionTimer = 1; + sSwimStrength = MIN_SWIM_STRENGTH; + } + } + + if (m->actionTimer == 1) { + play_sound(sSwimStrength == MIN_SWIM_STRENGTH ? SOUND_ACTION_SWIM : SOUND_ACTION_SWIM_FAST, + m->marioObj->header.gfx.cameraToObject); + reset_float_globals(m); + } + +#ifdef VERSION_SH + if (m->actionTimer < 6) { + func_sh_8024CA04(); + } +#endif + + set_mario_animation(m, MARIO_ANIM_SWIM_PART1); + common_swimming_step(m, sSwimStrength); + + return FALSE; +} + +static s32 act_swimming_end(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_PUNCH, 0); + } + + if (m->actionTimer >= 15) { + return set_mario_action(m, ACT_WATER_ACTION_END, 0); + } + + if (check_water_jump(m)) { + return TRUE; + } + + if ((m->input & INPUT_A_DOWN) && m->actionTimer >= 7) { + if (m->actionTimer == 7 && sSwimStrength < 280) { + sSwimStrength += 10; + } + return set_mario_action(m, ACT_BREASTSTROKE, 1); + } + + if (m->actionTimer >= 7) { + sSwimStrength = MIN_SWIM_STRENGTH; + } + + m->actionTimer++; + + m->forwardVel -= 0.25f; + set_mario_animation(m, MARIO_ANIM_SWIM_PART2); + common_swimming_step(m, sSwimStrength); + + return FALSE; +} + +static s32 act_flutter_kick(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_METAL_WATER_FALLING, 1); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_PUNCH, 0); + } + + if (!(m->input & INPUT_A_DOWN)) { + if (m->actionTimer == 0 && sSwimStrength < 280) { + sSwimStrength += 10; + } + return set_mario_action(m, ACT_SWIMMING_END, 0); + } + + m->forwardVel = approach_f32(m->forwardVel, 12.0f, 0.1f, 0.15f); + m->actionTimer = 1; + sSwimStrength = MIN_SWIM_STRENGTH; + + if (m->forwardVel < 14.0f) { + play_swimming_noise(m); + set_mario_animation(m, MARIO_ANIM_FLUTTERKICK); + } + + common_swimming_step(m, sSwimStrength); + return FALSE; +} + +static s32 act_hold_breaststroke(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0); + } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (++m->actionTimer == 17) { + return set_mario_action(m, ACT_HOLD_FLUTTER_KICK, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_THROW, 0); + } + + if (check_water_jump(m)) { + return TRUE; + } + + if (m->actionTimer < 6) { + m->forwardVel += 0.5f; + } + + if (m->actionTimer >= 9) { + m->forwardVel += 1.5f; + } + + if (m->actionTimer >= 2) { + if (m->actionTimer < 6 && (m->input & INPUT_A_PRESSED)) { + m->actionState = 1; + } + + if (m->actionTimer == 9 && m->actionState == 1) { + set_anim_to_frame(m, 0); + m->actionState = 0; + m->actionTimer = 1; + } + } + + if (m->actionTimer == 1) { + play_sound(SOUND_ACTION_SWIM, m->marioObj->header.gfx.cameraToObject); + reset_float_globals(m); + } + + set_mario_animation(m, MARIO_ANIM_SWIM_WITH_OBJ_PART1); + common_swimming_step(m, 0x00A0); + return FALSE; +} + +static s32 act_hold_swimming_end(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0); + } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->actionTimer >= 15) { + return set_mario_action(m, ACT_HOLD_WATER_ACTION_END, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_THROW, 0); + } + + if (check_water_jump(m)) { + return TRUE; + } + + if ((m->input & INPUT_A_DOWN) && m->actionTimer >= 7) { + return set_mario_action(m, ACT_HOLD_BREASTSTROKE, 0); + } + + m->actionTimer++; + + m->forwardVel -= 0.25f; + set_mario_animation(m, MARIO_ANIM_SWIM_WITH_OBJ_PART2); + common_swimming_step(m, 0x00A0); + return FALSE; +} + +static s32 act_hold_flutter_kick(struct MarioState *m) { + if (m->flags & MARIO_METAL_CAP) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0); + } + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_THROW, 0); + } + + if (!(m->input & INPUT_A_DOWN)) { + return set_mario_action(m, ACT_HOLD_SWIMMING_END, 0); + } + + m->forwardVel = approach_f32(m->forwardVel, 12.0f, 0.1f, 0.15f); + if (m->forwardVel < 14.0f) { + play_swimming_noise(m); + set_mario_animation(m, MARIO_ANIM_FLUTTERKICK_WITH_OBJ); + } + common_swimming_step(m, 0x00A0); + return FALSE; +} + +static s32 act_water_shell_swimming(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_B_PRESSED) { + return set_mario_action(m, ACT_WATER_THROW, 0); + } + + if (m->actionTimer++ == 240) { + m->heldObj->oInteractStatus = INT_STATUS_STOP_RIDING; + m->heldObj = NULL; + stop_shell_music(); + set_mario_action(m, ACT_FLUTTER_KICK, 0); + } + + m->forwardVel = approach_f32(m->forwardVel, 30.0f, 2.0f, 1.0f); + + play_swimming_noise(m); + set_mario_animation(m, MARIO_ANIM_FLUTTERKICK_WITH_OBJ); + common_swimming_step(m, 0x012C); + + return FALSE; +} + +static s32 check_water_grab(struct MarioState *m) { + //! Heave hos have the grabbable interaction type but are not normally + // grabbable. Since water grabbing doesn't check the appropriate input flag, + // you can use water grab to pick up heave ho. + if (m->marioObj->collidedObjInteractTypes & INTERACT_GRABBABLE) { + struct Object *object = mario_get_collided_object(m, INTERACT_GRABBABLE); + f32 dx = object->oPosX - m->pos[0]; + f32 dz = object->oPosZ - m->pos[2]; + s16 dAngleToObject = atan2s(dz, dx) - m->faceAngle[1]; + + if (dAngleToObject >= -0x2AAA && dAngleToObject <= 0x2AAA) { + m->usedObj = object; + mario_grab_used_object(m); + m->marioBodyState->grabPos = GRAB_POS_LIGHT_OBJ; + return TRUE; + } + } + + return FALSE; +} + +static s32 act_water_throw(struct MarioState *m) { + update_swimming_yaw(m); + update_swimming_pitch(m); + update_swimming_speed(m, MIN_SWIM_SPEED); + perform_water_step(m); + update_water_pitch(m); + + set_mario_animation(m, MARIO_ANIM_WATER_THROW_OBJ); + play_sound_if_no_flag(m, SOUND_ACTION_SWIM, MARIO_ACTION_SOUND_PLAYED); + + m->marioBodyState->headAngle[0] = approach_s32(m->marioBodyState->headAngle[0], 0, 0x200, 0x200); + + if (m->actionTimer++ == 5) { + mario_throw_held_object(m); +#ifdef VERSION_SH + queue_rumble_data(3, 50); +#endif + } + + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_WATER_IDLE, 0); + } + + return FALSE; +} + +static s32 act_water_punch(struct MarioState *m) { + if (m->forwardVel < 7.0f) { + m->forwardVel += 1.0f; + } + + update_swimming_yaw(m); + update_swimming_pitch(m); + update_swimming_speed(m, MIN_SWIM_SPEED); + perform_water_step(m); + update_water_pitch(m); + + m->marioBodyState->headAngle[0] = approach_s32(m->marioBodyState->headAngle[0], 0, 0x200, 0x200); + + play_sound_if_no_flag(m, SOUND_ACTION_SWIM, MARIO_ACTION_SOUND_PLAYED); + + switch (m->actionState) { + case 0: + set_mario_animation(m, MARIO_ANIM_WATER_GRAB_OBJ_PART1); + if (is_anim_at_end(m)) { + m->actionState = check_water_grab(m) + 1; + } + break; + + case 1: + set_mario_animation(m, MARIO_ANIM_WATER_GRAB_OBJ_PART2); + if (is_anim_at_end(m)) { + set_mario_action(m, ACT_WATER_ACTION_END, 0); + } + break; + + case 2: + set_mario_animation(m, MARIO_ANIM_WATER_PICK_UP_OBJ); + if (is_anim_at_end(m)) { +// if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) { +// play_shell_music(); +// set_mario_action(m, ACT_WATER_SHELL_SWIMMING, 0); +// } else { + set_mario_action(m, ACT_HOLD_WATER_ACTION_END, 1); +// } + } + break; + } + + return FALSE; +} + +static void common_water_knockback_step(struct MarioState *m, s32 animation, u32 endAction, s32 arg3) { + stationary_slow_down(m); + perform_water_step(m); + set_mario_animation(m, animation); + + m->marioBodyState->headAngle[0] = 0; + + if (is_anim_at_end(m)) { + if (arg3 > 0) { + m->invincTimer = 30; + } + + set_mario_action(m, m->health >= 0x100 ? endAction : ACT_WATER_DEATH, 0); + } +} + +static s32 act_backward_water_kb(struct MarioState *m) { + common_water_knockback_step(m, MARIO_ANIM_BACKWARDS_WATER_KB, ACT_WATER_IDLE, m->actionArg); + return FALSE; +} + +static s32 act_forward_water_kb(struct MarioState *m) { + common_water_knockback_step(m, MARIO_ANIM_WATER_FORWARD_KB, ACT_WATER_IDLE, m->actionArg); + return FALSE; +} + +static s32 act_water_shocked(struct MarioState *m) { + play_sound_if_no_flag(m, SOUND_MARIO_WAAAOOOW, MARIO_ACTION_SOUND_PLAYED); + play_sound(SOUND_MOVING_SHOCKED, m->marioObj->header.gfx.cameraToObject); + set_camera_shake_from_hit(SHAKE_SHOCK); + + if (set_mario_animation(m, MARIO_ANIM_SHOCKED) == 0) { + m->actionTimer++; + m->flags |= MARIO_METAL_SHOCK; + } + + if (m->actionTimer >= 6) { + m->invincTimer = 30; + set_mario_action(m, m->health < 0x100 ? ACT_WATER_DEATH : ACT_WATER_IDLE, 0); + } + + stationary_slow_down(m); + perform_water_step(m); + m->marioBodyState->headAngle[0] = 0; + return FALSE; +} + +static s32 act_drowning(struct MarioState *m) { + switch (m->actionState) { + case 0: + set_mario_animation(m, MARIO_ANIM_DROWNING_PART1); + m->marioBodyState->eyeState = MARIO_EYES_HALF_CLOSED; + if (is_anim_at_end(m)) { + m->actionState = 1; + } + break; + + case 1: + set_mario_animation(m, MARIO_ANIM_DROWNING_PART2); + m->marioBodyState->eyeState = MARIO_EYES_DEAD; + if (m->marioObj->header.gfx.animInfo.animFrame == 30) { + level_trigger_warp(m, WARP_OP_DEATH); + } + break; + } + + play_sound_if_no_flag(m, SOUND_MARIO_DROWNING, MARIO_ACTION_SOUND_PLAYED); + stationary_slow_down(m); + perform_water_step(m); + + return FALSE; +} + +static s32 act_water_death(struct MarioState *m) { + stationary_slow_down(m); + perform_water_step(m); + + m->marioBodyState->eyeState = MARIO_EYES_DEAD; + + set_mario_animation(m, MARIO_ANIM_WATER_DYING); + if (set_mario_animation(m, MARIO_ANIM_WATER_DYING) == 35) { + level_trigger_warp(m, WARP_OP_DEATH); + } + + return FALSE; +} + +static s32 act_water_plunge(struct MarioState *m) { + u32 stepResult; + s32 stateFlags = m->heldObj != NULL; + + f32 endVSpeed; + if (swimming_near_surface(m)) { + endVSpeed = 0.0f; + } else { + endVSpeed = -5.0f; + } + + if (m->flags & MARIO_METAL_CAP) { + stateFlags |= 4; + } else if ((m->prevAction & ACT_FLAG_DIVING) || (m->input & INPUT_A_DOWN)) { + stateFlags |= 2; + } + + m->actionTimer++; + + stationary_slow_down(m); + + stepResult = perform_water_step(m); + + if (m->actionState == 0) { + play_sound(SOUND_ACTION_UNKNOWN430, m->marioObj->header.gfx.cameraToObject); + if (m->peakHeight - m->pos[1] > 1150.0f) { + play_sound(SOUND_MARIO_HAHA_2, m->marioObj->header.gfx.cameraToObject); + } + + m->particleFlags |= PARTICLE_WATER_SPLASH; + m->actionState = 1; +#ifdef VERSION_SH + if (m->prevAction & ACT_FLAG_AIR) { + queue_rumble_data(5, 80); + } +#endif + } + + if (stepResult == WATER_STEP_HIT_FLOOR || m->vel[1] >= endVSpeed || m->actionTimer > 20) { + switch (stateFlags) { + case 0: + set_mario_action(m, ACT_WATER_ACTION_END, 0); + break; + case 1: + set_mario_action(m, ACT_HOLD_WATER_ACTION_END, 0); + break; + case 2: + set_mario_action(m, ACT_FLUTTER_KICK, 0); + break; + case 3: + set_mario_action(m, ACT_HOLD_FLUTTER_KICK, 0); + break; + case 4: + set_mario_action(m, ACT_METAL_WATER_FALLING, 0); + break; + case 5: + set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 0); + break; + } + D_80339FD2 = 0; + } + + switch (stateFlags) { + case 0: + set_mario_animation(m, MARIO_ANIM_WATER_ACTION_END); + break; + case 1: + set_mario_animation(m, MARIO_ANIM_WATER_ACTION_END_WITH_OBJ); + break; + case 2: + set_mario_animation(m, MARIO_ANIM_FLUTTERKICK); + break; + case 3: + set_mario_animation(m, MARIO_ANIM_FLUTTERKICK_WITH_OBJ); + break; + case 4: + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + break; + case 5: + set_mario_animation(m, MARIO_ANIM_FALL_WITH_LIGHT_OBJ); + break; + } + + m->particleFlags |= PARTICLE_PLUNGE_BUBBLE; + return FALSE; +} + +static s32 act_caught_in_whirlpool(struct MarioState *m) { + f32 sinAngleChange; + f32 cosAngleChange; + f32 newDistance; + s16 angleChange; + + struct Object *marioObj = m->marioObj; + struct Object *whirlpool = m->usedObj; + + f32 dx = m->pos[0] - whirlpool->oPosX; + f32 dz = m->pos[2] - whirlpool->oPosZ; + f32 distance = sqrtf(dx * dx + dz * dz); + + if ((marioObj->oMarioWhirlpoolPosY += m->vel[1]) < 0.0f) { + marioObj->oMarioWhirlpoolPosY = 0.0f; + if (distance < 16.1f && m->actionTimer++ == 16) { + level_trigger_warp(m, WARP_OP_DEATH); + } + } + + if (distance <= 28.0f) { + newDistance = 16.0f; + angleChange = 0x1800; + } else if (distance < 256.0f) { + newDistance = distance - (12.0f - distance / 32.0f); + angleChange = (s16)(0x1C00 - distance * 20.0f); + } else { + newDistance = distance - 4.0f; + angleChange = 0x800; + } + + m->vel[1] = -640.0f / (newDistance + 16.0f); + + sinAngleChange = sins(angleChange); + cosAngleChange = coss(angleChange); + + if (distance < 1.0f) { + dx = newDistance * sins(m->faceAngle[1]); + dz = newDistance * coss(m->faceAngle[1]); + } else { + dx *= newDistance / distance; + dz *= newDistance / distance; + } + + m->pos[0] = whirlpool->oPosX + dx * cosAngleChange + dz * sinAngleChange; + m->pos[2] = whirlpool->oPosZ - dx * sinAngleChange + dz * cosAngleChange; + m->pos[1] = whirlpool->oPosY + marioObj->oMarioWhirlpoolPosY; + + m->faceAngle[1] = atan2s(dz, dx) + 0x8000; + + set_mario_animation(m, MARIO_ANIM_GENERAL_FALL); + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); +#ifdef VERSION_SH + reset_rumble_timers(); +#endif + + return FALSE; +} + +static void play_metal_water_jumping_sound(struct MarioState *m, u32 landing) { + if (!(m->flags & MARIO_ACTION_SOUND_PLAYED)) { + m->particleFlags |= PARTICLE_MIST_CIRCLE; + } + + play_sound_if_no_flag(m, landing ? SOUND_ACTION_METAL_LAND_WATER : SOUND_ACTION_METAL_JUMP_WATER, + MARIO_ACTION_SOUND_PLAYED); +} + +static void play_metal_water_walking_sound(struct MarioState *m) { + if (is_anim_past_frame(m, 10) || is_anim_past_frame(m, 49)) { + play_sound(SOUND_ACTION_METAL_STEP_WATER, m->marioObj->header.gfx.cameraToObject); + m->particleFlags |= PARTICLE_DUST; + } +} + +static void update_metal_water_walking_speed(struct MarioState *m) { + f32 val = m->intendedMag / 1.5f; + + if (m->forwardVel <= 0.0f) { + m->forwardVel += 1.1f; + } else if (m->forwardVel <= val) { + m->forwardVel += 1.1f - m->forwardVel / 43.0f; + } else if (m->floor->normal.y >= 0.95f) { + m->forwardVel -= 1.0f; + } + + if (m->forwardVel > 32.0f) { + m->forwardVel = 32.0f; + } + + m->faceAngle[1] = + m->intendedYaw - approach_s32((s16)(m->intendedYaw - m->faceAngle[1]), 0, 0x800, 0x800); + + m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); + + m->vel[0] = m->slideVelX; + m->vel[1] = 0.0f; + m->vel[2] = m->slideVelZ; +} + +static s32 update_metal_water_jump_speed(struct MarioState *m) { + UNUSED f32 nextY = m->pos[1] + m->vel[1]; + f32 waterSurface = m->waterLevel - 100; + + if (m->vel[1] > 0.0f && m->pos[1] > waterSurface) { + return TRUE; + } + + if (m->input & INPUT_NONZERO_ANALOG) { + s16 intendedDYaw = m->intendedYaw - m->faceAngle[1]; + m->forwardVel += 0.8f * coss(intendedDYaw); + m->faceAngle[1] += 0x200 * sins(intendedDYaw); + } else { + m->forwardVel = approach_f32(m->forwardVel, 0.0f, 0.25f, 0.25f); + } + + if (m->forwardVel > 16.0f) { + m->forwardVel -= 1.0f; + } + + if (m->forwardVel < 0.0f) { + m->forwardVel += 2.0f; + } + + m->vel[0] = m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->vel[2] = m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); + return FALSE; +} + +static s32 act_metal_water_standing(struct MarioState *m) { + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_METAL_WATER_JUMP, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_METAL_WATER_WALKING, 0); + } + + switch (m->actionState) { + case 0: + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_LEFT); + break; + case 1: + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_RIGHT); + break; + case 2: + set_mario_animation(m, MARIO_ANIM_IDLE_HEAD_CENTER); + break; + } + + if (is_anim_at_end(m) && ++m->actionState == 3) { + m->actionState = 0; + } + + stop_and_set_height_to_floor(m); + if (m->pos[1] >= m->waterLevel - 150) { + m->particleFlags |= PARTICLE_IDLE_WATER_WAVE; + } + + return FALSE; +} + +static s32 act_hold_metal_water_standing(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_JUMP, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_WALKING, 0); + } + + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_IDLE_WITH_LIGHT_OBJ); + return FALSE; +} + +static s32 act_metal_water_walking(struct MarioState *m) { + s32 val04; + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_FIRST_PERSON) { + return set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_METAL_WATER_JUMP, 0); + } + + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + if ((val04 = (s32)(m->forwardVel / 4.0f * 0x10000)) < 0x1000) { + val04 = 0x1000; + } + + set_mario_anim_with_accel(m, MARIO_ANIM_WALKING, val04); + play_metal_water_walking_sound(m); + update_metal_water_walking_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_METAL_WATER_FALLING, 1); + break; + + case GROUND_STEP_HIT_WALL: + m->forwardVel = 0.0f; + break; + } + + return FALSE; +} + +static s32 act_hold_metal_water_walking(struct MarioState *m) { + s32 val04; + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_METAL_WATER_WALKING, 0); + } + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + + if (m->input & INPUT_A_PRESSED) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_JUMP, 0); + } + + if (m->input & INPUT_UNKNOWN_5) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_STANDING, 0); + } + + m->intendedMag *= 0.4f; + + if ((val04 = (s32)(m->forwardVel / 2.0f * 0x10000)) < 0x1000) { + val04 = 0x1000; + } + + set_mario_anim_with_accel(m, MARIO_ANIM_RUN_WITH_LIGHT_OBJ, val04); + play_metal_water_walking_sound(m); + update_metal_water_walking_speed(m); + + switch (perform_ground_step(m)) { + case GROUND_STEP_LEFT_GROUND: + set_mario_action(m, ACT_HOLD_METAL_WATER_FALLING, 1); + break; + + case GROUND_STEP_HIT_WALL: + m->forwardVel = 0.0f; + break; + } + + return FALSE; +} + +static s32 act_metal_water_jump(struct MarioState *m) { + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (update_metal_water_jump_speed(m)) { + return set_mario_action(m, ACT_WATER_JUMP, 1); + } + + play_metal_water_jumping_sound(m, FALSE); + set_mario_animation(m, MARIO_ANIM_SINGLE_JUMP); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_METAL_WATER_JUMP_LAND, 0); + break; + + case AIR_STEP_HIT_WALL: + m->forwardVel = 0.0f; + break; + } + + return FALSE; +} + +static s32 act_hold_metal_water_jump(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_METAL_WATER_FALLING, 0); + } + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + + if (update_metal_water_jump_speed(m)) { + return set_mario_action(m, ACT_HOLD_WATER_JUMP, 1); + } + + play_metal_water_jumping_sound(m, FALSE); + set_mario_animation(m, MARIO_ANIM_JUMP_WITH_LIGHT_OBJ); + + switch (perform_air_step(m, 0)) { + case AIR_STEP_LANDED: + set_mario_action(m, ACT_HOLD_METAL_WATER_JUMP_LAND, 0); + break; + + case AIR_STEP_HIT_WALL: + m->forwardVel = 0.0f; + break; + } + + return FALSE; +} + +static s32 act_metal_water_falling(struct MarioState *m) { + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + m->faceAngle[1] += 0x400 * sins(m->intendedYaw - m->faceAngle[1]); + } + + set_mario_animation(m, m->actionArg == 0 ? MARIO_ANIM_GENERAL_FALL : MARIO_ANIM_FALL_FROM_WATER); + stationary_slow_down(m); + + if (perform_water_step(m) & WATER_STEP_HIT_FLOOR) { // hit floor or cancelled + set_mario_action(m, ACT_METAL_WATER_FALL_LAND, 0); + } + + return FALSE; +} + +static s32 act_hold_metal_water_falling(struct MarioState *m) { + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_METAL_WATER_FALLING, 0); + } + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + m->faceAngle[1] += 0x400 * sins(m->intendedYaw - m->faceAngle[1]); + } + + set_mario_animation(m, MARIO_ANIM_FALL_WITH_LIGHT_OBJ); + stationary_slow_down(m); + + if (perform_water_step(m) & WATER_STEP_HIT_FLOOR) { // hit floor or cancelled + set_mario_action(m, ACT_HOLD_METAL_WATER_FALL_LAND, 0); + } + + return FALSE; +} + +static s32 act_metal_water_jump_land(struct MarioState *m) { + play_metal_water_jumping_sound(m, TRUE); + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_METAL_WATER_WALKING, 0); + } + + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_LAND_FROM_SINGLE_JUMP); + + if (is_anim_at_end(m)) { + return set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + return FALSE; +} + +static s32 act_hold_metal_water_jump_land(struct MarioState *m) { + play_metal_water_jumping_sound(m, TRUE); + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_WALKING, 0); + } + + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_JUMP_LAND_WITH_LIGHT_OBJ); + + if (is_anim_at_end(m)) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_STANDING, 0); + } + + return FALSE; +} + +static s32 act_metal_water_fall_land(struct MarioState *m) { + play_metal_water_jumping_sound(m, TRUE); + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_WATER_IDLE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_METAL_WATER_WALKING, 0); + } + + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_GENERAL_LAND); + + if (is_anim_at_end(m)) { + return set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + return FALSE; +} + +static s32 act_hold_metal_water_fall_land(struct MarioState *m) { + play_metal_water_jumping_sound(m, TRUE); + + if (m->marioObj->oInteractStatus & INT_STATUS_MARIO_DROP_OBJECT) { + return drop_and_set_mario_action(m, ACT_METAL_WATER_STANDING, 0); + } + + if (!(m->flags & MARIO_METAL_CAP)) { + return set_mario_action(m, ACT_HOLD_WATER_IDLE, 0); + } + + if (m->input & INPUT_NONZERO_ANALOG) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_WALKING, 0); + } + + stop_and_set_height_to_floor(m); + set_mario_animation(m, MARIO_ANIM_FALL_LAND_WITH_LIGHT_OBJ); + + if (is_anim_at_end(m)) { + return set_mario_action(m, ACT_HOLD_METAL_WATER_STANDING, 0); + } + + return FALSE; +} + +static s32 check_common_submerged_cancels(struct MarioState *m) { + if (m->pos[1] > m->waterLevel - 80) { + if (m->waterLevel - 80 > m->floorHeight) { + m->pos[1] = m->waterLevel - 80; + } else { + //! If you press B to throw the shell, there is a ~5 frame window + // where your held object is the shell, but you are not in the + // water shell swimming action. This allows you to hold the water + // shell on land (used for cloning in DDD). + if (m->action == ACT_WATER_SHELL_SWIMMING && m->heldObj != NULL) { + m->heldObj->oInteractStatus = INT_STATUS_STOP_RIDING; + m->heldObj = NULL; + stop_shell_music(); + } + + return transition_submerged_to_walking(m); + } + } + + if (m->health < 0x100 && !(m->action & (ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE))) { + set_mario_action(m, ACT_DROWNING, 0); + } + + return FALSE; +} + +s32 mario_execute_submerged_action(struct MarioState *m) { + s32 cancel; + + if (check_common_submerged_cancels(m)) { + return TRUE; + } + + m->quicksandDepth = 0.0f; + + m->marioBodyState->headAngle[1] = 0; + m->marioBodyState->headAngle[2] = 0; + + /* clang-format off */ + switch (m->action) { + case ACT_WATER_IDLE: cancel = act_water_idle(m); break; + case ACT_HOLD_WATER_IDLE: cancel = act_hold_water_idle(m); break; + case ACT_WATER_ACTION_END: cancel = act_water_action_end(m); break; + case ACT_HOLD_WATER_ACTION_END: cancel = act_hold_water_action_end(m); break; + case ACT_DROWNING: cancel = act_drowning(m); break; + case ACT_BACKWARD_WATER_KB: cancel = act_backward_water_kb(m); break; + case ACT_FORWARD_WATER_KB: cancel = act_forward_water_kb(m); break; + case ACT_WATER_DEATH: cancel = act_water_death(m); break; + case ACT_WATER_SHOCKED: cancel = act_water_shocked(m); break; + case ACT_BREASTSTROKE: cancel = act_breaststroke(m); break; + case ACT_SWIMMING_END: cancel = act_swimming_end(m); break; + case ACT_FLUTTER_KICK: cancel = act_flutter_kick(m); break; + case ACT_HOLD_BREASTSTROKE: cancel = act_hold_breaststroke(m); break; + case ACT_HOLD_SWIMMING_END: cancel = act_hold_swimming_end(m); break; + case ACT_HOLD_FLUTTER_KICK: cancel = act_hold_flutter_kick(m); break; + case ACT_WATER_SHELL_SWIMMING: cancel = act_water_shell_swimming(m); break; + case ACT_WATER_THROW: cancel = act_water_throw(m); break; + case ACT_WATER_PUNCH: cancel = act_water_punch(m); break; + case ACT_WATER_PLUNGE: cancel = act_water_plunge(m); break; + case ACT_CAUGHT_IN_WHIRLPOOL: cancel = act_caught_in_whirlpool(m); break; + case ACT_METAL_WATER_STANDING: cancel = act_metal_water_standing(m); break; + case ACT_METAL_WATER_WALKING: cancel = act_metal_water_walking(m); break; + case ACT_METAL_WATER_FALLING: cancel = act_metal_water_falling(m); break; + case ACT_METAL_WATER_FALL_LAND: cancel = act_metal_water_fall_land(m); break; + case ACT_METAL_WATER_JUMP: cancel = act_metal_water_jump(m); break; + case ACT_METAL_WATER_JUMP_LAND: cancel = act_metal_water_jump_land(m); break; + case ACT_HOLD_METAL_WATER_STANDING: cancel = act_hold_metal_water_standing(m); break; + case ACT_HOLD_METAL_WATER_WALKING: cancel = act_hold_metal_water_walking(m); break; + case ACT_HOLD_METAL_WATER_FALLING: cancel = act_hold_metal_water_falling(m); break; + case ACT_HOLD_METAL_WATER_FALL_LAND: cancel = act_hold_metal_water_fall_land(m); break; + case ACT_HOLD_METAL_WATER_JUMP: cancel = act_hold_metal_water_jump(m); break; + case ACT_HOLD_METAL_WATER_JUMP_LAND: cancel = act_hold_metal_water_jump_land(m); break; + } + /* clang-format on */ + + return cancel; +} diff --git a/src/game/mario_actions_submerged.h b/src/game/mario_actions_submerged.h new file mode 100644 index 0000000..991bd83 --- /dev/null +++ b/src/game/mario_actions_submerged.h @@ -0,0 +1,10 @@ +#ifndef MARIO_ACTIONS_SUBMERGED_H +#define MARIO_ACTIONS_SUBMERGED_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +s32 mario_execute_submerged_action(struct MarioState *m); + +#endif // MARIO_ACTIONS_SUBMERGED_H diff --git a/src/game/mario_misc.c b/src/game/mario_misc.c new file mode 100644 index 0000000..09441a2 --- /dev/null +++ b/src/game/mario_misc.c @@ -0,0 +1,656 @@ +#include "../include/PR/ultratypes.h" + +#include "../include/sm64.h" +#include "area.h" +//#include "audio/external.h" +//#include "behavior_actions.h" +//#include "behavior_data.h" +#include "camera.h" +//#include "dialog_ids.h" +//#include "engine/behavior_script.h" +#include "../engine/graph_node.h" +#include "../engine/math_util.h" +//#include "envfx_snow.h" +//#include "game_init.h" +//#include "goddard/renderer.h" +#include "interaction.h" +#include "level_update.h" +#include "mario_misc.h" +#include "../memory.h" +//#include "object_helpers.h" +//#include "object_list_processor.h" +#include "rendering_graph_node.h" +#include "save_file.h" +//#include "skybox.h" +//#include "sound_init.h" +#include "../shim.h" +#include "../include/mario_animation_ids.h" +#include "../include/object_fields.h" +#include "../include/mario_geo_switch_case_ids.h" + + +static Vec3f gVec3fZero = {0,0,0}; +static Vec3s gVec3sZero = {0,0,0}; +static Vec3f gVec3fOne = {1,1,1}; + +#define TOAD_STAR_1_REQUIREMENT 12 +#define TOAD_STAR_2_REQUIREMENT 25 +#define TOAD_STAR_3_REQUIREMENT 35 + +#define TOAD_STAR_1_DIALOG 90 // DIALOG_082 +#define TOAD_STAR_2_DIALOG 91 // DIALOG_076 +#define TOAD_STAR_3_DIALOG 92 // DIALOG_083 + +#define TOAD_STAR_1_DIALOG_AFTER 90 // DIALOG_154 +#define TOAD_STAR_2_DIALOG_AFTER 91 // DIALOG_155 +#define TOAD_STAR_3_DIALOG_AFTER 92 // DIALOG_156 + +enum ToadMessageStates { + TOAD_MESSAGE_FADED, + TOAD_MESSAGE_OPAQUE, + TOAD_MESSAGE_OPACIFYING, + TOAD_MESSAGE_FADING, + TOAD_MESSAGE_TALKING +}; + +enum UnlockDoorStarStates { + UNLOCK_DOOR_STAR_RISING, + UNLOCK_DOOR_STAR_WAITING, + UNLOCK_DOOR_STAR_SPAWNING_PARTICLES, + UNLOCK_DOOR_STAR_DONE +}; + +/** + * The eye texture on succesive frames of Mario's blink animation. + * He intentionally blinks twice each time. + */ +static s8 gMarioBlinkAnimation[7] = { 1, 2, 1, 0, 1, 2, 1 }; + +/** + * The scale values per frame for Mario's foot/hand for his attack animation + * There are 3 scale animations in groups of 6 frames. + * The first animation starts at frame index 3 and goes down, the others start at frame index 5. + * The values get divided by 10 before assigning, so e.g. 12 gives a scale factor 1.2. + * All combined, this means e.g. the first animation scales Mario's fist by {2.4, 1.6, 1.2, 1.0} on + * successive frames. + */ +static s8 gMarioAttackScaleAnimation[3 * 6] = { + 10, 12, 16, 24, 10, 10, 10, 14, 20, 30, 10, 10, 10, 16, 20, 26, 26, 20, +}; + +struct MarioBodyState gBodyStates[2]; // 2nd is never accessed in practice, most likely Luigi related +struct GraphNodeObject gMirrorMario; // copy of Mario's geo node for drawing mirror Mario + +// This whole file is weirdly organized. It has to be the same file due +// to rodata boundaries and function aligns, which means the programmer +// treated this like a "misc" file for vaguely Mario related things +// (message NPC related things, the Mario head geo, and Mario geo +// functions) + +/** + * Geo node script that draws Mario's head on the title screen. + */ +// Gfx *geo_draw_mario_head_goddard(s32 callContext, struct GraphNode *node, Mat4 *c) { +// Gfx *gfx = NULL; +// s16 sfx = 0; +// struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; +// UNUSED Mat4 *transform = c; +// +// if (callContext == GEO_CONTEXT_RENDER) { +// if (gPlayerController->controllerData != NULL && !gWarpTransition.isActive) { +// gd_copy_p1_contpad(gPlayer1Controller->controllerData); +// } +// gfx = (Gfx *) PHYSICAL_TO_VIRTUAL(gdm_gettestdl(asGenerated->parameter)); +// D_8032C6A0 = gd_vblank; +// sfx = gd_sfx_to_play(); +// play_menu_sounds(sfx); +// } +// return gfx; +// } + +static void toad_message_faded(void) { +// if (gCurrentObject->oDistanceToMario > 700.0f) { +// gCurrentObject->oToadMessageRecentlyTalked = FALSE; +// } +// if (!gCurrentObject->oToadMessageRecentlyTalked && gCurrentObject->oDistanceToMario < 600.0f) { +// gCurrentObject->oToadMessageState = TOAD_MESSAGE_OPACIFYING; +// } +} + +static void toad_message_opaque(void) { +// if (gCurrentObject->oDistanceToMario > 700.0f) { +// gCurrentObject->oToadMessageState = TOAD_MESSAGE_FADING; +// } else if (!gCurrentObject->oToadMessageRecentlyTalked) { +// gCurrentObject->oInteractionSubtype = INT_SUBTYPE_NPC; +// if (gCurrentObject->oInteractStatus & INT_STATUS_INTERACTED) { +// gCurrentObject->oInteractStatus = 0; +// gCurrentObject->oToadMessageState = TOAD_MESSAGE_TALKING; +// play_toads_jingle(); +// } +// } +} + +static void toad_message_talking(void) { +// if (cur_obj_update_dialog_with_cutscene(3, 1, CUTSCENE_DIALOG, gCurrentObject->oToadMessageDialogId) +// != 0) { +// gCurrentObject->oToadMessageRecentlyTalked = TRUE; +// gCurrentObject->oToadMessageState = TOAD_MESSAGE_FADING; +// switch (gCurrentObject->oToadMessageDialogId) { +// case TOAD_STAR_1_DIALOG: +// gCurrentObject->oToadMessageDialogId = TOAD_STAR_1_DIALOG_AFTER; +// bhv_spawn_star_no_level_exit(0); +// break; +// case TOAD_STAR_2_DIALOG: +// gCurrentObject->oToadMessageDialogId = TOAD_STAR_2_DIALOG_AFTER; +// bhv_spawn_star_no_level_exit(1); +// break; +// case TOAD_STAR_3_DIALOG: +// gCurrentObject->oToadMessageDialogId = TOAD_STAR_3_DIALOG_AFTER; +// bhv_spawn_star_no_level_exit(2); +// break; +// } +// } +} + +static void toad_message_opacifying(void) { + if ((gCurrentObject->oOpacity += 6) == 255) { + gCurrentObject->oToadMessageState = TOAD_MESSAGE_OPAQUE; + } +} + +static void toad_message_fading(void) { + if ((gCurrentObject->oOpacity -= 6) == 81) { + gCurrentObject->oToadMessageState = TOAD_MESSAGE_FADED; + } +} + +void bhv_toad_message_loop(void) { + if (gCurrentObject->header.gfx.node.flags & GRAPH_RENDER_ACTIVE) { + gCurrentObject->oInteractionSubtype = 0; + switch (gCurrentObject->oToadMessageState) { + case TOAD_MESSAGE_FADED: + toad_message_faded(); + break; + case TOAD_MESSAGE_OPAQUE: + toad_message_opaque(); + break; + case TOAD_MESSAGE_OPACIFYING: + toad_message_opacifying(); + break; + case TOAD_MESSAGE_FADING: + toad_message_fading(); + break; + case TOAD_MESSAGE_TALKING: + toad_message_talking(); + break; + } + } +} + +void bhv_toad_message_init(void) { +// s32 saveFlags = save_file_get_flags(); +// s32 starCount = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1); +// s32 dialogId = (gCurrentObject->oBehParams >> 24) & 0xFF; +// s32 enoughStars = TRUE; +// +// switch (dialogId) { +// case TOAD_STAR_1_DIALOG: +// enoughStars = (starCount >= TOAD_STAR_1_REQUIREMENT); +// if (saveFlags & SAVE_FLAG_COLLECTED_TOAD_STAR_1) { +// dialogId = TOAD_STAR_1_DIALOG_AFTER; +// } +// break; +// case TOAD_STAR_2_DIALOG: +// enoughStars = (starCount >= TOAD_STAR_2_REQUIREMENT); +// if (saveFlags & SAVE_FLAG_COLLECTED_TOAD_STAR_2) { +// dialogId = TOAD_STAR_2_DIALOG_AFTER; +// } +// break; +// case TOAD_STAR_3_DIALOG: +// enoughStars = (starCount >= TOAD_STAR_3_REQUIREMENT); +// if (saveFlags & SAVE_FLAG_COLLECTED_TOAD_STAR_3) { +// dialogId = TOAD_STAR_3_DIALOG_AFTER; +// } +// break; +// } +// if (enoughStars) { +// gCurrentObject->oToadMessageDialogId = dialogId; +// gCurrentObject->oToadMessageRecentlyTalked = FALSE; +// gCurrentObject->oToadMessageState = TOAD_MESSAGE_FADED; +// gCurrentObject->oOpacity = 81; +// } else { +// obj_mark_for_deletion(gCurrentObject); +// } +} + +static void star_door_unlock_spawn_particles(s16 angleOffset) { +// struct Object *sparkleParticle = spawn_object(gCurrentObject, 0, bhvSparkleSpawn); +// +// sparkleParticle->oPosX += +// 100.0f * sins((gCurrentObject->oUnlockDoorStarTimer * 0x2800) + angleOffset); +// sparkleParticle->oPosZ += +// 100.0f * coss((gCurrentObject->oUnlockDoorStarTimer * 0x2800) + angleOffset); +// // Particles are spawned lower each frame +// sparkleParticle->oPosY -= gCurrentObject->oUnlockDoorStarTimer * 10.0f; +} + +void bhv_unlock_door_star_init(void) { +// gCurrentObject->oUnlockDoorStarState = UNLOCK_DOOR_STAR_RISING; +// gCurrentObject->oUnlockDoorStarTimer = 0; +// gCurrentObject->oUnlockDoorStarYawVel = 0x1000; +// gCurrentObject->oPosX += 30.0f * sins(gMarioState->faceAngle[1] - 0x4000); +// gCurrentObject->oPosY += 160.0f; +// gCurrentObject->oPosZ += 30.0f * coss(gMarioState->faceAngle[1] - 0x4000); +// gCurrentObject->oMoveAngleYaw = 0x7800; +// obj_scale(gCurrentObject, 0.5f); +} + +void bhv_unlock_door_star_loop(void) { +// UNUSED u8 unused1[4]; +// s16 prevYaw = gCurrentObject->oMoveAngleYaw; +// UNUSED u8 unused2[4]; +// +// // Speed up the star every frame +// if (gCurrentObject->oUnlockDoorStarYawVel < 0x2400) { +// gCurrentObject->oUnlockDoorStarYawVel += 0x60; +// } +// switch (gCurrentObject->oUnlockDoorStarState) { +// case UNLOCK_DOOR_STAR_RISING: +// gCurrentObject->oPosY += 3.4f; // Raise the star up in the air +// gCurrentObject->oMoveAngleYaw += +// gCurrentObject->oUnlockDoorStarYawVel; // Apply yaw velocity +// obj_scale(gCurrentObject, gCurrentObject->oUnlockDoorStarTimer / 50.0f +// + 0.5f); // Scale the star to be bigger +// if (++gCurrentObject->oUnlockDoorStarTimer == 30) { +// gCurrentObject->oUnlockDoorStarTimer = 0; +// gCurrentObject->oUnlockDoorStarState++; // Sets state to UNLOCK_DOOR_STAR_WAITING +// } +// break; +// case UNLOCK_DOOR_STAR_WAITING: +// gCurrentObject->oMoveAngleYaw += +// gCurrentObject->oUnlockDoorStarYawVel; // Apply yaw velocity +// if (++gCurrentObject->oUnlockDoorStarTimer == 30) { +// play_sound(SOUND_MENU_STAR_SOUND, +// gCurrentObject->header.gfx.cameraToObject); // Play final sound +// cur_obj_hide(); // Hide the object +// gCurrentObject->oUnlockDoorStarTimer = 0; +// gCurrentObject +// ->oUnlockDoorStarState++; // Sets state to UNLOCK_DOOR_STAR_SPAWNING_PARTICLES +// } +// break; +// case UNLOCK_DOOR_STAR_SPAWNING_PARTICLES: +// // Spawn two particles, opposite sides of the star. +// star_door_unlock_spawn_particles(0); +// star_door_unlock_spawn_particles(0x8000); +// if (gCurrentObject->oUnlockDoorStarTimer++ == 20) { +// gCurrentObject->oUnlockDoorStarTimer = 0; +// gCurrentObject->oUnlockDoorStarState++; // Sets state to UNLOCK_DOOR_STAR_DONE +// } +// break; +// case UNLOCK_DOOR_STAR_DONE: // The object stays loaded for an additional 50 frames so that the +// // sound doesn't immediately stop. +// if (gCurrentObject->oUnlockDoorStarTimer++ == 50) { +// obj_mark_for_deletion(gCurrentObject); +// } +// break; +// } +// // Checks if the angle has cycled back to 0. +// // This means that the code will execute when the star completes a full revolution. +// if (prevYaw > (s16) gCurrentObject->oMoveAngleYaw) { +// play_sound( +// SOUND_GENERAL_SHORT_STAR, +// gCurrentObject->header.gfx.cameraToObject); // Play a sound every time the star spins once +// } +} + +/** + * Generate a display list that sets the correct blend mode and color for mirror Mario. + */ +static Gfx *make_gfx_mario_alpha(struct GraphNodeGenerated *node, s16 alpha) { + Gfx *gfx; + Gfx *gfxHead = NULL; + + if (alpha == 255) { + node->fnNode.node.flags = (node->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8); + gfxHead = alloc_display_list(2 * sizeof(*gfxHead)); + gfx = gfxHead; + } else { + node->fnNode.node.flags = (node->fnNode.node.flags & 0xFF) | (LAYER_TRANSPARENT << 8); + gfxHead = alloc_display_list(3 * sizeof(*gfxHead)); + gfx = gfxHead; + gDPSetAlphaCompare(gfx++, G_AC_DITHER); + } + gDPSetEnvColor(gfx++, 255, 255, 255, alpha); + gSPEndDisplayList(gfx); + return gfxHead; +} + +/** + * Sets the correct blend mode and color for mirror Mario. + */ +Gfx *geo_mirror_mario_set_alpha(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + UNUSED u8 unused1[4]; + Gfx *gfx = NULL; + struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; + struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter]; + s16 alpha; + UNUSED u8 unused2[4]; + + if (callContext == GEO_CONTEXT_RENDER) { + alpha = (bodyState->modelState & 0x100) ? (bodyState->modelState & 0xFF) : 255; + gfx = make_gfx_mario_alpha(asGenerated, alpha); + } + return gfx; +} + +/** + * Determines if Mario is standing or running for the level of detail of his model. + * If Mario is standing still, he is always high poly. If he is running, + * his level of detail depends on the distance to the camera. + */ +Gfx *geo_switch_mario_stand_run(s32 callContext, struct GraphNode *node, UNUSED Mat4 *mtx) { + struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; + struct MarioBodyState *bodyState = &gBodyStates[switchCase->numCases]; + + if (callContext == GEO_CONTEXT_RENDER) { + // assign result. 0 if moving, 1 if stationary. + switchCase->selectedCase = ((bodyState->action & ACT_FLAG_STATIONARY) == 0); + } + return NULL; +} + +/** + * Geo node script that makes Mario blink + */ +Gfx *geo_switch_mario_eyes(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; + struct MarioBodyState *bodyState = &gBodyStates[switchCase->numCases]; + s16 blinkFrame; + + if (callContext == GEO_CONTEXT_RENDER) { + if (bodyState->eyeState == 0) { + blinkFrame = ((switchCase->numCases * 32 + gAreaUpdateCounter) >> 1) & 0x1F; + if (blinkFrame < 7) { + switchCase->selectedCase = gMarioBlinkAnimation[blinkFrame]; + } else { + switchCase->selectedCase = 0; + } + } else { + switchCase->selectedCase = bodyState->eyeState - 1; + } + } + return NULL; +} + +/** + * Makes Mario's upper body tilt depending on the rotation stored in his bodyState + */ +Gfx *geo_mario_tilt_torso(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; + struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter]; + s32 action = bodyState->action; + + if (callContext == GEO_CONTEXT_RENDER) { + struct GraphNodeRotation *rotNode = (struct GraphNodeRotation *) node->next; + + if (action != ACT_BUTT_SLIDE && action != ACT_HOLD_BUTT_SLIDE && action != ACT_WALKING + && action != ACT_RIDING_SHELL_GROUND) { + vec3s_copy(bodyState->torsoAngle, gVec3sZero); + } + rotNode->rotation[0] = bodyState->torsoAngle[1]; + rotNode->rotation[1] = bodyState->torsoAngle[2]; + rotNode->rotation[2] = bodyState->torsoAngle[0]; + } + return NULL; +} + +/** + * Makes Mario's head rotate with the camera angle when in C-up mode + */ +Gfx *geo_mario_head_rotation(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; + struct MarioBodyState *bodyState = &gBodyStates[asGenerated->parameter]; + s32 action = bodyState->action; + + if (callContext == GEO_CONTEXT_RENDER) { + struct GraphNodeRotation *rotNode = (struct GraphNodeRotation *) node->next; + // struct Camera *camera = gCurGraphNodeCamera->config.camera; + + // if (camera->mode == CAMERA_MODE_C_UP) { + // rotNode->rotation[0] = 0; // gPlayerCameraState->headRotation[1]; // PATCH + // rotNode->rotation[2] = 0; // gPlayerCameraState->headRotation[0]; + // } else if (action & ACT_FLAG_WATER_OR_TEXT) { + // rotNode->rotation[0] = bodyState->headAngle[1]; + // rotNode->rotation[1] = bodyState->headAngle[2]; + // rotNode->rotation[2] = bodyState->headAngle[0]; + // } else { + vec3s_set(bodyState->headAngle, 0, 0, 0); + vec3s_set(rotNode->rotation, 0, 0, 0); + // } + } + return NULL; +} + +/** + * Switch between hand models. + * Possible options are described in the MarioHandGSCId enum. + */ +Gfx *geo_switch_mario_hand(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; + struct MarioBodyState *bodyState = &gBodyStates[0]; + + if (callContext == GEO_CONTEXT_RENDER) { + if (bodyState->handState == MARIO_HAND_FISTS) { + // switch between fists (0) and open (1) + switchCase->selectedCase = ((bodyState->action & ACT_FLAG_SWIMMING_OR_FLYING) != 0); + } else { + if (switchCase->numCases == 0) { + switchCase->selectedCase = + (bodyState->handState < 5) ? bodyState->handState : MARIO_HAND_OPEN; + } else { + switchCase->selectedCase = + (bodyState->handState < 2) ? bodyState->handState : MARIO_HAND_FISTS; + } + } + } + return NULL; +} + +/** + * Increase Mario's hand / foot size when he punches / kicks. + * Since animation geo nodes only support rotation, this scaling animation + * was scripted separately. The node with this script should be placed before + * a scaling node containing the hand / foot geo layout. + * ! Since the animation gets updated in GEO_CONTEXT_RENDER, drawing Mario multiple times + * (such as in the mirror room) results in a faster and desynced punch / kick animation. + */ +Gfx *geo_mario_hand_foot_scaler(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + static s16 sMarioAttackAnimCounter = 0; + struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; + struct GraphNodeScale *scaleNode = (struct GraphNodeScale *) node->next; + struct MarioBodyState *bodyState = &gBodyStates[0]; + + if (callContext == GEO_CONTEXT_RENDER) { + scaleNode->scale = 1.0f; + if (asGenerated->parameter == bodyState->punchState >> 6) { + if (sMarioAttackAnimCounter != gAreaUpdateCounter && (bodyState->punchState & 0x3F) > 0) { + bodyState->punchState -= 1; + sMarioAttackAnimCounter = gAreaUpdateCounter; + } + scaleNode->scale = + gMarioAttackScaleAnimation[asGenerated->parameter * 6 + (bodyState->punchState & 0x3F)] + / 10.0f; + } + } + return NULL; +} + +/** + * Switch between normal cap, wing cap, vanish cap and metal cap. + */ +Gfx *geo_switch_mario_cap_effect(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; + struct MarioBodyState *bodyState = &gBodyStates[switchCase->numCases]; + + if (callContext == GEO_CONTEXT_RENDER) { + switchCase->selectedCase = bodyState->modelState >> 8; + } + return NULL; +} + +/** + * Determine whether Mario's head is drawn with or without a cap on. + * Also sets the visibility of the wing cap wings on or off. + */ +Gfx *geo_switch_mario_cap_on_off(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + struct GraphNode *next = node->next; + struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node; + struct MarioBodyState *bodyState = &gBodyStates[switchCase->numCases]; + + if (callContext == GEO_CONTEXT_RENDER) { + switchCase->selectedCase = bodyState->capState & 1; + while (next != node) { + if (next->type == GRAPH_NODE_TYPE_TRANSLATION_ROTATION) { + if (bodyState->capState & 2) { + next->flags |= GRAPH_RENDER_ACTIVE; + } else { + next->flags &= ~GRAPH_RENDER_ACTIVE; + } + } + next = next->next; + } + } + return NULL; +} + +/** + * Geo node script that makes the wings on Mario's wing cap flap. + * Should be placed before a rotation node. + */ +Gfx *geo_mario_rotate_wing_cap_wings(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + s16 rotX; + struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; + + if (callContext == GEO_CONTEXT_RENDER) { + struct GraphNodeRotation *rotNode = (struct GraphNodeRotation *) node->next; + + if (!gBodyStates[asGenerated->parameter >> 1].wingFlutter) { + rotX = (coss((gAreaUpdateCounter & 0xF) << 12) + 1.0f) * 4096.0f; + } else { + rotX = (coss((gAreaUpdateCounter & 7) << 13) + 1.0f) * 6144.0f; + } + if (!(asGenerated->parameter & 1)) { + rotNode->rotation[0] = -rotX; + } else { + rotNode->rotation[0] = rotX; + } + } + return NULL; +} + +/** + * Geo node that updates the held object node and the HOLP. + */ +Gfx *geo_switch_mario_hand_grab_pos(s32 callContext, struct GraphNode *b, Mat4 *mtx) { + struct GraphNodeHeldObject *asHeldObj = (struct GraphNodeHeldObject *) b; + Mat4 *curTransform = mtx; + struct MarioState *marioState = gMarioState; // &gMarioStates[asHeldObj->playerIndex]; // PATCH + + if (callContext == GEO_CONTEXT_RENDER) { + asHeldObj->objNode = NULL; + if (marioState->heldObj != NULL) { + asHeldObj->objNode = marioState->heldObj; + switch (marioState->marioBodyState->grabPos) { + case GRAB_POS_LIGHT_OBJ: + if (marioState->action & ACT_FLAG_THROWING) { + vec3s_set(asHeldObj->translation, 50, 0, 0); + } else { + vec3s_set(asHeldObj->translation, 50, 0, 110); + } + break; + case GRAB_POS_HEAVY_OBJ: + vec3s_set(asHeldObj->translation, 145, -173, 180); + break; + case GRAB_POS_BOWSER: + vec3s_set(asHeldObj->translation, 80, -270, 1260); + break; + } + } + } else if (callContext == GEO_CONTEXT_HELD_OBJ) { + // ! The HOLP is set here, which is why it only updates when the held object is drawn. + // This is why it won't update during a pause buffered hitstun or when the camera is very far + // away. + get_pos_from_transform_mtx(marioState->marioBodyState->heldObjLastPosition, *curTransform, + *gCurGraphNodeCamera->matrixPtr); + } + return NULL; +} + +// X position of the mirror +#define MIRROR_X 4331.53 + +/** + * Geo node that creates a clone of Mario's geo node and updates it to becomes + * a mirror image of the player. + */ +Gfx *geo_render_mirror_mario(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { + f32 mirroredX; + struct Object *mario = gMarioState->marioObj; // PATCH gMarioStates[0].marioObj; + + switch (callContext) { + case GEO_CONTEXT_CREATE: + init_graph_node_object(NULL, &gMirrorMario, NULL, gVec3fZero, gVec3sZero, gVec3fOne); + break; + case GEO_CONTEXT_AREA_LOAD: + geo_add_child(node, &gMirrorMario.node); + break; + case GEO_CONTEXT_AREA_UNLOAD: + geo_remove_child(&gMirrorMario.node); + break; + case GEO_CONTEXT_RENDER: + if (mario->header.gfx.pos[0] > 1700.0f) { + // TODO: Is this a geo layout copy or a graph node copy? + gMirrorMario.sharedChild = mario->header.gfx.sharedChild; + gMirrorMario.areaIndex = mario->header.gfx.areaIndex; + vec3s_copy(gMirrorMario.angle, mario->header.gfx.angle); + vec3f_copy(gMirrorMario.pos, mario->header.gfx.pos); + vec3f_copy(gMirrorMario.scale, mario->header.gfx.scale); + + gMirrorMario.animInfo = mario->header.gfx.animInfo; + mirroredX = MIRROR_X - gMirrorMario.pos[0]; + gMirrorMario.pos[0] = mirroredX + MIRROR_X; + gMirrorMario.angle[1] = -gMirrorMario.angle[1]; + gMirrorMario.scale[0] *= -1.0f; + ((struct GraphNode *) &gMirrorMario)->flags |= 1; + } else { + ((struct GraphNode *) &gMirrorMario)->flags &= ~1; + } + break; + } + return NULL; +} + +/** + * Since Mirror Mario has an x scale of -1, the mesh becomes inside out. + * This node corrects that by changing the culling mode accordingly. + */ +Gfx *geo_mirror_mario_backface_culling(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c) { +// struct GraphNodeGenerated *asGenerated = (struct GraphNodeGenerated *) node; +// Gfx *gfx = NULL; + +// if (callContext == GEO_CONTEXT_RENDER && gCurGraphNodeObject == &gMirrorMario) { +// gfx = alloc_display_list(3 * sizeof(*gfx)); + +// if (asGenerated->parameter == 0) { +// gSPClearGeometryMode(&gfx[0], G_CULL_BACK); +// gSPSetGeometryMode(&gfx[1], G_CULL_FRONT); +// gSPEndDisplayList(&gfx[2]); +// } else { +// gSPClearGeometryMode(&gfx[0], G_CULL_FRONT); +// gSPSetGeometryMode(&gfx[1], G_CULL_BACK); +// gSPEndDisplayList(&gfx[2]); +// } +// asGenerated->fnNode.node.flags = (asGenerated->fnNode.node.flags & 0xFF) | (LAYER_OPAQUE << 8); +// } +// return gfx; +} diff --git a/src/game/mario_misc.h b/src/game/mario_misc.h new file mode 100644 index 0000000..dafac13 --- /dev/null +++ b/src/game/mario_misc.h @@ -0,0 +1,31 @@ +#ifndef MARIO_MISC_H +#define MARIO_MISC_H + +#include "../include/PR/ultratypes.h" + +#include "../include/macros.h" +#include "../include/types.h" + +extern struct GraphNodeObject gMirrorMario; +extern struct MarioBodyState gBodyStates[2]; + +// Gfx *geo_draw_mario_head_goddard(s32 callContext, struct GraphNode *node, Mat4 *c); +void bhv_toad_message_loop(void); +void bhv_toad_message_init(void); +void bhv_unlock_door_star_init(void); +void bhv_unlock_door_star_loop(void); +Gfx *geo_mirror_mario_set_alpha(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_switch_mario_stand_run(s32 callContext, struct GraphNode *node, UNUSED Mat4 *mtx); +Gfx *geo_switch_mario_eyes(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_mario_tilt_torso(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_mario_head_rotation(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_switch_mario_hand(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_mario_hand_foot_scaler(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_switch_mario_cap_effect(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_switch_mario_cap_on_off(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_mario_rotate_wing_cap_wings(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_switch_mario_hand_grab_pos(s32 callContext, struct GraphNode *b, Mat4 *mtx); +Gfx *geo_render_mirror_mario(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); +Gfx *geo_mirror_mario_backface_culling(s32 callContext, struct GraphNode *node, UNUSED Mat4 *c); + +#endif // MARIO_MISC_H diff --git a/src/game/mario_step.c b/src/game/mario_step.c new file mode 100644 index 0000000..d3e2d8a --- /dev/null +++ b/src/game/mario_step.c @@ -0,0 +1,668 @@ +#include "../shim.h" +#include "../include/sm64.h" +#include "../engine/math_util.h" +#include "../engine/surface_collision.h" +#include "mario.h" +//#include "audio/external.h" +//#include "game_init.h" +#include "interaction.h" +#include "mario_step.h" + +static s16 sMovingSandSpeeds[] = { 12, 8, 4, 0 }; + +struct Surface gWaterSurfacePseudoFloor = { + SURFACE_VERY_SLIPPERY, 0, 0, 0, 0, 0, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, + { 0.0f, 1.0f, 0.0f }, 0.0f, NULL, +}; + +/** + * Always returns zero. This may have been intended + * to be used for the beta trampoline. Its return value + * is used by set_mario_y_vel_based_on_fspeed as a constant + * addition to Mario's Y velocity. Given the closeness of + * this function to stub_mario_step_2, it is probable that this + * was intended to check whether a trampoline had made itself + * known through stub_mario_step_2 and whether Mario was on it, + * and if so return a higher value than 0. + */ +f32 get_additive_y_vel_for_jumps(void) { + return 0.0f; +} + +/** + * Does nothing, but takes in a MarioState. This is only ever + * called by update_mario_inputs, which is called as part of Mario's + * update routine. Due to its proximity to stub_mario_step_2, an + * incomplete trampoline function, and get_additive_y_vel_for_jumps, + * a potentially trampoline-related function, it is plausible that + * this could be used for checking if Mario was on the trampoline. + * It could, for example, make him bounce. + */ +void stub_mario_step_1(UNUSED struct MarioState *x) { +} + +/** + * Does nothing. This is only called by the beta trampoline. + * Due to its proximity to get_additive_y_vel_for_jumps, another + * currently-pointless function, it is probable that this was used + * by the trampoline to make itself known to get_additive_y_vel_for_jumps, + * or to set a variable with its intended additive Y vel. + */ +void stub_mario_step_2(void) { +} + +void transfer_bully_speed(struct BullyCollisionData *obj1, struct BullyCollisionData *obj2) { + f32 rx = obj2->posX - obj1->posX; + f32 rz = obj2->posZ - obj1->posZ; + + //! Bully NaN crash + f32 projectedV1 = (rx * obj1->velX + rz * obj1->velZ) / (rx * rx + rz * rz); + f32 projectedV2 = (-rx * obj2->velX - rz * obj2->velZ) / (rx * rx + rz * rz); + + // Kill speed along r. Convert one object's speed along r and transfer it to + // the other object. + obj2->velX += obj2->conversionRatio * projectedV1 * rx - projectedV2 * -rx; + obj2->velZ += obj2->conversionRatio * projectedV1 * rz - projectedV2 * -rz; + + obj1->velX += -projectedV1 * rx + obj1->conversionRatio * projectedV2 * -rx; + obj1->velZ += -projectedV1 * rz + obj1->conversionRatio * projectedV2 * -rz; + + //! Bully battery +} + +BAD_RETURN(s32) init_bully_collision_data(struct BullyCollisionData *data, f32 posX, f32 posZ, + f32 forwardVel, s16 yaw, f32 conversionRatio, f32 radius) { + if (forwardVel < 0.0f) { + forwardVel *= -1.0f; + yaw += 0x8000; + } + + data->radius = radius; + data->conversionRatio = conversionRatio; + data->posX = posX; + data->posZ = posZ; + data->velX = forwardVel * sins(yaw); + data->velZ = forwardVel * coss(yaw); +} + +void mario_bonk_reflection(struct MarioState *m, u32 negateSpeed) { + if (m->wall != NULL) { + s16 wallAngle = atan2s(m->wall->normal.z, m->wall->normal.x); + m->faceAngle[1] = wallAngle - (s16)(m->faceAngle[1] - wallAngle); + + play_sound((m->flags & MARIO_METAL_CAP) ? SOUND_ACTION_METAL_BONK : SOUND_ACTION_BONK, + m->marioObj->header.gfx.cameraToObject); + } else { + play_sound(SOUND_ACTION_HIT, m->marioObj->header.gfx.cameraToObject); + } + + if (negateSpeed) { + mario_set_forward_vel(m, -m->forwardVel); + } else { + m->faceAngle[1] += 0x8000; + } +} + +u32 mario_update_quicksand(struct MarioState *m, f32 sinkingSpeed) { + if (m->action & ACT_FLAG_RIDING_SHELL) { + m->quicksandDepth = 0.0f; + } else { + if (m->quicksandDepth < 1.1f) { + m->quicksandDepth = 1.1f; + } + + switch (m->floor->type) { + case SURFACE_SHALLOW_QUICKSAND: + if ((m->quicksandDepth += sinkingSpeed) >= 10.0f) { + m->quicksandDepth = 10.0f; + } + break; + + case SURFACE_SHALLOW_MOVING_QUICKSAND: + if ((m->quicksandDepth += sinkingSpeed) >= 25.0f) { + m->quicksandDepth = 25.0f; + } + break; + + case SURFACE_QUICKSAND: + case SURFACE_MOVING_QUICKSAND: + if ((m->quicksandDepth += sinkingSpeed) >= 60.0f) { + m->quicksandDepth = 60.0f; + } + break; + + case SURFACE_DEEP_QUICKSAND: + case SURFACE_DEEP_MOVING_QUICKSAND: + if ((m->quicksandDepth += sinkingSpeed) >= 160.0f) { + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0); + } + break; + + case SURFACE_INSTANT_QUICKSAND: + case SURFACE_INSTANT_MOVING_QUICKSAND: + update_mario_sound_and_camera(m); + return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0); + break; + + default: + m->quicksandDepth = 0.0f; + break; + } + } + + return FALSE; +} + +u32 mario_push_off_steep_floor(struct MarioState *m, u32 action, u32 actionArg) { + s16 floorDYaw = m->floorAngle - m->faceAngle[1]; + + if (floorDYaw > -0x4000 && floorDYaw < 0x4000) { + m->forwardVel = 16.0f; + m->faceAngle[1] = m->floorAngle; + } else { + m->forwardVel = -16.0f; + m->faceAngle[1] = m->floorAngle + 0x8000; + } + + return set_mario_action(m, action, actionArg); +} + +u32 mario_update_moving_sand(struct MarioState *m) { + struct Surface *floor = m->floor; + s32 floorType = floor->type; + + if (floorType == SURFACE_DEEP_MOVING_QUICKSAND || floorType == SURFACE_SHALLOW_MOVING_QUICKSAND + || floorType == SURFACE_MOVING_QUICKSAND || floorType == SURFACE_INSTANT_MOVING_QUICKSAND) { + s16 pushAngle = floor->force << 8; + f32 pushSpeed = sMovingSandSpeeds[floor->force >> 8]; + + m->vel[0] += pushSpeed * sins(pushAngle); + m->vel[2] += pushSpeed * coss(pushAngle); + + return TRUE; + } + + return FALSE; +} + +u32 mario_update_windy_ground(struct MarioState *m) { + struct Surface *floor = m->floor; + + if (floor->type == SURFACE_HORIZONTAL_WIND) { + f32 pushSpeed; + s16 pushAngle = floor->force << 8; + + if (m->action & ACT_FLAG_MOVING) { + s16 pushDYaw = m->faceAngle[1] - pushAngle; + + pushSpeed = m->forwardVel > 0.0f ? -m->forwardVel * 0.5f : -8.0f; + + if (pushDYaw > -0x4000 && pushDYaw < 0x4000) { + pushSpeed *= -1.0f; + } + + pushSpeed *= coss(pushDYaw); + } else { + pushSpeed = 3.2f + (gGlobalTimer % 4); + } + + m->vel[0] += pushSpeed * sins(pushAngle); + m->vel[2] += pushSpeed * coss(pushAngle); + +#if VERSION_JP + play_sound(SOUND_ENV_WIND2, m->marioObj->header.gfx.cameraToObject); +#endif + return TRUE; + } + + return FALSE; +} + +void stop_and_set_height_to_floor(struct MarioState *m) { + struct Object *marioObj = m->marioObj; + + mario_set_forward_vel(m, 0.0f); + m->vel[1] = 0.0f; + + //! This is responsible for some downwarps. + m->pos[1] = m->floorHeight; + + vec3f_copy(marioObj->header.gfx.pos, m->pos); + vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); +} + +s32 stationary_ground_step(struct MarioState *m) { + u32 takeStep; + struct Object *marioObj = m->marioObj; + u32 stepResult = GROUND_STEP_NONE; + + mario_set_forward_vel(m, 0.0f); + + takeStep = mario_update_moving_sand(m); + takeStep |= mario_update_windy_ground(m); + if (takeStep) { + stepResult = perform_ground_step(m); + } else { + //! This is responsible for several stationary downwarps. + m->pos[1] = m->floorHeight; + + vec3f_copy(marioObj->header.gfx.pos, m->pos); + vec3s_set(marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + } + + return stepResult; +} + +static s32 perform_ground_quarter_step(struct MarioState *m, Vec3f nextPos) { + UNUSED struct Surface *lowerWall; + struct Surface *upperWall; + struct Surface *ceil; + struct Surface *floor; + f32 ceilHeight; + f32 floorHeight; + f32 waterLevel; + + lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 24.0f); + upperWall = resolve_and_return_wall_collisions(nextPos, 60.0f, 50.0f); + + floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); + ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + + waterLevel = find_water_level(nextPos[0], nextPos[2]); + + m->wall = upperWall; + + if (floor == NULL) { + return GROUND_STEP_HIT_WALL_STOP_QSTEPS; + } + + if ((m->action & ACT_FLAG_RIDING_SHELL) && floorHeight < waterLevel) { + floorHeight = waterLevel; + floor = &gWaterSurfacePseudoFloor; + floor->originOffset = floorHeight; //! Wrong origin offset (no effect) + } + + if (nextPos[1] > floorHeight + 100.0f) { + if (nextPos[1] + 160.0f >= ceilHeight) { + return GROUND_STEP_HIT_WALL_STOP_QSTEPS; + } + + vec3f_copy(m->pos, nextPos); + m->floor = floor; + m->floorHeight = floorHeight; + return GROUND_STEP_LEFT_GROUND; + } + + if (floorHeight + 160.0f >= ceilHeight) { + return GROUND_STEP_HIT_WALL_STOP_QSTEPS; + } + + vec3f_set(m->pos, nextPos[0], floorHeight, nextPos[2]); + m->floor = floor; + m->floorHeight = floorHeight; + + if (upperWall != NULL) { + s16 wallDYaw = atan2s(upperWall->normal.z, upperWall->normal.x) - m->faceAngle[1]; + + if (wallDYaw >= 0x2AAA && wallDYaw <= 0x5555) { + return GROUND_STEP_NONE; + } + if (wallDYaw <= -0x2AAA && wallDYaw >= -0x5555) { + return GROUND_STEP_NONE; + } + + return GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS; + } + + return GROUND_STEP_NONE; +} + +s32 perform_ground_step(struct MarioState *m) { + s32 i; + u32 stepResult; + Vec3f intendedPos; + + for (i = 0; i < 4; i++) { + intendedPos[0] = m->pos[0] + m->floor->normal.y * (m->vel[0] / 4.0f); + intendedPos[2] = m->pos[2] + m->floor->normal.y * (m->vel[2] / 4.0f); + intendedPos[1] = m->pos[1]; + + stepResult = perform_ground_quarter_step(m, intendedPos); + if (stepResult == GROUND_STEP_LEFT_GROUND || stepResult == GROUND_STEP_HIT_WALL_STOP_QSTEPS) { + break; + } + } + + m->terrainSoundAddend = mario_get_terrain_sound_addend(m); + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + + if (stepResult == GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS) { + stepResult = GROUND_STEP_HIT_WALL; + } + return stepResult; +} + +u32 check_ledge_grab(struct MarioState *m, struct Surface *wall, Vec3f intendedPos, Vec3f nextPos) { + struct Surface *ledgeFloor; + Vec3f ledgePos; + f32 displacementX; + f32 displacementZ; + + if (m->vel[1] > 0) { + return FALSE; + } + + displacementX = nextPos[0] - intendedPos[0]; + displacementZ = nextPos[2] - intendedPos[2]; + + // Only ledge grab if the wall displaced Mario in the opposite direction of + // his velocity. + if (displacementX * m->vel[0] + displacementZ * m->vel[2] > 0.0f) { + return FALSE; + } + + //! Since the search for floors starts at y + 160, we will sometimes grab + // a higher ledge than expected (glitchy ledge grab) + ledgePos[0] = nextPos[0] - wall->normal.x * 60.0f; + ledgePos[2] = nextPos[2] - wall->normal.z * 60.0f; + ledgePos[1] = find_floor(ledgePos[0], nextPos[1] + 160.0f, ledgePos[2], &ledgeFloor); + + if (ledgePos[1] - nextPos[1] <= 100.0f) { + return FALSE; + } + + vec3f_copy(m->pos, ledgePos); + m->floor = ledgeFloor; + m->floorHeight = ledgePos[1]; + + m->floorAngle = atan2s(ledgeFloor->normal.z, ledgeFloor->normal.x); + + m->faceAngle[0] = 0; + m->faceAngle[1] = atan2s(wall->normal.z, wall->normal.x) + 0x8000; + return TRUE; +} + +s32 perform_air_quarter_step(struct MarioState *m, Vec3f intendedPos, u32 stepArg) { + s16 wallDYaw; + Vec3f nextPos; + struct Surface *upperWall; + struct Surface *lowerWall; + struct Surface *ceil; + struct Surface *floor; + f32 ceilHeight; + f32 floorHeight; + f32 waterLevel; + + vec3f_copy(nextPos, intendedPos); + + upperWall = resolve_and_return_wall_collisions(nextPos, 150.0f, 50.0f); + lowerWall = resolve_and_return_wall_collisions(nextPos, 30.0f, 50.0f); + + floorHeight = find_floor(nextPos[0], nextPos[1], nextPos[2], &floor); + ceilHeight = vec3f_find_ceil(nextPos, floorHeight, &ceil); + + waterLevel = find_water_level(nextPos[0], nextPos[2]); + + m->wall = NULL; + + //! The water pseudo floor is not referenced when your intended qstep is + // out of bounds, so it won't detect you as landing. + + if (floor == NULL) { + if (nextPos[1] <= m->floorHeight) { + m->pos[1] = m->floorHeight; + return AIR_STEP_LANDED; + } + + m->pos[1] = nextPos[1]; + return AIR_STEP_HIT_WALL; + } + + if ((m->action & ACT_FLAG_RIDING_SHELL) && floorHeight < waterLevel) { + floorHeight = waterLevel; + floor = &gWaterSurfacePseudoFloor; + floor->originOffset = floorHeight; //! Incorrect origin offset (no effect) + } + + //! This check uses f32, but findFloor uses short (overflow jumps) + if (nextPos[1] <= floorHeight) { + if (ceilHeight - floorHeight > 160.0f) { + m->pos[0] = nextPos[0]; + m->pos[2] = nextPos[2]; + m->floor = floor; + m->floorHeight = floorHeight; + } + + //! When ceilHeight - floorHeight <= 160, the step result says that + // Mario landed, but his movement is cancelled and his referenced floor + // isn't updated (pedro spots) + m->pos[1] = floorHeight; + return AIR_STEP_LANDED; + } + + if (nextPos[1] + 160.0f > ceilHeight) { + if (m->vel[1] >= 0.0f) { + m->vel[1] = 0.0f; + + //! Uses referenced ceiling instead of ceil (ceiling hang upwarp) + if ((stepArg & AIR_STEP_CHECK_HANG) && m->ceil != NULL + && m->ceil->type == SURFACE_HANGABLE) { + return AIR_STEP_GRABBED_CEILING; + } + + return AIR_STEP_NONE; + } + + //! Potential subframe downwarp->upwarp? + if (nextPos[1] <= m->floorHeight) { + m->pos[1] = m->floorHeight; + return AIR_STEP_LANDED; + } + + m->pos[1] = nextPos[1]; + return AIR_STEP_HIT_WALL; + } + + //! When the wall is not completely vertical or there is a slight wall + // misalignment, you can activate these conditions in unexpected situations + if ((stepArg & AIR_STEP_CHECK_LEDGE_GRAB) && upperWall == NULL && lowerWall != NULL) { + if (check_ledge_grab(m, lowerWall, intendedPos, nextPos)) { + return AIR_STEP_GRABBED_LEDGE; + } + + vec3f_copy(m->pos, nextPos); + m->floor = floor; + m->floorHeight = floorHeight; + return AIR_STEP_NONE; + } + + vec3f_copy(m->pos, nextPos); + m->floor = floor; + m->floorHeight = floorHeight; + + if (upperWall != NULL || lowerWall != NULL) { + m->wall = upperWall != NULL ? upperWall : lowerWall; + wallDYaw = atan2s(m->wall->normal.z, m->wall->normal.x) - m->faceAngle[1]; + + if (m->wall->type == SURFACE_BURNING) { + return AIR_STEP_HIT_LAVA_WALL; + } + + if (wallDYaw < -0x6000 || wallDYaw > 0x6000) { + m->flags |= MARIO_UNKNOWN_30; + return AIR_STEP_HIT_WALL; + } + } + + return AIR_STEP_NONE; +} + +void apply_twirl_gravity(struct MarioState *m) { + f32 terminalVelocity; + f32 heaviness = 1.0f; + + if (m->angleVel[1] > 1024) { + heaviness = 1024.0f / m->angleVel[1]; + } + + terminalVelocity = -75.0f * heaviness; + + m->vel[1] -= 4.0f * heaviness; + if (m->vel[1] < terminalVelocity) { + m->vel[1] = terminalVelocity; + } +} + +u32 should_strengthen_gravity_for_jump_ascent(struct MarioState *m) { + if (!(m->flags & MARIO_UNKNOWN_08)) { + return FALSE; + } + + if (m->action & (ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE)) { + return FALSE; + } + + if (!(m->input & INPUT_A_DOWN) && m->vel[1] > 20.0f) { + return (m->action & ACT_FLAG_CONTROL_JUMP_HEIGHT) != 0; + } + + return FALSE; +} + +void apply_gravity(struct MarioState *m) { + if (m->action == ACT_TWIRLING && m->vel[1] < 0.0f) { + apply_twirl_gravity(m); + } else if (m->action == ACT_SHOT_FROM_CANNON) { + m->vel[1] -= 1.0f; + if (m->vel[1] < -75.0f) { + m->vel[1] = -75.0f; + } + } else if (m->action == ACT_LONG_JUMP || m->action == ACT_SLIDE_KICK + || m->action == ACT_BBH_ENTER_SPIN) { + m->vel[1] -= 2.0f; + if (m->vel[1] < -75.0f) { + m->vel[1] = -75.0f; + } + } else if (m->action == ACT_LAVA_BOOST || m->action == ACT_FALL_AFTER_STAR_GRAB) { + m->vel[1] -= 3.2f; + if (m->vel[1] < -65.0f) { + m->vel[1] = -65.0f; + } + } else if (m->action == ACT_GETTING_BLOWN) { + m->vel[1] -= m->unkC4; + if (m->vel[1] < -75.0f) { + m->vel[1] = -75.0f; + } + } else if (should_strengthen_gravity_for_jump_ascent(m)) { + m->vel[1] /= 4.0f; + } else if (m->action & ACT_FLAG_METAL_WATER) { + m->vel[1] -= 1.6f; + if (m->vel[1] < -16.0f) { + m->vel[1] = -16.0f; + } + } else if ((m->flags & MARIO_WING_CAP) && m->vel[1] < 0.0f && (m->input & INPUT_A_DOWN)) { + m->marioBodyState->wingFlutter = TRUE; + + m->vel[1] -= 2.0f; + if (m->vel[1] < -37.5f) { + if ((m->vel[1] += 4.0f) > -37.5f) { + m->vel[1] = -37.5f; + } + } + } else { + m->vel[1] -= 4.0f; + if (m->vel[1] < -75.0f) { + m->vel[1] = -75.0f; + } + } +} + +void apply_vertical_wind(struct MarioState *m) { + f32 maxVelY; + f32 offsetY; + + if (m->action != ACT_GROUND_POUND) { + offsetY = m->pos[1] - -1500.0f; + + if (m->floor->type == SURFACE_VERTICAL_WIND && -3000.0f < offsetY && offsetY < 2000.0f) { + if (offsetY >= 0.0f) { + maxVelY = 10000.0f / (offsetY + 200.0f); + } else { + maxVelY = 50.0f; + } + + if (m->vel[1] < maxVelY) { + if ((m->vel[1] += maxVelY / 8.0f) > maxVelY) { + m->vel[1] = maxVelY; + } + } + +#ifdef VERSION_JP + play_sound(SOUND_ENV_WIND2, m->marioObj->header.gfx.cameraToObject); +#endif + } + } +} + +s32 perform_air_step(struct MarioState *m, u32 stepArg) { + Vec3f intendedPos; + s32 i; + s32 quarterStepResult; + s32 stepResult = AIR_STEP_NONE; + + m->wall = NULL; + + for (i = 0; i < 4; i++) { + intendedPos[0] = m->pos[0] + m->vel[0] / 4.0f; + intendedPos[1] = m->pos[1] + m->vel[1] / 4.0f; + intendedPos[2] = m->pos[2] + m->vel[2] / 4.0f; + + quarterStepResult = perform_air_quarter_step(m, intendedPos, stepArg); + + //! 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) { + stepResult = quarterStepResult; + } + + if (quarterStepResult == AIR_STEP_LANDED || quarterStepResult == AIR_STEP_GRABBED_LEDGE + || quarterStepResult == AIR_STEP_GRABBED_CEILING + || quarterStepResult == AIR_STEP_HIT_LAVA_WALL) { + break; + } + } + + if (m->vel[1] >= 0.0f) { + m->peakHeight = m->pos[1]; + } + + m->terrainSoundAddend = mario_get_terrain_sound_addend(m); + + if (m->action != ACT_FLYING) { + apply_gravity(m); + } + apply_vertical_wind(m); + + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + + return stepResult; +} + +// They had these functions the whole time and never used them? Lol + +void set_vel_from_pitch_and_yaw(struct MarioState *m) { + m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]); + m->vel[1] = m->forwardVel * sins(m->faceAngle[0]); + m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]); +} + +void set_vel_from_yaw(struct MarioState *m) { + m->vel[0] = m->slideVelX = m->forwardVel * sins(m->faceAngle[1]); + m->vel[1] = 0.0f; + m->vel[2] = m->slideVelZ = m->forwardVel * coss(m->faceAngle[1]); +} diff --git a/src/game/mario_step.h b/src/game/mario_step.h new file mode 100644 index 0000000..5f9899f --- /dev/null +++ b/src/game/mario_step.h @@ -0,0 +1,36 @@ +#ifndef MARIO_STEP_H +#define MARIO_STEP_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" + +struct BullyCollisionData { + /*0x00*/ f32 conversionRatio; + /*0x04*/ f32 radius; + /*0x08*/ f32 posX; + /*0x0C*/ f32 posZ; + /*0x10*/ f32 velX; + /*0x14*/ f32 velZ; +}; + +extern struct Surface gWaterSurfacePseudoFloor; + +f32 get_additive_y_vel_for_jumps(void); +void stub_mario_step_1(struct MarioState *); +void stub_mario_step_2(void); + +void mario_bonk_reflection(struct MarioState *, u32); +void transfer_bully_speed(struct BullyCollisionData *obj1, struct BullyCollisionData *obj2); +BAD_RETURN(s32) init_bully_collision_data(struct BullyCollisionData *data, f32 posX, f32 posZ, + f32 forwardVel, s16 yaw, f32 conversionRatio, f32 radius); +u32 mario_update_quicksand(struct MarioState *, f32); +u32 mario_push_off_steep_floor(struct MarioState *, u32, u32); +u32 mario_update_moving_sand(struct MarioState *); +u32 mario_update_windy_ground(struct MarioState *); +void stop_and_set_height_to_floor(struct MarioState *); +s32 stationary_ground_step(struct MarioState *); +s32 perform_ground_step(struct MarioState *); +s32 perform_air_step(struct MarioState *, u32); + +#endif // MARIO_STEP_H \ No newline at end of file diff --git a/src/game/object_stuff.c b/src/game/object_stuff.c new file mode 100644 index 0000000..000c802 --- /dev/null +++ b/src/game/object_stuff.c @@ -0,0 +1,308 @@ +/** + * This file just hacks a bunch of stuff together to get a valid Object and GraphNode for Mario + */ +#include +#include "mario.h" +#include "../shim.h" +#include "../engine/math_util.h" +#include "../include/object_fields.h" + +/* activeFlags */ +#define ACTIVE_FLAG_DEACTIVATED 0 // 0x0000 +#define ACTIVE_FLAG_ACTIVE (1 << 0) // 0x0001 +#define ACTIVE_FLAG_FAR_AWAY (1 << 1) // 0x0002 +#define ACTIVE_FLAG_UNK2 (1 << 2) // 0x0004 +#define ACTIVE_FLAG_IN_DIFFERENT_ROOM (1 << 3) // 0x0008 +#define ACTIVE_FLAG_UNIMPORTANT (1 << 4) // 0x0010 +#define ACTIVE_FLAG_INITIATED_TIME_STOP (1 << 5) // 0x0020 +#define ACTIVE_FLAG_MOVE_THROUGH_GRATE (1 << 6) // 0x0040 +#define ACTIVE_FLAG_DITHERED_ALPHA (1 << 7) // 0x0080 +#define ACTIVE_FLAG_UNK8 (1 << 8) // 0x0100 +#define ACTIVE_FLAG_UNK9 (1 << 9) // 0x0200 +#define ACTIVE_FLAG_UNK10 (1 << 10) // 0x0400 + +// The discriminant for different types of geo nodes +#define GRAPH_NODE_TYPE_ROOT 0x001 +#define GRAPH_NODE_TYPE_ORTHO_PROJECTION 0x002 +#define GRAPH_NODE_TYPE_PERSPECTIVE (0x003 | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_MASTER_LIST 0x004 +#define GRAPH_NODE_TYPE_START 0x00A +#define GRAPH_NODE_TYPE_LEVEL_OF_DETAIL 0x00B +#define GRAPH_NODE_TYPE_SWITCH_CASE (0x00C | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_CAMERA (0x014 | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_TRANSLATION_ROTATION 0x015 +#define GRAPH_NODE_TYPE_TRANSLATION 0x016 +#define GRAPH_NODE_TYPE_ROTATION 0x017 +#define GRAPH_NODE_TYPE_OBJECT 0x018 +#define GRAPH_NODE_TYPE_ANIMATED_PART 0x019 +#define GRAPH_NODE_TYPE_BILLBOARD 0x01A +#define GRAPH_NODE_TYPE_DISPLAY_LIST 0x01B +#define GRAPH_NODE_TYPE_SCALE 0x01C +#define GRAPH_NODE_TYPE_SHADOW 0x028 +#define GRAPH_NODE_TYPE_OBJECT_PARENT 0x029 +#define GRAPH_NODE_TYPE_GENERATED_LIST (0x02A | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_BACKGROUND (0x02C | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_HELD_OBJ (0x02E | GRAPH_NODE_TYPE_FUNCTIONAL) +#define GRAPH_NODE_TYPE_CULLING_RADIUS 0x02F + +/* respawnInfoType */ +#define RESPAWN_INFO_TYPE_NULL 0 +#define RESPAWN_INFO_TYPE_32 1 +#define RESPAWN_INFO_TYPE_16 2 + +#define GRAPH_RENDER_ACTIVE (1 << 0) +#define GRAPH_RENDER_CHILDREN_FIRST (1 << 1) +#define GRAPH_RENDER_BILLBOARD (1 << 2) +#define GRAPH_RENDER_Z_BUFFER (1 << 3) +#define GRAPH_RENDER_INVISIBLE (1 << 4) +#define GRAPH_RENDER_HAS_ANIMATION (1 << 5) + +static Vec3f gVec3fZero = { 0.0f, 0.0f, 0.0f }; +static Vec3s gVec3sZero = { 0, 0, 0 }; +static Vec3f gVec3fOne = { 1.0f, 1.0f, 1.0f }; + +static struct GraphNodeObject *init_graph_node_object( Vec3f pos, Vec3s angle, Vec3f scale) { + struct GraphNodeObject *graphNode = malloc(sizeof(struct GraphNodeObject)); + + //init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_OBJECT); + + graphNode->node.type = GRAPH_NODE_TYPE_OBJECT; + graphNode->node.flags = GRAPH_RENDER_ACTIVE; + graphNode->node.prev = &graphNode->node; + graphNode->node.next = &graphNode->node; + graphNode->node.parent = NULL; + graphNode->node.children = NULL; + + vec3f_copy(graphNode->pos, pos); + vec3f_copy(graphNode->scale, scale); + vec3s_copy(graphNode->angle, angle); + vec3f_copy(graphNode->cameraToObject, gVec3fZero); + + graphNode->sharedChild = NULL; + graphNode->throwMatrix = NULL; + graphNode->animInfo.animID = 0; + graphNode->animInfo.curAnim = NULL; + graphNode->animInfo.animFrame = 0; + graphNode->animInfo.animFrameAccelAssist = 0; + graphNode->animInfo.animAccel = 0x10000; + graphNode->animInfo.animTimer = 0; + graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION; + + return graphNode; +} + +static struct Object *try_allocate_object(void) { + struct ObjectNode *nextObj; + nextObj = (struct ObjectNode *) malloc(sizeof(struct Object)); + nextObj->prev = NULL; + nextObj->next = NULL; + + init_graph_node_object(gVec3fZero, gVec3sZero, gVec3fOne); + + return (struct Object *) nextObj; +} + +static struct Object *allocate_object(void) { + s32 i; + struct Object *obj = try_allocate_object(); + + // Initialize object fields + + obj->activeFlags = ACTIVE_FLAG_ACTIVE | ACTIVE_FLAG_UNK8; + obj->parentObj = obj; + obj->prevObj = NULL; + obj->collidedObjInteractTypes = 0; + obj->numCollidedObjs = 0; + + for (i = 0; i < 0x50; i++) { + obj->rawData.asS32[i] = 0; + obj->ptrData.asVoidPtr[i] = NULL; + } + + obj->unused1 = 0; + obj->bhvStackIndex = 0; + obj->bhvDelayTimer = 0; + + obj->hitboxRadius = 37.0f; // Override directly for Mario + obj->hitboxHeight = 160.0f; // + obj->hurtboxRadius = 0.0f; + obj->hurtboxHeight = 0.0f; + obj->hitboxDownOffset = 0.0f; + obj->unused2 = 0; + + obj->platform = NULL; + obj->collisionData = NULL; + obj->oIntangibleTimer = -1; + obj->oDamageOrCoinValue = 0; + obj->oHealth = 2048; + + obj->oCollisionDistance = 1000.0f; + if (gCurrLevelNum == LEVEL_TTC) { + obj->oDrawingDistance = 2000.0f; + } else { + obj->oDrawingDistance = 4000.0f; + } + + mtxf_identity(obj->transform); + + obj->respawnInfoType = RESPAWN_INFO_TYPE_NULL; + obj->respawnInfo = NULL; + + obj->oDistanceToMario = 19000.0f; + obj->oRoom = -1; + + obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; + obj->header.gfx.pos[0] = -10000.0f; + obj->header.gfx.pos[1] = -10000.0f; + obj->header.gfx.pos[2] = -10000.0f; + obj->header.gfx.throwMatrix = NULL; + + return obj; +} + +static struct Object *create_object(void) { + struct Object *obj; + obj = allocate_object(); + obj->curBhvCommand = NULL; + obj->behavior = NULL; + return obj; +} + +static void geo_obj_init(struct GraphNodeObject *graphNode, void *sharedChild, Vec3f pos, Vec3s angle) { + vec3f_set(graphNode->scale, 1.0f, 1.0f, 1.0f); + vec3f_copy(graphNode->pos, pos); + vec3s_copy(graphNode->angle, angle); + + graphNode->sharedChild = sharedChild; + graphNode->unk4C = 0; + graphNode->throwMatrix = NULL; + graphNode->animInfo.curAnim = NULL; + + graphNode->node.flags |= GRAPH_RENDER_ACTIVE; + graphNode->node.flags &= ~GRAPH_RENDER_INVISIBLE; + graphNode->node.flags |= GRAPH_RENDER_HAS_ANIMATION; + graphNode->node.flags &= ~GRAPH_RENDER_BILLBOARD; +} + +static struct Object *spawn_object_at_origin(void) { + struct Object *obj; + obj = create_object(); + + obj->parentObj = NULL; + obj->header.gfx.areaIndex = 0; + obj->header.gfx.activeAreaIndex = 0; +// geo_obj_init((struct GraphNodeObject *) &obj->header.gfx, gLoadedGraphNodes[model], gVec3fZero, gVec3sZero); + geo_obj_init((struct GraphNodeObject *) &obj->header.gfx, NULL, gVec3fZero, gVec3sZero); + + return obj; +} + +/** + * Copy position, velocity, and angle variables from MarioState to the Mario + * object. + */ +static void copy_mario_state_to_object(void) { + s32 i = 0; + // L is real + if (gCurrentObject != gMarioObject) { + i += 1; + } + + gCurrentObject->oVelX = gMarioState->vel[0]; + gCurrentObject->oVelY = gMarioState->vel[1]; + gCurrentObject->oVelZ = gMarioState->vel[2]; + + gCurrentObject->oPosX = gMarioState->pos[0]; + gCurrentObject->oPosY = gMarioState->pos[1]; + gCurrentObject->oPosZ = gMarioState->pos[2]; + + gCurrentObject->oMoveAnglePitch = gCurrentObject->header.gfx.angle[0]; + gCurrentObject->oMoveAngleYaw = gCurrentObject->header.gfx.angle[1]; + gCurrentObject->oMoveAngleRoll = gCurrentObject->header.gfx.angle[2]; + + gCurrentObject->oFaceAnglePitch = gCurrentObject->header.gfx.angle[0]; + gCurrentObject->oFaceAngleYaw = gCurrentObject->header.gfx.angle[1]; + gCurrentObject->oFaceAngleRoll = gCurrentObject->header.gfx.angle[2]; + + gCurrentObject->oAngleVelPitch = gMarioState->angleVel[0]; + gCurrentObject->oAngleVelYaw = gMarioState->angleVel[1]; + gCurrentObject->oAngleVelRoll = gMarioState->angleVel[2]; +} + +struct Object *hack_allocate_mario(void) +{ + return spawn_object_at_origin(); +} + +/** + * Mario's primary behavior update function. + */ +void bhv_mario_update(void) { + u32 particleFlags = 0; + s32 i; + + gCurrentObject = gMarioObject; + particleFlags = execute_mario_action(gCurrentObject); + gCurrentObject->oMarioParticleFlags = particleFlags; + + // Mario code updates MarioState's versions of position etc, so we need + // to sync it with the Mario object + copy_mario_state_to_object(); + +// i = 0; +// while (sParticleTypes[i].particleFlag != 0) { +// if (particleFlags & sParticleTypes[i].particleFlag) { +// spawn_particle(sParticleTypes[i].activeParticleFlag, sParticleTypes[i].model, +// sParticleTypes[i].behavior); +// } + +// i++; +// } +} + + + + + +void create_transformation_from_matrices(Mat4 a0, Mat4 a1, Mat4 a2) { + f32 spC, sp8, sp4; + + spC = a2[3][0] * a2[0][0] + a2[3][1] * a2[0][1] + a2[3][2] * a2[0][2]; + sp8 = a2[3][0] * a2[1][0] + a2[3][1] * a2[1][1] + a2[3][2] * a2[1][2]; + sp4 = a2[3][0] * a2[2][0] + a2[3][1] * a2[2][1] + a2[3][2] * a2[2][2]; + + a0[0][0] = a1[0][0] * a2[0][0] + a1[0][1] * a2[0][1] + a1[0][2] * a2[0][2]; + a0[0][1] = a1[0][0] * a2[1][0] + a1[0][1] * a2[1][1] + a1[0][2] * a2[1][2]; + a0[0][2] = a1[0][0] * a2[2][0] + a1[0][1] * a2[2][1] + a1[0][2] * a2[2][2]; + + a0[1][0] = a1[1][0] * a2[0][0] + a1[1][1] * a2[0][1] + a1[1][2] * a2[0][2]; + a0[1][1] = a1[1][0] * a2[1][0] + a1[1][1] * a2[1][1] + a1[1][2] * a2[1][2]; + a0[1][2] = a1[1][0] * a2[2][0] + a1[1][1] * a2[2][1] + a1[1][2] * a2[2][2]; + + a0[2][0] = a1[2][0] * a2[0][0] + a1[2][1] * a2[0][1] + a1[2][2] * a2[0][2]; + a0[2][1] = a1[2][0] * a2[1][0] + a1[2][1] * a2[1][1] + a1[2][2] * a2[1][2]; + a0[2][2] = a1[2][0] * a2[2][0] + a1[2][1] * a2[2][1] + a1[2][2] * a2[2][2]; + + a0[3][0] = a1[3][0] * a2[0][0] + a1[3][1] * a2[0][1] + a1[3][2] * a2[0][2] - spC; + a0[3][1] = a1[3][0] * a2[1][0] + a1[3][1] * a2[1][1] + a1[3][2] * a2[1][2] - sp8; + a0[3][2] = a1[3][0] * a2[2][0] + a1[3][1] * a2[2][1] + a1[3][2] * a2[2][2] - sp4; + + a0[0][3] = 0; + a0[1][3] = 0; + a0[2][3] = 0; + a0[3][3] = 1.0f; +} +void obj_update_pos_from_parent_transformation(Mat4 a0, struct Object *a1) { + f32 spC = a1->oParentRelativePosX; + f32 sp8 = a1->oParentRelativePosY; + f32 sp4 = a1->oParentRelativePosZ; + + a1->oPosX = spC * a0[0][0] + sp8 * a0[1][0] + sp4 * a0[2][0] + a0[3][0]; + a1->oPosY = spC * a0[0][1] + sp8 * a0[1][1] + sp4 * a0[2][1] + a0[3][1]; + a1->oPosZ = spC * a0[0][2] + sp8 * a0[1][2] + sp4 * a0[2][2] + a0[3][2]; +} +void obj_set_gfx_pos_from_pos(struct Object *obj) { + obj->header.gfx.pos[0] = obj->oPosX; + obj->header.gfx.pos[1] = obj->oPosY; + obj->header.gfx.pos[2] = obj->oPosZ; +} \ No newline at end of file diff --git a/src/game/object_stuff.h b/src/game/object_stuff.h new file mode 100644 index 0000000..14f3eba --- /dev/null +++ b/src/game/object_stuff.h @@ -0,0 +1,9 @@ +#pragma once + +#include "../include/types.h" + +struct Object *hack_allocate_mario(void); +void bhv_mario_update(void); +void create_transformation_from_matrices(Mat4 a0, Mat4 a1, Mat4 a2); +void obj_update_pos_from_parent_transformation(Mat4 a0, struct Object *a1); +void obj_set_gfx_pos_from_pos(struct Object *obj); \ No newline at end of file diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c new file mode 100644 index 0000000..87360f0 --- /dev/null +++ b/src/game/rendering_graph_node.c @@ -0,0 +1,1166 @@ +#define NO_SEGMENTED_MEMORY + +#include "../include/PR/gbi.h" +#include "../include/PR/ultratypes.h" + +#include "area.h" +#include "../engine/math_util.h" +//#include "game_init.h" +//#include "gfx_dimensions.h" +//#include "main.h" +#include "../memory.h" +//#include "print.h" +#include "rendering_graph_node.h" +//#include "shadow.h" +#include "../include/sm64.h" +#include "../shim.h" +#include "../gfx_adapter.h" + + + +#include "../model/model.inc.h" + + +// PATCH +static Vec3s gVec3sZero = { 0, 0, 0 }; +static Vec3f gVec3fZero = { 0, 0, 0 }; +static Gfx *gDisplayListHead; +#define USE_SYSTEM_MALLOC + + + +/** + * This file contains the code that processes the scene graph for rendering. + * The scene graph is responsible for drawing everything except the HUD / text boxes. + * First the root of the scene graph is processed when geo_process_root + * is called from level_script.c. The rest of the tree is traversed recursively + * using the function geo_process_node_and_siblings, which switches over all + * geo node types and calls a specialized function accordingly. + * The types are defined in engine/graph_node.h + * + * The scene graph typically looks like: + * - Root (viewport) + * - Master list + * - Ortho projection + * - Background (skybox) + * - Master list + * - Perspective + * - Camera + * - + * - Object parent + * - + * - Master list + * - Script node (Cannon overlay) + * + */ + +s16 gMatStackIndex; +Mat4 gMatStack[32]; +Mtx *gMatStackFixed[32]; + +/** + * Animation nodes have state in global variables, so this struct captures + * the animation state so a 'context switch' can be made when rendering the + * held object. + */ +struct GeoAnimState { + /*0x00*/ u8 type; + /*0x01*/ u8 enabled; + /*0x02*/ s16 frame; + /*0x04*/ f32 translationMultiplier; + /*0x08*/ u16 *attribute; + /*0x0C*/ s16 *data; +}; + +// For some reason, this is a GeoAnimState struct, but the current state consists +// of separate global variables. It won't match EU otherwise. +struct GeoAnimState gGeoTempState; + +u8 gCurAnimType; +u8 gCurAnimEnabled; +s16 gCurrAnimFrame; +f32 gCurAnimTranslationMultiplier; +u16 *gCurrAnimAttribute; +s16 *gCurAnimData; + +struct AllocOnlyPool *gDisplayListHeap; + +struct RenderModeContainer { + u32 modes[8]; +}; + +/* Rendermode settings for cycle 1 for all 8 layers. */ +struct RenderModeContainer renderModeTable_1Cycle[2] = { { { + G_RM_OPA_SURF, + G_RM_AA_OPA_SURF, + G_RM_AA_OPA_SURF, + G_RM_AA_OPA_SURF, + G_RM_AA_TEX_EDGE, + G_RM_AA_XLU_SURF, + G_RM_AA_XLU_SURF, + G_RM_AA_XLU_SURF, + } }, + { { + /* z-buffered */ + G_RM_ZB_OPA_SURF, + G_RM_AA_ZB_OPA_SURF, + G_RM_AA_ZB_OPA_DECAL, + G_RM_AA_ZB_OPA_INTER, + G_RM_AA_ZB_TEX_EDGE, + G_RM_AA_ZB_XLU_SURF, + G_RM_AA_ZB_XLU_DECAL, + G_RM_AA_ZB_XLU_INTER, + } } }; + +/* Rendermode settings for cycle 2 for all 8 layers. */ +struct RenderModeContainer renderModeTable_2Cycle[2] = { { { + G_RM_OPA_SURF2, + G_RM_AA_OPA_SURF2, + G_RM_AA_OPA_SURF2, + G_RM_AA_OPA_SURF2, + G_RM_AA_TEX_EDGE2, + G_RM_AA_XLU_SURF2, + G_RM_AA_XLU_SURF2, + G_RM_AA_XLU_SURF2, + } }, + { { + /* z-buffered */ + G_RM_ZB_OPA_SURF2, + G_RM_AA_ZB_OPA_SURF2, + G_RM_AA_ZB_OPA_DECAL2, + G_RM_AA_ZB_OPA_INTER2, + G_RM_AA_ZB_TEX_EDGE2, + G_RM_AA_ZB_XLU_SURF2, + G_RM_AA_ZB_XLU_DECAL2, + G_RM_AA_ZB_XLU_INTER2, + } } }; + +struct GraphNodeRoot *gCurGraphNodeRoot = NULL; +struct GraphNodeMasterList *gCurGraphNodeMasterList = NULL; +struct GraphNodePerspective *gCurGraphNodeCamFrustum = NULL; +struct GraphNodeCamera *gCurGraphNodeCamera = NULL; +struct GraphNodeObject *gCurGraphNodeObject = NULL; +struct GraphNodeHeldObject *gCurGraphNodeHeldObject = NULL; +u16 gAreaUpdateCounter = 0; + +#ifdef F3DEX_GBI_2 +LookAt lookAt; +#endif + +/** + * Process a master list node. + */ +static void geo_process_master_list_sub(struct GraphNodeMasterList *node) { + struct DisplayListNode *currList; + s32 i; + s32 enableZBuffer = (node->node.flags & GRAPH_RENDER_Z_BUFFER) != 0; + struct RenderModeContainer *modeList = &renderModeTable_1Cycle[enableZBuffer]; + struct RenderModeContainer *mode2List = &renderModeTable_2Cycle[enableZBuffer]; + + // @bug This is where the LookAt values should be calculated but aren't. + // As a result, environment mapping is broken on Fast3DEX2 without the + // changes below. +#ifdef F3DEX_GBI_2 + Mtx lMtx; + guLookAtReflect(&lMtx, &lookAt, 0, 0, 0, /* eye */ 0, 0, 1, /* at */ 1, 0, 0 /* up */); +#endif + + if (enableZBuffer != 0) { + gDPPipeSync(gDisplayListHead++); + gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER); + } + + // HACK + // Mario ends up in the second master list for some reason, and the first item in that list is an invalid pointer. + int xx = FALSE; + for (i = 1; i < GFX_NUM_MASTER_LISTS; i++) { + if ((currList = node->listHeads[i]) != NULL) { + gDPSetRenderMode(gDisplayListHead++, modeList->modes[i], mode2List->modes[i]); + while (currList != NULL) { + if( !xx ) { + xx = TRUE; + currList = currList->next; + continue; + } + + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(currList->transform), + G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + gSPDisplayList(gDisplayListHead++, currList->displayList); + currList = currList->next; + } + } + } + if (enableZBuffer != 0) { + gDPPipeSync(gDisplayListHead++); + gSPClearGeometryMode(gDisplayListHead++, G_ZBUFFER); + } +} + +/** + * Appends the display list to one of the master lists based on the layer + * parameter. Look at the RenderModeContainer struct to see the corresponding + * render modes of layers. + */ +static void geo_append_display_list(void *displayList, s16 layer) { + +#ifdef F3DEX_GBI_2 + gSPLookAt(gDisplayListHead++, &lookAt); +#endif + if (gCurGraphNodeMasterList != 0) { + struct DisplayListNode *listNode = + alloc_only_pool_alloc(gDisplayListHeap, sizeof(struct DisplayListNode)); + + listNode->transform = gMatStackFixed[gMatStackIndex]; + listNode->displayList = displayList; + listNode->next = 0; + if (gCurGraphNodeMasterList->listHeads[layer] == 0) { + gCurGraphNodeMasterList->listHeads[layer] = listNode; + } else { + gCurGraphNodeMasterList->listTails[layer]->next = listNode; + } + gCurGraphNodeMasterList->listTails[layer] = listNode; + } +} + +/** + * Process the master list node. + */ +static void geo_process_master_list(struct GraphNodeMasterList *node) { + s32 i; + UNUSED s32 sp1C; + + if (gCurGraphNodeMasterList == NULL && node->node.children != NULL) { + gCurGraphNodeMasterList = node; + for (i = 0; i < GFX_NUM_MASTER_LISTS; i++) { + node->listHeads[i] = NULL; + } + geo_process_node_and_siblings(node->node.children); + geo_process_master_list_sub(node); + gCurGraphNodeMasterList = NULL; + } +} + +/** + * Process an orthographic projection node. + */ +static void geo_process_ortho_projection(struct GraphNodeOrthoProjection *node) { + if (node->node.children != NULL) { + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + f32 left = (gCurGraphNodeRoot->x - gCurGraphNodeRoot->width) / 2.0f * node->scale; + f32 right = (gCurGraphNodeRoot->x + gCurGraphNodeRoot->width) / 2.0f * node->scale; + f32 top = (gCurGraphNodeRoot->y - gCurGraphNodeRoot->height) / 2.0f * node->scale; + f32 bottom = (gCurGraphNodeRoot->y + gCurGraphNodeRoot->height) / 2.0f * node->scale; + + guOrtho(mtx, left, right, bottom, top, -2.0f, 2.0f, 1.0f); + gSPPerspNormalize(gDisplayListHead++, 0xFFFF); + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + + geo_process_node_and_siblings(node->node.children); + } +} + +/** + * Process a perspective projection node. + */ +static void geo_process_perspective(struct GraphNodePerspective *node) { + if (node->fnNode.func != NULL) { + node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); + } + if (node->fnNode.node.children != NULL) { + u16 perspNorm; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + +#ifdef VERSION_EU + f32 aspect = ((f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height) * 1.1f; +#else + f32 aspect = (f32) gCurGraphNodeRoot->width / (f32) gCurGraphNodeRoot->height; +#endif + + guPerspective(mtx, &perspNorm, node->fov, aspect, node->near, node->far, 1.0f); + gSPPerspNormalize(gDisplayListHead++, perspNorm); + + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(mtx), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); + + gCurGraphNodeCamFrustum = node; + geo_process_node_and_siblings(node->fnNode.node.children); + gCurGraphNodeCamFrustum = NULL; + } +} + +/** + * Process a level of detail node. From the current transformation matrix, + * the perpendicular distance to the camera is extracted and the children + * of this node are only processed if that distance is within the render + * range of this node. + */ +static void geo_process_level_of_detail(struct GraphNodeLevelOfDetail *node) { +#ifdef GBI_FLOATS + Mtx *mtx = gMatStackFixed[gMatStackIndex]; + s16 distanceFromCam = (s32) -mtx->m[3][2]; // z-component of the translation column +#else + // The fixed point Mtx type is defined as 16 longs, but it's actually 16 + // shorts for the integer parts followed by 16 shorts for the fraction parts + Mtx *mtx = gMatStackFixed[gMatStackIndex]; + s16 distanceFromCam = -GET_HIGH_S16_OF_32(mtx->m[1][3]); // z-component of the translation column +#endif + +#ifndef TARGET_N64 + // We assume modern hardware is powerful enough to draw the most detailed variant + distanceFromCam = 0; +#endif + + if (node->minDistance <= distanceFromCam && distanceFromCam < node->maxDistance) { + if (node->node.children != 0) { + geo_process_node_and_siblings(node->node.children); + } + } +} + +/** + * Process a switch case node. The node's selection function is called + * if it is 0, and among the node's children, only the selected child is + * processed next. + */ +static void geo_process_switch(struct GraphNodeSwitchCase *node) { + struct GraphNode *selectedChild = node->fnNode.node.children; + s32 i; + + if (node->fnNode.func != NULL) { + node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); + } + for (i = 0; selectedChild != NULL && node->selectedCase > i; i++) { + selectedChild = selectedChild->next; + } + if (selectedChild != NULL) { + geo_process_node_and_siblings(selectedChild); + } +} + +/** + * Process a camera node. + */ +static void geo_process_camera(struct GraphNodeCamera *node) { + Mat4 cameraTransform; + Mtx *rollMtx = alloc_display_list(sizeof(*rollMtx)); + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + if (node->fnNode.func != NULL) { + node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); + } + mtxf_rotate_xy(rollMtx, node->rollScreen); + + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(rollMtx), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH); + + mtxf_lookat(cameraTransform, node->pos, node->focus, node->roll); + mtxf_mul(gMatStack[gMatStackIndex + 1], cameraTransform, gMatStack[gMatStackIndex]); + gMatStackIndex++; + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->fnNode.node.children != 0) { + gCurGraphNodeCamera = node; + node->matrixPtr = &gMatStack[gMatStackIndex]; + geo_process_node_and_siblings(node->fnNode.node.children); + gCurGraphNodeCamera = NULL; + } + gMatStackIndex--; +} + +/** + * Process a translation / rotation node. A transformation matrix based + * on the node's translation and rotation is created and pushed on both + * the float and fixed point matrix stacks. + * For the rest it acts as a normal display list node. + */ +static void geo_process_translation_rotation(struct GraphNodeTranslationRotation *node) { + Mat4 mtxf; + Vec3f translation; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + vec3s_to_vec3f(translation, node->translation); + mtxf_rotate_zxy_and_translate(mtxf, translation, node->rotation); + mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + gMatStackIndex++; + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } + gMatStackIndex--; +} + +/** + * Process a translation node. A transformation matrix based on the node's + * translation is created and pushed on both the float and fixed point matrix stacks. + * For the rest it acts as a normal display list node. + */ +static void geo_process_translation(struct GraphNodeTranslation *node) { + Mat4 mtxf; + Vec3f translation; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + vec3s_to_vec3f(translation, node->translation); + mtxf_rotate_zxy_and_translate(mtxf, translation, gVec3sZero); + mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + gMatStackIndex++; + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } + gMatStackIndex--; +} + +/** + * Process a rotation node. A transformation matrix based on the node's + * rotation is created and pushed on both the float and fixed point matrix stacks. + * For the rest it acts as a normal display list node. + */ +static void geo_process_rotation(struct GraphNodeRotation *node) { + Mat4 mtxf; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + mtxf_rotate_zxy_and_translate(mtxf, gVec3fZero, node->rotation); + mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + gMatStackIndex++; + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } + gMatStackIndex--; +} + +/** + * Process a scaling node. A transformation matrix based on the node's + * scale is created and pushed on both the float and fixed point matrix stacks. + * For the rest it acts as a normal display list node. + */ +static void geo_process_scale(struct GraphNodeScale *node) { + UNUSED Mat4 transform; + Vec3f scaleVec; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + vec3f_set(scaleVec, node->scale, node->scale, node->scale); + mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], scaleVec); + gMatStackIndex++; + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } + gMatStackIndex--; +} + +/** + * Process a billboard node. A transformation matrix is created that makes its + * children face the camera, and it is pushed on the floating point and fixed + * point matrix stacks. + * For the rest it acts as a normal display list node. + */ +static void geo_process_billboard(struct GraphNodeBillboard *node) { + Vec3f translation; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + gMatStackIndex++; + vec3s_to_vec3f(translation, node->translation); + mtxf_billboard(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex - 1], translation, + gCurGraphNodeCamera->roll); + if (gCurGraphNodeHeldObject != NULL) { + mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], + gCurGraphNodeHeldObject->objNode->header.gfx.scale); + } else if (gCurGraphNodeObject != NULL) { + mtxf_scale_vec3f(gMatStack[gMatStackIndex], gMatStack[gMatStackIndex], + gCurGraphNodeObject->scale); + } + + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } + gMatStackIndex--; +} + +/** + * Process a display list node. It draws a display list without first pushing + * a transformation on the stack, so all transformations are inherited from the + * parent node. It processes its children if it has them. + */ +static void geo_process_display_list(struct GraphNodeDisplayList *node) { + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } +} + +/** + * Process a generated list. Instead of storing a pointer to a display list, + * the list is generated on the fly by a function. + */ +static void geo_process_generated_list(struct GraphNodeGenerated *node) { + if (node->fnNode.func != NULL) { + Gfx *list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, + (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); + + if (list != NULL) { + geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); + } + } + if (node->fnNode.node.children != NULL) { + geo_process_node_and_siblings(node->fnNode.node.children); + } +} + +/** + * Process a background node. Tries to retrieve a background display list from + * the function of the node. If that function is null or returns null, a black + * rectangle is drawn instead. + */ +static void geo_process_background(struct GraphNodeBackground *node) { + Gfx *list = NULL; + + if (node->fnNode.func != NULL) { + list = node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, + (struct AllocOnlyPool *) gMatStack[gMatStackIndex]); + } + if (list != NULL) { + geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(list), node->fnNode.node.flags >> 8); + } else if (gCurGraphNodeMasterList != NULL) { +#ifndef F3DEX_GBI_2E + Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 7); +#else + Gfx *gfxStart = alloc_display_list(sizeof(Gfx) * 8); +#endif + Gfx *gfx = gfxStart; + + gDPPipeSync(gfx++); + gDPSetCycleType(gfx++, G_CYC_FILL); + gDPSetFillColor(gfx++, node->background); + gDPFillRectangle(gfx++, GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(0), BORDER_HEIGHT, + GFX_DIMENSIONS_RECT_FROM_RIGHT_EDGE(0) - 1, SCREEN_HEIGHT - BORDER_HEIGHT - 1); + gDPPipeSync(gfx++); + gDPSetCycleType(gfx++, G_CYC_1CYCLE); + gSPEndDisplayList(gfx++); + + geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(gfxStart), 0); + } + if (node->fnNode.node.children != NULL) { + geo_process_node_and_siblings(node->fnNode.node.children); + } +} + +/** + * Render an animated part. The current animation state is not part of the node + * but set in global variables. If an animated part is skipped, everything afterwards desyncs. + */ +static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) { + Mat4 matrix; + Vec3s rotation; + Vec3f translation; + Mtx *matrixPtr = alloc_display_list(sizeof(*matrixPtr)); + + vec3s_copy(rotation, gVec3sZero); + vec3f_set(translation, node->translation[0], node->translation[1], node->translation[2]); + if (gCurAnimType == ANIM_TYPE_TRANSLATION) { + translation[0] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] + * gCurAnimTranslationMultiplier; + translation[1] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] + * gCurAnimTranslationMultiplier; + translation[2] += gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] + * gCurAnimTranslationMultiplier; + gCurAnimType = ANIM_TYPE_ROTATION; + } else { + if (gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { + translation[0] += + gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] + * gCurAnimTranslationMultiplier; + gCurrAnimAttribute += 2; + translation[2] += + gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] + * gCurAnimTranslationMultiplier; + gCurAnimType = ANIM_TYPE_ROTATION; + } else { + if (gCurAnimType == ANIM_TYPE_VERTICAL_TRANSLATION) { + gCurrAnimAttribute += 2; + translation[1] += + gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] + * gCurAnimTranslationMultiplier; + gCurrAnimAttribute += 2; + gCurAnimType = ANIM_TYPE_ROTATION; + } else if (gCurAnimType == ANIM_TYPE_NO_TRANSLATION) { + gCurrAnimAttribute += 6; + gCurAnimType = ANIM_TYPE_ROTATION; + } + } + } + + if (gCurAnimType == ANIM_TYPE_ROTATION) { + rotation[0] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; + rotation[1] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; + rotation[2] = gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)]; + } + mtxf_rotate_xyz_and_translate(matrix, translation, rotation); + mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]); + gMatStackIndex++; + mtxf_to_mtx(matrixPtr, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = matrixPtr; + if (node->displayList != NULL) { + geo_append_display_list(node->displayList, node->node.flags >> 8); + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } + gMatStackIndex--; +} + +/** + * Initialize the animation-related global variables for the currently drawn + * object's animation. + */ +void geo_set_animation_globals(struct AnimInfo *node, s32 hasAnimation) { + struct Animation *anim = node->curAnim; + + if (hasAnimation) { + node->animFrame = geo_update_animation_frame(node, &node->animFrameAccelAssist); + } + node->animTimer = gAreaUpdateCounter; + if (anim->flags & ANIM_FLAG_HOR_TRANS) { + gCurAnimType = ANIM_TYPE_VERTICAL_TRANSLATION; + } else if (anim->flags & ANIM_FLAG_VERT_TRANS) { + gCurAnimType = ANIM_TYPE_LATERAL_TRANSLATION; + } else if (anim->flags & ANIM_FLAG_6) { + gCurAnimType = ANIM_TYPE_NO_TRANSLATION; + } else { + gCurAnimType = ANIM_TYPE_TRANSLATION; + } + + gCurrAnimFrame = node->animFrame; + gCurAnimEnabled = (anim->flags & ANIM_FLAG_5) == 0; + gCurrAnimAttribute = segmented_to_virtual((void *) anim->index); + gCurAnimData = segmented_to_virtual((void *) anim->values); + + if (anim->animYTransDivisor == 0) { + gCurAnimTranslationMultiplier = 1.0f; + } else { + gCurAnimTranslationMultiplier = (f32) node->animYTrans / (f32) anim->animYTransDivisor; + } +} + +/** + * Process a shadow node. Renders a shadow under an object offset by the + * translation of the first animated component and rotated according to + * the floor below it. + */ +static void geo_process_shadow(struct GraphNodeShadow *node) { +// Gfx *shadowList; +// Mat4 mtxf; +// Vec3f shadowPos; +// Vec3f animOffset; +// f32 objScale; +// f32 shadowScale; +// f32 sinAng; +// f32 cosAng; +// struct GraphNode *geo; +// Mtx *mtx; + +// if (gCurGraphNodeCamera != NULL && gCurGraphNodeObject != NULL) { +// if (gCurGraphNodeHeldObject != NULL) { +// get_pos_from_transform_mtx(shadowPos, gMatStack[gMatStackIndex], +// *gCurGraphNodeCamera->matrixPtr); +// shadowScale = node->shadowScale; +// } else { +// vec3f_copy(shadowPos, gCurGraphNodeObject->pos); +// shadowScale = node->shadowScale * gCurGraphNodeObject->scale[0]; +// } + +// objScale = 1.0f; +// if (gCurAnimEnabled) { +// if (gCurAnimType == ANIM_TYPE_TRANSLATION +// || gCurAnimType == ANIM_TYPE_LATERAL_TRANSLATION) { +// geo = node->node.children; +// if (geo != NULL && geo->type == GRAPH_NODE_TYPE_SCALE) { +// objScale = ((struct GraphNodeScale *) geo)->scale; +// } +// animOffset[0] = +// gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +// * gCurAnimTranslationMultiplier * objScale; +// animOffset[1] = 0.0f; +// gCurrAnimAttribute += 2; +// animOffset[2] = +// gCurAnimData[retrieve_animation_index(gCurrAnimFrame, &gCurrAnimAttribute)] +// * gCurAnimTranslationMultiplier * objScale; +// gCurrAnimAttribute -= 6; + +// // simple matrix rotation so the shadow offset rotates along with the object +// sinAng = sins(gCurGraphNodeObject->angle[1]); +// cosAng = coss(gCurGraphNodeObject->angle[1]); + +// shadowPos[0] += animOffset[0] * cosAng + animOffset[2] * sinAng; +// shadowPos[2] += -animOffset[0] * sinAng + animOffset[2] * cosAng; +// } +// } + +// shadowList = create_shadow_below_xyz(shadowPos[0], shadowPos[1], shadowPos[2], shadowScale, +// node->shadowSolidity, node->shadowType); +// if (shadowList != NULL) { +// mtx = alloc_display_list(sizeof(*mtx)); +// gMatStackIndex++; +// mtxf_translate(mtxf, shadowPos); +// mtxf_mul(gMatStack[gMatStackIndex], mtxf, *gCurGraphNodeCamera->matrixPtr); +// mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); +// gMatStackFixed[gMatStackIndex] = mtx; +// if (gShadowAboveWaterOrLava == TRUE) { +// geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 4); +// } else if (gMarioOnIceOrCarpet == 1) { +// geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 5); +// } else { +// geo_append_display_list((void *) VIRTUAL_TO_PHYSICAL(shadowList), 6); +// } +// gMatStackIndex--; +// } +// } +// if (node->node.children != NULL) { +// geo_process_node_and_siblings(node->node.children); +// } +} + +/** + * Check whether an object is in view to determine whether it should be drawn. + * This is known as frustum culling. + * It checks whether the object is far away, very close / behind the camera, + * or horizontally out of view. It does not check whether it is vertically + * out of view. It assumes a sphere of 300 units around the object's position + * unless the object has a culling radius node that specifies otherwise. + * + * The matrix parameter should be the top of the matrix stack, which is the + * object's transformation matrix times the camera 'look-at' matrix. The math + * is counter-intuitive, but it checks column 3 (translation vector) of this + * matrix to determine where the origin (0,0,0) in object space will be once + * transformed to camera space (x+ = right, y+ = up, z = 'coming out the screen'). + * In 3D graphics, you typically model the world as being moved in front of a + * static camera instead of a moving camera through a static world, which in + * this case simplifies calculations. Note that the perspective matrix is not + * on the matrix stack, so there are still calculations with the fov to compute + * the slope of the lines of the frustum. + * + * z- + * + * \ | / + * \ | / + * \ | / + * \ | / + * \ | / + * \|/ + * C x+ + * + * Since (0,0,0) is unaffected by rotation, columns 0, 1 and 2 are ignored. + */ +static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { + s16 cullingRadius; + s16 halfFov; // half of the fov in in-game angle units instead of degrees + struct GraphNode *geo; + f32 hScreenEdge; + + if (node->node.flags & GRAPH_RENDER_INVISIBLE) { + return FALSE; + } + + geo = node->sharedChild; + + // ! @bug The aspect ratio is not accounted for. When the fov value is 45, + // the horizontal effective fov is actually 60 degrees, so you can see objects + // visibly pop in or out at the edge of the screen. + halfFov = (gCurGraphNodeCamFrustum->fov / 2.0f + 1.0f) * 32768.0f / 180.0f + 0.5f; + + hScreenEdge = -matrix[3][2] * sins(halfFov) / coss(halfFov); + // -matrix[3][2] is the depth, which gets multiplied by tan(halfFov) to get + // the amount of units between the center of the screen and the horizontal edge + // given the distance from the object to the camera. + +#ifdef WIDESCREEN + // This multiplication should really be performed on 4:3 as well, + // but the issue will be more apparent on widescreen. + hScreenEdge *= GFX_DIMENSIONS_ASPECT_RATIO; +#endif + + if (geo != NULL && geo->type == GRAPH_NODE_TYPE_CULLING_RADIUS) { + cullingRadius = + (f32)((struct GraphNodeCullingRadius *) geo)->cullingRadius; //! Why is there a f32 cast? + } else { + cullingRadius = 300; + } + + // Don't render if the object is close to or behind the camera + if (matrix[3][2] > -100.0f + cullingRadius) { + return FALSE; + } + + //! This makes the HOLP not update when the camera is far away, and it + // makes PU travel safe when the camera is locked on the main map. + // If Mario were rendered with a depth over 65536 it would cause overflow + // when converting the transformation matrix to a fixed point matrix. + if (matrix[3][2] < -20000.0f - cullingRadius) { + return FALSE; + } + + // Check whether the object is horizontally in view + if (matrix[3][0] > hScreenEdge + cullingRadius) { + return FALSE; + } + if (matrix[3][0] < -hScreenEdge - cullingRadius) { + return FALSE; + } + return TRUE; +} + +/** + * Process an object node. + */ +static void geo_process_object(struct Object *node) { + Mat4 mtxf; + s32 hasAnimation = (node->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; + + if (node->header.gfx.areaIndex == gCurGraphNodeRoot->areaIndex) { + if (node->header.gfx.throwMatrix != NULL) { + mtxf_mul(gMatStack[gMatStackIndex + 1], *node->header.gfx.throwMatrix, + gMatStack[gMatStackIndex]); + } else if (node->header.gfx.node.flags & GRAPH_RENDER_BILLBOARD) { + mtxf_billboard(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex], + node->header.gfx.pos, gCurGraphNodeCamera->roll); + } else { + mtxf_rotate_zxy_and_translate(mtxf, node->header.gfx.pos, node->header.gfx.angle); + mtxf_mul(gMatStack[gMatStackIndex + 1], mtxf, gMatStack[gMatStackIndex]); + } + + mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], + node->header.gfx.scale); + node->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex]; + node->header.gfx.cameraToObject[0] = gMatStack[gMatStackIndex][3][0]; + node->header.gfx.cameraToObject[1] = gMatStack[gMatStackIndex][3][1]; + node->header.gfx.cameraToObject[2] = gMatStack[gMatStackIndex][3][2]; + + // FIXME: correct types + if (node->header.gfx.animInfo.curAnim != NULL) { + geo_set_animation_globals(&node->header.gfx.animInfo, hasAnimation); + } + if (obj_is_in_view(&node->header.gfx, gMatStack[gMatStackIndex])) { + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + if (node->header.gfx.sharedChild != NULL) { + gCurGraphNodeObject = (struct GraphNodeObject *) node; + node->header.gfx.sharedChild->parent = &node->header.gfx.node; + geo_process_node_and_siblings(node->header.gfx.sharedChild); + node->header.gfx.sharedChild->parent = NULL; + gCurGraphNodeObject = NULL; + } + if (node->header.gfx.node.children != NULL) { + geo_process_node_and_siblings(node->header.gfx.node.children); + } + } + + gMatStackIndex--; + gCurAnimType = ANIM_TYPE_NONE; + node->header.gfx.throwMatrix = NULL; + } +} + +/** + * Process an object parent node. Temporarily assigns itself as the parent of + * the subtree rooted at 'sharedChild' and processes the subtree, after which the + * actual children are be processed. (in practice they are null though) + */ +static void geo_process_object_parent(struct GraphNodeObjectParent *node) { + if (node->sharedChild != NULL) { + node->sharedChild->parent = (struct GraphNode *) node; + geo_process_node_and_siblings(node->sharedChild); + node->sharedChild->parent = NULL; + } + if (node->node.children != NULL) { + geo_process_node_and_siblings(node->node.children); + } +} + +/** + * Process a held object node. + */ +void geo_process_held_object(struct GraphNodeHeldObject *node) { + Mat4 mat; + Vec3f translation; + Mtx *mtx = alloc_display_list(sizeof(*mtx)); + +#ifdef F3DEX_GBI_2 + gSPLookAt(gDisplayListHead++, &lookAt); +#endif + + if (node->fnNode.func != NULL) { + node->fnNode.func(GEO_CONTEXT_RENDER, &node->fnNode.node, gMatStack[gMatStackIndex]); + } + if (node->objNode != NULL && node->objNode->header.gfx.sharedChild != NULL) { + s32 hasAnimation = (node->objNode->header.gfx.node.flags & GRAPH_RENDER_HAS_ANIMATION) != 0; + + translation[0] = node->translation[0] / 4.0f; + translation[1] = node->translation[1] / 4.0f; + translation[2] = node->translation[2] / 4.0f; + + mtxf_translate(mat, translation); + mtxf_copy(gMatStack[gMatStackIndex + 1], *gCurGraphNodeObject->throwMatrix); + gMatStack[gMatStackIndex + 1][3][0] = gMatStack[gMatStackIndex][3][0]; + gMatStack[gMatStackIndex + 1][3][1] = gMatStack[gMatStackIndex][3][1]; + gMatStack[gMatStackIndex + 1][3][2] = gMatStack[gMatStackIndex][3][2]; + mtxf_mul(gMatStack[gMatStackIndex + 1], mat, gMatStack[gMatStackIndex + 1]); + mtxf_scale_vec3f(gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], + node->objNode->header.gfx.scale); + if (node->fnNode.func != NULL) { + node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, + (struct AllocOnlyPool *) gMatStack[gMatStackIndex + 1]); + } + gMatStackIndex++; + mtxf_to_mtx(mtx, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = mtx; + gGeoTempState.type = gCurAnimType; + gGeoTempState.enabled = gCurAnimEnabled; + gGeoTempState.frame = gCurrAnimFrame; + gGeoTempState.translationMultiplier = gCurAnimTranslationMultiplier; + gGeoTempState.attribute = gCurrAnimAttribute; + gGeoTempState.data = gCurAnimData; + gCurAnimType = 0; + gCurGraphNodeHeldObject = (void *) node; + if (node->objNode->header.gfx.animInfo.curAnim != NULL) { + geo_set_animation_globals(&node->objNode->header.gfx.animInfo, hasAnimation); + } + + geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild); + gCurGraphNodeHeldObject = NULL; + gCurAnimType = gGeoTempState.type; + gCurAnimEnabled = gGeoTempState.enabled; + gCurrAnimFrame = gGeoTempState.frame; + gCurAnimTranslationMultiplier = gGeoTempState.translationMultiplier; + gCurrAnimAttribute = gGeoTempState.attribute; + gCurAnimData = gGeoTempState.data; + gMatStackIndex--; + } + + if (node->fnNode.node.children != NULL) { + geo_process_node_and_siblings(node->fnNode.node.children); + } +} + +/** + * Processes the children of the given GraphNode if it has any + */ +void geo_try_process_children(struct GraphNode *node) { + if (node->children != NULL) { + geo_process_node_and_siblings(node->children); + } +} + +/** + * Process a generic geo node and its siblings. + * The first argument is the start node, and all its siblings will + * be iterated over. + */ +void geo_process_node_and_siblings(struct GraphNode *firstNode) { + s16 iterateChildren = TRUE; + struct GraphNode *curGraphNode = firstNode; + struct GraphNode *parent = curGraphNode->parent; + + // In the case of a switch node, exactly one of the children of the node is + // processed instead of all children like usual + if (parent != NULL) { + iterateChildren = (parent->type != GRAPH_NODE_TYPE_SWITCH_CASE); + } + + do { + if (curGraphNode->flags & GRAPH_RENDER_ACTIVE) { + if (curGraphNode->flags & GRAPH_RENDER_CHILDREN_FIRST) { + geo_try_process_children(curGraphNode); + } else { + switch (curGraphNode->type) { + case GRAPH_NODE_TYPE_ORTHO_PROJECTION: + geo_process_ortho_projection((struct GraphNodeOrthoProjection *) curGraphNode); + break; + case GRAPH_NODE_TYPE_PERSPECTIVE: + geo_process_perspective((struct GraphNodePerspective *) curGraphNode); + break; + case GRAPH_NODE_TYPE_MASTER_LIST: + geo_process_master_list((struct GraphNodeMasterList *) curGraphNode); + break; + case GRAPH_NODE_TYPE_LEVEL_OF_DETAIL: + geo_process_level_of_detail((struct GraphNodeLevelOfDetail *) curGraphNode); + break; + case GRAPH_NODE_TYPE_SWITCH_CASE: + geo_process_switch((struct GraphNodeSwitchCase *) curGraphNode); + break; + case GRAPH_NODE_TYPE_CAMERA: + geo_process_camera((struct GraphNodeCamera *) curGraphNode); + break; + case GRAPH_NODE_TYPE_TRANSLATION_ROTATION: + geo_process_translation_rotation( + (struct GraphNodeTranslationRotation *) curGraphNode); + break; + case GRAPH_NODE_TYPE_TRANSLATION: + geo_process_translation((struct GraphNodeTranslation *) curGraphNode); + break; + case GRAPH_NODE_TYPE_ROTATION: + geo_process_rotation((struct GraphNodeRotation *) curGraphNode); + break; + case GRAPH_NODE_TYPE_OBJECT: + geo_process_object((struct Object *) curGraphNode); + break; + case GRAPH_NODE_TYPE_ANIMATED_PART: + geo_process_animated_part((struct GraphNodeAnimatedPart *) curGraphNode); + break; + case GRAPH_NODE_TYPE_BILLBOARD: + geo_process_billboard((struct GraphNodeBillboard *) curGraphNode); + break; + case GRAPH_NODE_TYPE_DISPLAY_LIST: + geo_process_display_list((struct GraphNodeDisplayList *) curGraphNode); + break; + case GRAPH_NODE_TYPE_SCALE: + geo_process_scale((struct GraphNodeScale *) curGraphNode); + break; + case GRAPH_NODE_TYPE_SHADOW: + geo_process_shadow((struct GraphNodeShadow *) curGraphNode); + break; + case GRAPH_NODE_TYPE_OBJECT_PARENT: + geo_process_object_parent((struct GraphNodeObjectParent *) curGraphNode); + break; + case GRAPH_NODE_TYPE_GENERATED_LIST: + geo_process_generated_list((struct GraphNodeGenerated *) curGraphNode); + break; + case GRAPH_NODE_TYPE_BACKGROUND: + geo_process_background((struct GraphNodeBackground *) curGraphNode); + break; + case GRAPH_NODE_TYPE_HELD_OBJ: + geo_process_held_object((struct GraphNodeHeldObject *) curGraphNode); + break; + default: + geo_try_process_children((struct GraphNode *) curGraphNode); + break; + } + } + } else { + if (curGraphNode->type == GRAPH_NODE_TYPE_OBJECT) { + ((struct GraphNodeObject *) curGraphNode)->throwMatrix = NULL; + } + } + } while (iterateChildren && (curGraphNode = curGraphNode->next) != firstNode); +} + +/** + * Process a root node. This is the entry point for processing the scene graph. + * The root node itself sets up the viewport, then all its children are processed + * to set up the projection and draw display lists. + */ +// void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor) { +// UNUSED s32 unused; +// +// if (node->node.flags & GRAPH_RENDER_ACTIVE) { +// Mtx *initialMatrix; +// Vp *viewport = alloc_display_list(sizeof(*viewport)); +// +// #ifdef USE_SYSTEM_MALLOC +// gDisplayListHeap = alloc_only_pool_init(); +// #else +// gDisplayListHeap = alloc_only_pool_init(main_pool_available() - sizeof(struct AllocOnlyPool), +// MEMORY_POOL_LEFT); +// #endif +// initialMatrix = alloc_display_list(sizeof(*initialMatrix)); +// gMatStackIndex = 0; +// gCurAnimType = 0; +// vec3s_set(viewport->vp.vtrans, node->x * 4, node->y * 4, 511); +// vec3s_set(viewport->vp.vscale, node->width * 4, node->height * 4, 511); +// +// // if (b != NULL) { +// // clear_frame_buffer(clearColor); +// // make_viewport_clip_rect(b); +// // *viewport = *b; +// // } +// // else if (c != NULL) { +// // clear_frame_buffer(clearColor); +// // make_viewport_clip_rect(c); +// // } +// +// mtxf_identity(gMatStack[gMatStackIndex]); +// mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]); +// gMatStackFixed[gMatStackIndex] = initialMatrix; +// gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(viewport)); +// gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), +// G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); +// gCurGraphNodeRoot = node; +// if (node->node.children != NULL) { +// geo_process_node_and_siblings(node->node.children); +// } +// gCurGraphNodeRoot = NULL; +// // if (gShowDebugText) { +// //#ifndef USE_SYSTEM_MALLOC +// // print_text_fmt_int(180, 36, "MEM %d", +// // gDisplayListHeap->totalSpace - gDisplayListHeap->usedSpace); +// //#endif +// // } +// main_pool_free(gDisplayListHeap); +// } +// } + +void geo_process_root_hack_single_node(struct GraphNode *node) +{ + gDisplayListHead = NULL; // Currently unused, but referenced + + Mtx *initialMatrix; + + gDisplayListHeap = alloc_only_pool_init(); + initialMatrix = alloc_display_list(sizeof(*initialMatrix)); + gMatStackIndex = 0; + gCurAnimType = 0; + + mtxf_identity(gMatStack[gMatStackIndex]); + mtxf_to_mtx(initialMatrix, gMatStack[gMatStackIndex]); + gMatStackFixed[gMatStackIndex] = initialMatrix; + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(gMatStackFixed[gMatStackIndex]), + G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + + // Hacked in from geo_proces_object since we only have Mario + //geo_process_object( node ); + if (gMarioObject->header.gfx.throwMatrix != NULL) { + mtxf_mul(gMatStack[gMatStackIndex + 1], *gMarioObject->header.gfx.throwMatrix, gMatStack[gMatStackIndex]); + mtxf_scale_vec3f( gMatStack[gMatStackIndex + 1], gMatStack[gMatStackIndex + 1], gMarioObject->header.gfx.scale ); + gMarioObject->header.gfx.throwMatrix = &gMatStack[++gMatStackIndex]; + } + else { + Mat4 identity, scale, rotTran; + mtxf_identity( identity ); + mtxf_scale_vec3f( scale, identity, gMarioObject->header.gfx.scale ); + mtxf_rotate_zxy_and_translate( rotTran, gMarioObject->header.gfx.pos, gMarioObject->header.gfx.angle ); + mtxf_mul( gMatStack[++gMatStackIndex], scale, rotTran ); + } + geo_set_animation_globals(&gMarioObject->header.gfx.animInfo, 1); + + gCurGraphNodeRoot = (struct GraphNodeRoot *)node; + if (node->children != NULL) { + geo_process_node_and_siblings(node->children); + } + gCurGraphNodeRoot = NULL; + + gMarioObject->header.gfx.throwMatrix = NULL; + + main_pool_free(gDisplayListHeap); +} \ No newline at end of file diff --git a/src/game/rendering_graph_node.h b/src/game/rendering_graph_node.h new file mode 100644 index 0000000..9d7ce6d --- /dev/null +++ b/src/game/rendering_graph_node.h @@ -0,0 +1,34 @@ +#ifndef RENDERING_GRAPH_NODE_H +#define RENDERING_GRAPH_NODE_H + +#include "../include/PR/ultratypes.h" + +#include "../engine/graph_node.h" + +extern struct GraphNodeRoot *gCurGraphNodeRoot; +extern struct GraphNodeMasterList *gCurGraphNodeMasterList; +extern struct GraphNodePerspective *gCurGraphNodeCamFrustum; +extern struct GraphNodeCamera *gCurGraphNodeCamera; +extern struct GraphNodeObject *gCurGraphNodeObject; +extern struct GraphNodeHeldObject *gCurGraphNodeHeldObject; +extern u16 gAreaUpdateCounter; + +// after processing an object, the type is reset to this +#define ANIM_TYPE_NONE 0 + +// Not all parts have full animation: to save space, some animations only +// have xz, y, or no translation at all. All animations have rotations though +#define ANIM_TYPE_TRANSLATION 1 +#define ANIM_TYPE_VERTICAL_TRANSLATION 2 +#define ANIM_TYPE_LATERAL_TRANSLATION 3 +#define ANIM_TYPE_NO_TRANSLATION 4 + +// Every animation includes rotation, after processing any of the above +// translation types the type is set to this +#define ANIM_TYPE_ROTATION 5 + +void geo_process_node_and_siblings(struct GraphNode *firstNode); +//void geo_process_root(struct GraphNodeRoot *node, Vp *b, Vp *c, s32 clearColor); +void geo_process_root_hack_single_node(struct GraphNode *node); + +#endif // RENDERING_GRAPH_NODE_H diff --git a/src/game/save_file.h b/src/game/save_file.h new file mode 100644 index 0000000..4cafb32 --- /dev/null +++ b/src/game/save_file.h @@ -0,0 +1,175 @@ +#ifndef SAVE_FILE_H +#define SAVE_FILE_H + +#include "../include/PR/ultratypes.h" + +#include "../include/types.h" +#include "area.h" + +// #include "course_table.h" +// ===== PATCH ===== +#define COURSE_COUNT 120 +#define COURSE_STAGES_COUNT 15 +// ================= + +#define EEPROM_SIZE 0x200 +#define NUM_SAVE_FILES 4 + +struct SaveBlockSignature +{ + u16 magic; + u16 chksum; +}; + +struct SaveFile +{ + // Location of lost cap. + // Note: the coordinates get set, but are never actually used, since the + // cap can always be found in a fixed spot within the course + u8 capLevel; + u8 capArea; + Vec3s capPos; + + u32 flags; + + // Star flags for each course. + // The most significant bit of the byte *following* each course is set if the + // cannon is open. + u8 courseStars[COURSE_COUNT]; + + u8 courseCoinScores[COURSE_STAGES_COUNT]; + + struct SaveBlockSignature signature; +}; + +enum SaveFileIndex { + SAVE_FILE_A, + SAVE_FILE_B, + SAVE_FILE_C, + SAVE_FILE_D +}; + +struct MainMenuSaveData +{ + // Each save file has a 2 bit "age" for each course. The higher this value, + // the older the high score is. This is used for tie-breaking when displaying + // on the high score screen. + u32 coinScoreAges[NUM_SAVE_FILES]; + u16 soundMode; + +#ifdef VERSION_EU + u16 language; +#define SUBTRAHEND 8 +#else +#define SUBTRAHEND 6 +#endif + + // Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU) + u8 filler[ 128 ]; // EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; + + struct SaveBlockSignature signature; +}; + +struct SaveBuffer +{ + // Each of the four save files has two copies. If one is bad, the other is used as a backup. + struct SaveFile files[NUM_SAVE_FILES][2]; + // The main menu data has two copies. If one is bad, the other is used as a backup. +// struct MainMenuSaveData menuData[0]; +}; + +extern u8 gLastCompletedCourseNum; +extern u8 gLastCompletedStarNum; +extern s8 sUnusedGotGlobalCoinHiScore; +extern u8 gGotFileCoinHiScore; +extern u8 gCurrCourseStarFlags; +extern u8 gSpecialTripleJump; +extern s8 gLevelToCourseNumTable[]; + +// game progress flags +#define SAVE_FLAG_FILE_EXISTS /* 0x00000001 */ (1 << 0) +#define SAVE_FLAG_HAVE_WING_CAP /* 0x00000002 */ (1 << 1) +#define SAVE_FLAG_HAVE_METAL_CAP /* 0x00000004 */ (1 << 2) +#define SAVE_FLAG_HAVE_VANISH_CAP /* 0x00000008 */ (1 << 3) +#define SAVE_FLAG_HAVE_KEY_1 /* 0x00000010 */ (1 << 4) +#define SAVE_FLAG_HAVE_KEY_2 /* 0x00000020 */ (1 << 5) +#define SAVE_FLAG_UNLOCKED_BASEMENT_DOOR /* 0x00000040 */ (1 << 6) +#define SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR /* 0x00000080 */ (1 << 7) +#define SAVE_FLAG_DDD_MOVED_BACK /* 0x00000100 */ (1 << 8) +#define SAVE_FLAG_MOAT_DRAINED /* 0x00000200 */ (1 << 9) +#define SAVE_FLAG_UNLOCKED_PSS_DOOR /* 0x00000400 */ (1 << 10) +#define SAVE_FLAG_UNLOCKED_WF_DOOR /* 0x00000800 */ (1 << 11) +#define SAVE_FLAG_UNLOCKED_CCM_DOOR /* 0x00001000 */ (1 << 12) +#define SAVE_FLAG_UNLOCKED_JRB_DOOR /* 0x00002000 */ (1 << 13) +#define SAVE_FLAG_UNLOCKED_BITDW_DOOR /* 0x00004000 */ (1 << 14) +#define SAVE_FLAG_UNLOCKED_BITFS_DOOR /* 0x00008000 */ (1 << 15) +#define SAVE_FLAG_CAP_ON_GROUND /* 0x00010000 */ (1 << 16) +#define SAVE_FLAG_CAP_ON_KLEPTO /* 0x00020000 */ (1 << 17) +#define SAVE_FLAG_CAP_ON_UKIKI /* 0x00040000 */ (1 << 18) +#define SAVE_FLAG_CAP_ON_MR_BLIZZARD /* 0x00080000 */ (1 << 19) +#define SAVE_FLAG_UNLOCKED_50_STAR_DOOR /* 0x00100000 */ (1 << 20) +#define SAVE_FLAG_COLLECTED_TOAD_STAR_1 /* 0x01000000 */ (1 << 24) +#define SAVE_FLAG_COLLECTED_TOAD_STAR_2 /* 0x02000000 */ (1 << 25) +#define SAVE_FLAG_COLLECTED_TOAD_STAR_3 /* 0x04000000 */ (1 << 26) +#define SAVE_FLAG_COLLECTED_MIPS_STAR_1 /* 0x08000000 */ (1 << 27) +#define SAVE_FLAG_COLLECTED_MIPS_STAR_2 /* 0x10000000 */ (1 << 28) + +#define SAVE_FLAG_TO_STAR_FLAG(cmd) (((cmd) >> 24) & 0x7F) +#define STAR_FLAG_TO_SAVE_FLAG(cmd) ((cmd) << 24) + +// Variable for setting a warp checkpoint. + +// possibly a WarpDest struct where arg is a union. TODO: Check? +struct WarpCheckpoint { + /*0x00*/ u8 actNum; + /*0x01*/ u8 courseNum; + /*0x02*/ u8 levelID; + /*0x03*/ u8 areaNum; + /*0x04*/ u8 warpNode; +}; + +extern struct WarpCheckpoint gWarpCheckpoint; + +extern s8 gMainMenuDataModified; +extern s8 gSaveFileModified; + +void save_file_do_save(s32 fileIndex); +void save_file_erase(s32 fileIndex); +BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex); +void save_file_load_all(void); +void save_file_reload(void); +void save_file_collect_star_or_key(s16 coinScore, s16 starIndex); +s32 save_file_exists(s32 fileIndex); +u32 save_file_get_max_coin_score(s32 courseIndex); +s32 save_file_get_course_star_count(s32 fileIndex, s32 courseIndex); +s32 save_file_get_total_star_count(s32 fileIndex, s32 minCourse, s32 maxCourse); +void save_file_set_flags(u32 flags); +void save_file_clear_flags(u32 flags); +u32 save_file_get_flags(void); +u32 save_file_get_star_flags(s32 fileIndex, s32 courseIndex); +void save_file_set_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlags); +s32 save_file_get_course_coin_score(s32 fileIndex, s32 courseIndex); +s32 save_file_is_cannon_unlocked(void); +void save_file_set_cannon_unlocked(void); +void save_file_set_cap_pos(s16 x, s16 y, s16 z); +s32 save_file_get_cap_pos(Vec3s capPos); +void save_file_set_sound_mode(u16 mode); +u16 save_file_get_sound_mode(void); +void save_file_move_cap_to_default_location(void); + +void disable_warp_checkpoint(void); +void check_if_should_set_warp_checkpoint(struct WarpNode *warpNode); +s32 check_warp_checkpoint(struct WarpNode *warpNode); + +#ifdef VERSION_EU +enum EuLanguages { + LANGUAGE_ENGLISH, + LANGUAGE_FRENCH, + LANGUAGE_GERMAN +}; + +void eu_set_language(u16 language); +u16 eu_get_language(void); +#endif + +#endif // SAVE_FILE_H diff --git a/src/gfx_adapter.c b/src/gfx_adapter.c new file mode 100644 index 0000000..4c47190 --- /dev/null +++ b/src/gfx_adapter.c @@ -0,0 +1,145 @@ +#include +#include + +#include "libsm64.h" +#include "engine/math_util.h" +#include "guMtxF2L.h" +#include "gfx_adapter.h" +#include "gfx_adapter_commands.h" + +static Mat4 s_curMatrix; +static float s_curColor[3]; +static struct SM64MarioGeometryBuffers *s_outBuffers; +static float *s_trianglePtr; +static float *s_colorPtr; +static float *s_normalPtr; + +static void mtxf_mul_vec3f(Mat4 mtx, Vec3f b, float w, Vec3f out) +{ + out[0] = b[0] * mtx[0][0] + b[1] * mtx[1][0] + b[2] * mtx[2][0] + w * mtx[3][0]; + out[1] = b[0] * mtx[0][1] + b[1] * mtx[1][1] + b[2] * mtx[2][1] + w * mtx[3][1]; + out[2] = b[0] * mtx[0][2] + b[1] * mtx[1][2] + b[2] * mtx[2][2] + w * mtx[3][2]; +} + +static void process_display_list( void *dl ) +{ + int64_t *ptr = (int64_t *)dl; + Vtx *vdata = NULL; + + for( ;; ) + { + switch( *ptr++ ) + { + case GFXCMD_VertexData: + { + UNUSED int64_t v = *ptr++; + UNUSED int64_t n = *ptr++; + UNUSED int64_t v0 = *ptr++; + vdata = (Vtx*)v; + break; + } + + case GFXCMD_Triangle: + { + int64_t v00 = *ptr++; + int64_t v01 = *ptr++; + int64_t v02 = *ptr++; + UNUSED int64_t flag0 = *ptr++; + + short x0 = vdata[v00].v.ob[0], y0 = vdata[v00].v.ob[1], z0 = vdata[v00].v.ob[2]; + short x1 = vdata[v01].v.ob[0], y1 = vdata[v01].v.ob[1], z1 = vdata[v01].v.ob[2]; + short x2 = vdata[v02].v.ob[0], y2 = vdata[v02].v.ob[1], z2 = vdata[v02].v.ob[2]; + Vec3f p0 = { (float)x0, (float)y0, (float)z0 }; + Vec3f p1 = { (float)x1, (float)y1, (float)z1 }; + Vec3f p2 = { (float)x2, (float)y2, (float)z2 }; + + signed char nx0 = vdata[v00].n.n[0], ny0 = vdata[v00].n.n[1], nz0 = vdata[v00].n.n[2]; + signed char nx1 = vdata[v01].n.n[0], ny1 = vdata[v01].n.n[1], nz1 = vdata[v01].n.n[2]; + signed char nx2 = vdata[v02].n.n[0], ny2 = vdata[v02].n.n[1], nz2 = vdata[v02].n.n[2]; + Vec3f n0 = { ((float)nx0) / 128.0f, ((float)ny0) / 128.0f, ((float)nz0) / 128.0f }; + Vec3f n1 = { ((float)nx1) / 128.0f, ((float)ny1) / 128.0f, ((float)nz1) / 128.0f }; + Vec3f n2 = { ((float)nx2) / 128.0f, ((float)ny2) / 128.0f, ((float)nz2) / 128.0f }; + + mtxf_mul_vec3f( s_curMatrix, p0, 1.0f, s_trianglePtr ); + s_trianglePtr += 3; + mtxf_mul_vec3f( s_curMatrix, p1, 1.0f, s_trianglePtr ); + s_trianglePtr += 3; + mtxf_mul_vec3f( s_curMatrix, p2, 1.0f, s_trianglePtr ); + s_trianglePtr += 3; + + // TODO normals arent correct under non-uniform scale. multiple by inverse/transpose + mtxf_mul_vec3f( s_curMatrix, n0, 0.0f, s_normalPtr ); + vec3f_normalize( s_normalPtr ); + s_normalPtr += 3; + mtxf_mul_vec3f( s_curMatrix, n1, 0.0f, s_normalPtr ); + vec3f_normalize( s_normalPtr ); + s_normalPtr += 3; + mtxf_mul_vec3f( s_curMatrix, n2, 0.0f, s_normalPtr ); + vec3f_normalize( s_normalPtr ); + s_normalPtr += 3; + + *s_colorPtr++ = s_curColor[0]; + *s_colorPtr++ = s_curColor[1]; + *s_colorPtr++ = s_curColor[2]; + *s_colorPtr++ = s_curColor[0]; + *s_colorPtr++ = s_curColor[1]; + *s_colorPtr++ = s_curColor[2]; + *s_colorPtr++ = s_curColor[0]; + *s_colorPtr++ = s_curColor[1]; + *s_colorPtr++ = s_curColor[2]; + + break; + } + + case GFXCMD_Light: + { + int64_t l = *ptr++; + int64_t n = *ptr++; + + if( n == 1 ) + { + Light *data = (Light*)l; + s_curColor[0] = (float)data->l.col[0] / 255.0f; + s_curColor[1] = (float)data->l.col[1] / 255.0f; + s_curColor[2] = (float)data->l.col[2] / 255.0f; + } + + break; + } + + case GFXCMD_SubDisplayList: + { + int64_t dl = *ptr++; + process_display_list( (void*)dl ); + break; + } + + case GFXCMD_EndDisplayList: + goto break_top; + } + } + + s_outBuffers->bufferUsedSize = s_trianglePtr - s_outBuffers->position; + +break_top: + {} +} + +void gSPMatrix( void *pkt, Mtx *m, uint8_t flags ) +{ + guMtxL2F( s_curMatrix, m ); +} + +void gSPDisplayList( void *pkt, struct DisplayListNode *dl ) +{ + process_display_list( (void*)dl ); +} + +void gfx_adapter_bind_output_buffers( struct SM64MarioGeometryBuffers *outBuffers ) +{ + s_outBuffers = outBuffers; + s_trianglePtr = s_outBuffers->position; + s_colorPtr = s_outBuffers->color; + s_normalPtr = s_outBuffers->normal; + s_outBuffers->bufferUsedSize = 0; +} \ No newline at end of file diff --git a/src/gfx_adapter.h b/src/gfx_adapter.h new file mode 100644 index 0000000..c758c65 --- /dev/null +++ b/src/gfx_adapter.h @@ -0,0 +1,21 @@ +#pragma once + +#include "engine/graph_node.h" +#include "libsm64.h" + +// Commented out in gbi.h - Replaced here +#define gDPPipeSync(pkt) ({}) +#define gSPSetGeometryMode(pkt, word) ({}) +#define gDPSetRenderMode(pkt, c0, c1) ({}) +#define gSPClearGeometryMode(pkt, word) ({}) +#define gSPPerspNormalize(pkt, s) ({}) +#define gDPFillRectangle(pkt, ulx, uly, lrx, lry) ({}) +#define gSPViewport(pkt, v) ({}) +extern void gSPMatrix( void *pkt, Mtx *m, uint8_t flags ); +extern void gSPDisplayList( void *pkt, struct DisplayListNode *dl ); + +// from lib/src/gu*.c +#define guOrtho(mtx, left, right, bottom, top, a, b, c) ({}) +#define guPerspective(mtx, perspNorm, fov, aspect, near, far, a) ({}) + +extern void gfx_adapter_bind_output_buffers( struct SM64MarioGeometryBuffers *outBuffers ); \ No newline at end of file diff --git a/src/gfx_adapter_commands.h b/src/gfx_adapter_commands.h new file mode 100644 index 0000000..b84f79a --- /dev/null +++ b/src/gfx_adapter_commands.h @@ -0,0 +1,11 @@ +#pragma once + +enum GFXAdapterCommands +{ + GFXCMD_None = 0, + GFXCMD_VertexData, + GFXCMD_Triangle, + GFXCMD_Light, + GFXCMD_SubDisplayList, + GFXCMD_EndDisplayList, +}; \ No newline at end of file diff --git a/src/guMtxF2L.c b/src/guMtxF2L.c new file mode 100644 index 0000000..7d89761 --- /dev/null +++ b/src/guMtxF2L.c @@ -0,0 +1,69 @@ +#include "guMtxF2L.h" + +#ifdef GBI_FLOATS +#include +#endif + +#ifndef GBI_FLOATS +void guMtxF2L(float mf[4][4], Mtx *m) { + int r, c; + s32 tmp1; + s32 tmp2; + s32 *m1 = &m->m[0][0]; + s32 *m2 = &m->m[2][0]; + for (r = 0; r < 4; r++) { + for (c = 0; c < 2; c++) { + tmp1 = mf[r][2 * c] * 65536.0f; + tmp2 = mf[r][2 * c + 1] * 65536.0f; + *m1++ = (tmp1 & 0xffff0000) | ((tmp2 >> 0x10) & 0xffff); + *m2++ = ((tmp1 << 0x10) & 0xffff0000) | (tmp2 & 0xffff); + } + } +} +void guMtxL2F(float mf[4][4], Mtx *m) { + int r, c; + u32 tmp1; + u32 tmp2; + u32 *m1; + u32 *m2; + s32 stmp1, stmp2; + m1 = (u32 *) &m->m[0][0]; + m2 = (u32 *) &m->m[2][0]; + for (r = 0; r < 4; r++) { + for (c = 0; c < 2; c++) { + tmp1 = (*m1 & 0xffff0000) | ((*m2 >> 0x10) & 0xffff); + tmp2 = ((*m1++ << 0x10) & 0xffff0000) | (*m2++ & 0xffff); + stmp1 = *(s32 *) &tmp1; + stmp2 = *(s32 *) &tmp2; + mf[r][c * 2 + 0] = stmp1 / 65536.0f; + mf[r][c * 2 + 1] = stmp2 / 65536.0f; + } + } +} +#else +void guMtxF2L(float mf[4][4], Mtx *m) { + memcpy(m, mf, sizeof(Mtx)); +} +#endif + +void guMtxIdentF(float mf[4][4]) { + int r, c; + for (r = 0; r < 4; r++) { + for (c = 0; c < 4; c++) { + if (r == c) { + mf[r][c] = 1.0f; + } else { + mf[r][c] = 0.0f; + } + } + } +} +void guMtxIdent(Mtx *m) { +#ifndef GBI_FLOATS + float mf[4][4]; + guMtxIdentF(mf); + guMtxF2L(mf, m); +#else + guMtxIdentF(m->m); +#endif +} diff --git a/src/guMtxF2L.h b/src/guMtxF2L.h new file mode 100644 index 0000000..751cc07 --- /dev/null +++ b/src/guMtxF2L.h @@ -0,0 +1,6 @@ +#pragma once + +#include "include/PR/gbi.h" + +extern void guMtxF2L(float mf[4][4], Mtx *m); +extern void guMtxL2F(float mf[4][4], Mtx *m); \ No newline at end of file diff --git a/src/include/PR/gbi.h b/src/include/PR/gbi.h new file mode 100644 index 0000000..7bd7a70 --- /dev/null +++ b/src/include/PR/gbi.h @@ -0,0 +1,4850 @@ +/************************************************************************** + * * + * Copyright (C) 1994, Silicon Graphics, Inc. * + * * + * These coded instructions, statements, and computer programs contain * + * unpublished proprietary information of Silicon Graphics, Inc., and * + * are protected by Federal copyright law. They may not be disclosed * + * to third parties or copied or duplicated in any form, in whole or * + * in part, without the prior written consent of Silicon Graphics, Inc. * + * * + **************************************************************************/ +/************************************************************************** + * + * $Revision: 1.141 $ + * $Date: 1999/09/03 03:43:08 $ + * $Source: /exdisk2/cvs/N64OS/Master/cvsmdev2/PR/include/gbi.h,v $ + * + **************************************************************************/ + +#ifndef _GBI_H_ +#define _GBI_H_ + +#define _LANGUAGE_C // PATCH +#include "ultratypes.h" + + + +// FROM mbi.h +/* + * the SHIFT macros are used to build display list commands, inserting + * bit-fields into a 32-bit word. They take a value, a shift amount, + * and a width. + * + * For the left shift, the lower bits of the value are masked, + * then shifted left. + * + * For the right shift, the value is shifted right, then the lower bits + * are masked. + * + * (NOTE: _SHIFTL(v, 0, 32) won't work, just use an assignment) + * + */ +#define _SHIFTL(v, s, w) \ + ((unsigned int) (((unsigned int)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((unsigned int)(((unsigned int)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#define _SHIFT _SHIFTL /* old, for compatibility only */ + +#define G_ON (1) +#define G_OFF (0) + + + + + +/* + * To use the F3DEX ucodes, define F3DEX_GBI before include this file. + * + * #define F3DEX_GBI + * #include + * + * or + * + * cc -c -DF3DEX_GBI -I.... foo.c + * + */ + +/************************************************************************** + * + * Graphics Binary Interface + * + **************************************************************************/ + +/* + * Graphics Commands, 'xxx' parts may be generated from ucode + * + * The command format is + * + * |00xxxxxx| = DMA 0,..,127 + * |10xxxxxx| = Immediate Mode -65,..,-128 + * |11xxxxxx| = RDP cmds -1,..,-64 + * + * Note: in order for the RSP microcode to process RDP commands opaquely, + * we need to further identify those RDP commands that need DRAM address + * "fixup". To do this, we have the dummy command G_RDP_ADDR_FIXUP, and + * all |RDP commands| less than this are commands with embedded DRAM + * addresses. Further, the format of these commands should be similar so + * only one fixup routine is needed. + * + * Further explanation: + * The names of the commands are somewhat misleading. Here is clarification: + * + * - a 'DMA' type command has a pointer to additional data and + * causes a DMA transfer to bring that into DMEM. + * + * - an 'Immediate' type command isn't really 'immediate', in the + * traditional sense. This just means that the entire command fits + * in the 64-bit word, and the ucode can execute it 'immediately' + * without additional memory transfers. + * + * - an 'RDP' command is identified as such because the RDP + * commands can be passed-thru the RSP and sent to the RDP + * directly. One further confusing thing, is that some 'DP' + * macros below actually generate immediate commands, not + * not direct DP commands. + * + * IMPLEMENTATION NOTE: + * There is another group of RDP commands that includes the triangle commands + * generated by the RSP code. These are the raw commands the rasterizer + * hardware chews on, with slope info, etc. They will follow the RDP + * ordering... + * + * IMPLEMENTATION NOTE: + * The RDP hardware has some of these bit patterns wired up. If the hardware + * changes, we must adjust this table, likewise we can't change/add things + * once the hardware is frozen. (actually, the RDP hardware only looks at + * the lower 6 bits of the command byte) + * + */ + +#ifdef F3DEX_GBI_2E +# ifndef F3DEX_GBI_2 +# define F3DEX_GBI_2 +# endif +# define GBI_FLOATS +#endif + +#ifdef F3DEX_GBI_2 +# ifndef F3DEX_GBI +# define F3DEX_GBI +# endif +#define G_NOOP 0x00 +#define G_RDPHALF_2 0xf1 +#define G_SETOTHERMODE_H 0xe3 +#define G_SETOTHERMODE_L 0xe2 +#define G_RDPHALF_1 0xe1 +#define G_SPNOOP 0xe0 +#define G_ENDDL 0xdf +#define G_DL 0xde +#define G_LOAD_UCODE 0xdd +#define G_MOVEMEM 0xdc +#define G_MOVEWORD 0xdb +#define G_MTX 0xda +#define G_GEOMETRYMODE 0xd9 +#define G_POPMTX 0xd8 +#define G_TEXTURE 0xd7 +#define G_DMA_IO 0xd6 +#define G_SPECIAL_1 0xd5 +#define G_SPECIAL_2 0xd4 +#define G_SPECIAL_3 0xd3 + +#define G_VTX 0x01 +#define G_MODIFYVTX 0x02 +#define G_CULLDL 0x03 +#define G_BRANCH_Z 0x04 +#define G_TRI1 0x05 +#define G_TRI2 0x06 +#define G_QUAD 0x07 +#define G_LINE3D 0x08 +#else /* F3DEX_GBI_2 */ + +/* DMA commands: */ +#define G_SPNOOP 0 /* handle 0 gracefully */ +#define G_MTX 1 +#define G_RESERVED0 2 /* not implemeted */ +#define G_MOVEMEM 3 /* move a block of memory (up to 4 words) to dmem */ +#define G_VTX 4 +#define G_RESERVED1 5 /* not implemeted */ +#define G_DL 6 +#define G_RESERVED2 7 /* not implemeted */ +#define G_RESERVED3 8 /* not implemeted */ +#define G_SPRITE2D_BASE 9 /* sprite command */ + +/* IMMEDIATE commands: */ +#define G_IMMFIRST -65 +#define G_TRI1 (G_IMMFIRST-0) +#define G_CULLDL (G_IMMFIRST-1) +#define G_POPMTX (G_IMMFIRST-2) +#define G_MOVEWORD (G_IMMFIRST-3) +#define G_TEXTURE (G_IMMFIRST-4) +#define G_SETOTHERMODE_H (G_IMMFIRST-5) +#define G_SETOTHERMODE_L (G_IMMFIRST-6) +#define G_ENDDL (G_IMMFIRST-7) +#define G_SETGEOMETRYMODE (G_IMMFIRST-8) +#define G_CLEARGEOMETRYMODE (G_IMMFIRST-9) +#define G_LINE3D (G_IMMFIRST-10) +#define G_RDPHALF_1 (G_IMMFIRST-11) +#define G_RDPHALF_2 (G_IMMFIRST-12) +#if (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +# define G_MODIFYVTX (G_IMMFIRST-13) +# define G_TRI2 (G_IMMFIRST-14) +# define G_BRANCH_Z (G_IMMFIRST-15) +# define G_LOAD_UCODE (G_IMMFIRST-16) +#else +# define G_RDPHALF_CONT (G_IMMFIRST-13) +#endif + +/* We are overloading 2 of the immediate commands + to keep the byte alignment of dmem the same */ + +#define G_SPRITE2D_SCALEFLIP (G_IMMFIRST-1) +#define G_SPRITE2D_DRAW (G_IMMFIRST-2) + +/* RDP commands: */ +#define G_NOOP 0xc0 /* 0 */ + +#endif /* F3DEX_GBI_2 */ + +/* RDP commands: */ +#define G_SETCIMG 0xff /* -1 */ +#define G_SETZIMG 0xfe /* -2 */ +#define G_SETTIMG 0xfd /* -3 */ +#define G_SETCOMBINE 0xfc /* -4 */ +#define G_SETENVCOLOR 0xfb /* -5 */ +#define G_SETPRIMCOLOR 0xfa /* -6 */ +#define G_SETBLENDCOLOR 0xf9 /* -7 */ +#define G_SETFOGCOLOR 0xf8 /* -8 */ +#define G_SETFILLCOLOR 0xf7 /* -9 */ +#define G_FILLRECT 0xf6 /* -10 */ +#define G_SETTILE 0xf5 /* -11 */ +#define G_LOADTILE 0xf4 /* -12 */ +#define G_LOADBLOCK 0xf3 /* -13 */ +#define G_SETTILESIZE 0xf2 /* -14 */ +#define G_LOADTLUT 0xf0 /* -16 */ +#define G_RDPSETOTHERMODE 0xef /* -17 */ +#define G_SETPRIMDEPTH 0xee /* -18 */ +#define G_SETSCISSOR 0xed /* -19 */ +#define G_SETCONVERT 0xec /* -20 */ +#define G_SETKEYR 0xeb /* -21 */ +#define G_SETKEYGB 0xea /* -22 */ +#define G_RDPFULLSYNC 0xe9 /* -23 */ +#define G_RDPTILESYNC 0xe8 /* -24 */ +#define G_RDPPIPESYNC 0xe7 /* -25 */ +#define G_RDPLOADSYNC 0xe6 /* -26 */ +#define G_TEXRECTFLIP 0xe5 /* -27 */ +#define G_TEXRECT 0xe4 /* -28 */ + + +/* + * The following commands are the "generated" RDP commands; the user + * never sees them, the RSP microcode generates them. + * + * The layout of the bits is magical, to save work in the ucode. + * These id's are -56, -52, -54, -50, -55, -51, -53, -49, ... + * edge, shade, texture, zbuff bits: estz + */ +#define G_TRI_FILL 0xc8 /* fill triangle: 11001000 */ +#define G_TRI_SHADE 0xcc /* shade triangle: 11001100 */ +#define G_TRI_TXTR 0xca /* texture triangle: 11001010 */ +#define G_TRI_SHADE_TXTR 0xce /* shade, texture triangle: 11001110 */ +#define G_TRI_FILL_ZBUFF 0xc9 /* fill, zbuff triangle: 11001001 */ +#define G_TRI_SHADE_ZBUFF 0xcd /* shade, zbuff triangle: 11001101 */ +#define G_TRI_TXTR_ZBUFF 0xcb /* texture, zbuff triangle: 11001011 */ +#define G_TRI_SHADE_TXTR_ZBUFF 0xcf /* shade, txtr, zbuff trngl: 11001111 */ + +/* + * A TRI_FILL triangle is just the edges. You need to set the DP + * to use primcolor, in order to see anything. (it is NOT a triangle + * that gets rendered in 'fill mode'. Triangles can't be rendered + * in 'fill mode') + * + * A TRI_SHADE is a gouraud triangle that has colors interpolated. + * Flat-shaded triangles (from the software) are still gouraud shaded, + * it's just the colors are all the same and the deltas are 0. + * + * Other triangle types, and combinations are more obvious. + */ + +/* masks to build RDP triangle commands: */ +#define G_RDP_TRI_FILL_MASK 0x08 +#define G_RDP_TRI_SHADE_MASK 0x04 +#define G_RDP_TRI_TXTR_MASK 0x02 +#define G_RDP_TRI_ZBUFF_MASK 0x01 + +/* + * HACK: + * This is a dreadful hack. For version 1.0 hardware, there are still + * some 'bowtie' hangs. This parameter can be increased to avoid + * the hangs. Every increase of 4 chops one scanline off of every + * triangle. Values of 4,8,12 should be sufficient to avoid any + * bowtie hang. + * + * Change this value, then recompile ALL of your program (including static + * display lists!) + * + * THIS WILL BE REMOVED FOR HARDWARE VERSION 2.0! + */ +#define BOWTIE_VAL 0 + + +/* gets added to RDP command, in order to test for addres fixup: */ +#define G_RDP_ADDR_FIXUP 3 /* |RDP cmds| <= this, do addr fixup */ +#ifdef _LANGUAGE_ASSEMBLY +#define G_RDP_TEXRECT_CHECK ((-1*G_TEXRECTFLIP)& 0xff) +#endif + +/* macros for command parsing: */ +#define GDMACMD(x) (x) +#define GIMMCMD(x) (G_IMMFIRST-(x)) +#define GRDPCMD(x) (0xff-(x)) + +#define G_DMACMDSIZ 128 +#define G_IMMCMDSIZ 64 +#define G_RDPCMDSIZ 64 + +/* + * Coordinate shift values, number of bits of fraction + */ +#define G_TEXTURE_IMAGE_FRAC 2 +#define G_TEXTURE_SCALE_FRAC 16 +#define G_SCALE_FRAC 8 +#define G_ROTATE_FRAC 16 + +/* + * Parameters to graphics commands + */ + +/* + * Data packing macros + */ + +/* + * Maximum z-buffer value, used to initialize the z-buffer. + * Note : this number is NOT the viewport z-scale constant. + * See the comment next to G_MAXZ for more info. + */ +#define G_MAXFBZ 0x3fff /* 3b exp, 11b mantissa */ + +#define GPACK_RGBA5551(r, g, b, a) ((((r)<<8) & 0xf800) | \ + (((g)<<3) & 0x7c0) | \ + (((b)>>2) & 0x3e) | ((a) & 0x1)) +#define GPACK_ZDZ(z, dz) ((z) << 2 | (dz)) + +/* + * G_MTX: parameter flags + */ +#ifdef F3DEX_GBI_2 +# define G_MTX_MODELVIEW 0x00 /* matrix types */ +# define G_MTX_PROJECTION 0x04 +# define G_MTX_MUL 0x00 /* concat or load */ +# define G_MTX_LOAD 0x02 +# define G_MTX_NOPUSH 0x00 /* push or not */ +# define G_MTX_PUSH 0x01 +#else /* F3DEX_GBI_2 */ +# define G_MTX_MODELVIEW 0x00 /* matrix types */ +# define G_MTX_PROJECTION 0x01 +# define G_MTX_MUL 0x00 /* concat or load */ +# define G_MTX_LOAD 0x02 +# define G_MTX_NOPUSH 0x00 /* push or not */ +# define G_MTX_PUSH 0x04 +#endif /* F3DEX_GBI_2 */ + +/* + * flags for G_SETGEOMETRYMODE + * (this rendering state is maintained in RSP) + * + * DO NOT USE THE LOW 8 BITS OF GEOMETRYMODE: + * The weird bit-ordering is for the micro-code: the lower byte + * can be OR'd in with G_TRI_SHADE (11001100) to construct + * the triangle command directly. Don't break it... + * + * DO NOT USE THE HIGH 8 BITS OF GEOMETRYMODE: + * The high byte is OR'd with 0x703 to form the clip code mask. + * If it is set to 0x04, this will cause near clipping to occur. + * If it is zero, near clipping will not occur. + * + * Further explanation: + * G_SHADE is necessary in order to see the color that you passed + * down with the vertex. If G_SHADE isn't set, you need to set the DP + * appropriately and use primcolor to see anything. + * + * G_SHADING_SMOOTH enabled means use all 3 colors of the triangle. + * If it is not set, then do 'flat shading', where only one vertex color + * is used (and all 3 vertices are set to that same color by the ucode) + * See the man page for gSP1Triangle(). + * + */ +#define G_ZBUFFER 0x00000001 +#define G_SHADE 0x00000004 /* enable Gouraud interp */ +/* rest of low byte reserved for setup ucode */ +#ifdef F3DEX_GBI_2 +# define G_TEXTURE_ENABLE 0x00000000 /* Ignored */ +# define G_SHADING_SMOOTH 0x00200000 /* flat or smooth shaded */ +# define G_CULL_FRONT 0x00000200 +# define G_CULL_BACK 0x00000400 +# define G_CULL_BOTH 0x00000600 /* To make code cleaner */ +#else +# define G_TEXTURE_ENABLE 0x00000002 /* Microcode use only */ +# define G_SHADING_SMOOTH 0x00000200 /* flat or smooth shaded */ +# define G_CULL_FRONT 0x00001000 +# define G_CULL_BACK 0x00002000 +# define G_CULL_BOTH 0x00003000 /* To make code cleaner */ +#endif +#define G_FOG 0x00010000 +#define G_LIGHTING 0x00020000 +#define G_TEXTURE_GEN 0x00040000 +#define G_TEXTURE_GEN_LINEAR 0x00080000 +#define G_LOD 0x00100000 /* NOT IMPLEMENTED */ +#if (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +# define G_CLIPPING 0x00800000 +#else +# define G_CLIPPING 0x00000000 +#endif + +#ifdef _LANGUAGE_ASSEMBLY +#define G_FOG_H (G_FOG/0x10000) +#define G_LIGHTING_H (G_LIGHTING/0x10000) +#define G_TEXTURE_GEN_H (G_TEXTURE_GEN/0x10000) +#define G_TEXTURE_GEN_LINEAR_H (G_TEXTURE_GEN_LINEAR/0x10000) +#define G_LOD_H (G_LOD/0x10000) /* NOT IMPLEMENTED */ +#if (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +# define G_CLIPPING_H (G_CLIPPING/0x10000) +#endif +#endif + +/* Need these defined for Sprite Microcode */ +#ifdef _LANGUAGE_ASSEMBLY +#define G_TX_LOADTILE 7 +#define G_TX_RENDERTILE 0 + +#define G_TX_NOMIRROR 0 +#define G_TX_WRAP 0 +#define G_TX_MIRROR 0x1 +#define G_TX_CLAMP 0x2 +#define G_TX_NOMASK 0 +#define G_TX_NOLOD 0 +#endif + +/* + * G_SETIMG fmt: set image formats + */ +#define G_IM_FMT_RGBA 0 +#define G_IM_FMT_YUV 1 +#define G_IM_FMT_CI 2 +#define G_IM_FMT_IA 3 +#define G_IM_FMT_I 4 + +/* + * G_SETIMG siz: set image pixel size + */ +#define G_IM_SIZ_4b 0 +#define G_IM_SIZ_8b 1 +#define G_IM_SIZ_16b 2 +#define G_IM_SIZ_32b 3 +#define G_IM_SIZ_DD 5 + +#define G_IM_SIZ_4b_BYTES 0 +#define G_IM_SIZ_4b_TILE_BYTES G_IM_SIZ_4b_BYTES +#define G_IM_SIZ_4b_LINE_BYTES G_IM_SIZ_4b_BYTES + +#define G_IM_SIZ_8b_BYTES 1 +#define G_IM_SIZ_8b_TILE_BYTES G_IM_SIZ_8b_BYTES +#define G_IM_SIZ_8b_LINE_BYTES G_IM_SIZ_8b_BYTES + +#define G_IM_SIZ_16b_BYTES 2 +#define G_IM_SIZ_16b_TILE_BYTES G_IM_SIZ_16b_BYTES +#define G_IM_SIZ_16b_LINE_BYTES G_IM_SIZ_16b_BYTES + +#define G_IM_SIZ_32b_BYTES 4 +#define G_IM_SIZ_32b_TILE_BYTES 2 +#define G_IM_SIZ_32b_LINE_BYTES 2 + +#define G_IM_SIZ_4b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_8b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_16b_LOAD_BLOCK G_IM_SIZ_16b +#define G_IM_SIZ_32b_LOAD_BLOCK G_IM_SIZ_32b + +#define G_IM_SIZ_4b_SHIFT 2 +#define G_IM_SIZ_8b_SHIFT 1 +#define G_IM_SIZ_16b_SHIFT 0 +#define G_IM_SIZ_32b_SHIFT 0 + +#define G_IM_SIZ_4b_INCR 3 +#define G_IM_SIZ_8b_INCR 1 +#define G_IM_SIZ_16b_INCR 0 +#define G_IM_SIZ_32b_INCR 0 + +/* + * G_SETCOMBINE: color combine modes + */ +/* Color combiner constants: */ +#define G_CCMUX_COMBINED 0 +#define G_CCMUX_TEXEL0 1 +#define G_CCMUX_TEXEL1 2 +#define G_CCMUX_PRIMITIVE 3 +#define G_CCMUX_SHADE 4 +#define G_CCMUX_ENVIRONMENT 5 +#define G_CCMUX_CENTER 6 +#define G_CCMUX_SCALE 6 +#define G_CCMUX_COMBINED_ALPHA 7 +#define G_CCMUX_TEXEL0_ALPHA 8 +#define G_CCMUX_TEXEL1_ALPHA 9 +#define G_CCMUX_PRIMITIVE_ALPHA 10 +#define G_CCMUX_SHADE_ALPHA 11 +#define G_CCMUX_ENV_ALPHA 12 +#define G_CCMUX_LOD_FRACTION 13 +#define G_CCMUX_PRIM_LOD_FRAC 14 +#define G_CCMUX_NOISE 7 +#define G_CCMUX_K4 7 +#define G_CCMUX_K5 15 +#define G_CCMUX_1 6 +#define G_CCMUX_0 31 + +/* Alpha combiner constants: */ +#define G_ACMUX_COMBINED 0 +#define G_ACMUX_TEXEL0 1 +#define G_ACMUX_TEXEL1 2 +#define G_ACMUX_PRIMITIVE 3 +#define G_ACMUX_SHADE 4 +#define G_ACMUX_ENVIRONMENT 5 +#define G_ACMUX_LOD_FRACTION 0 +#define G_ACMUX_PRIM_LOD_FRAC 6 +#define G_ACMUX_1 6 +#define G_ACMUX_0 7 + +/* typical CC cycle 1 modes */ +#define G_CC_PRIMITIVE 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE +#define G_CC_SHADE 0, 0, 0, SHADE, 0, 0, 0, SHADE + +#define G_CC_MODULATEI TEXEL0, 0, SHADE, 0, 0, 0, 0, SHADE +#define G_CC_MODULATEIDECALA TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0 +#define G_CC_MODULATEIFADE TEXEL0, 0, SHADE, 0, 0, 0, 0, ENVIRONMENT + +#define G_CC_MODULATERGB G_CC_MODULATEI +#define G_CC_MODULATERGBDECALA G_CC_MODULATEIDECALA +#define G_CC_MODULATERGBFADE G_CC_MODULATEIFADE + +#define G_CC_MODULATEIA TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0 +#define G_CC_MODULATEIFADEA TEXEL0, 0, SHADE, 0, TEXEL0, 0, ENVIRONMENT, 0 + +#define G_CC_MODULATEFADE TEXEL0, 0, SHADE, 0, ENVIRONMENT, 0, TEXEL0, 0 + +#define G_CC_MODULATERGBA G_CC_MODULATEIA +#define G_CC_MODULATERGBFADEA G_CC_MODULATEIFADEA + +#define G_CC_MODULATEI_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0 +#define G_CC_MODULATEIDECALA_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, TEXEL0 + +#define G_CC_MODULATERGB_PRIM G_CC_MODULATEI_PRIM +#define G_CC_MODULATERGBA_PRIM G_CC_MODULATEIA_PRIM +#define G_CC_MODULATERGBDECALA_PRIM G_CC_MODULATEIDECALA_PRIM + +#define G_CC_FADE SHADE, 0, ENVIRONMENT, 0, SHADE, 0, ENVIRONMENT, 0 +#define G_CC_FADEA TEXEL0, 0, ENVIRONMENT, 0, TEXEL0, 0, ENVIRONMENT, 0 + +#define G_CC_DECALRGB 0, 0, 0, TEXEL0, 0, 0, 0, SHADE +#define G_CC_DECALRGBA 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0 +#define G_CC_DECALFADE 0, 0, 0, TEXEL0, 0, 0, 0, ENVIRONMENT + +#define G_CC_DECALFADEA 0, 0, 0, TEXEL0, TEXEL0, 0, ENVIRONMENT, 0 + +#define G_CC_BLENDI ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDIA ENVIRONMENT, SHADE, TEXEL0, SHADE, TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDIDECALA ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0 + +#define G_CC_BLENDRGBA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDRGBDECALA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_BLENDRGBFADEA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, ENVIRONMENT + +#define G_CC_ADDRGB TEXEL0, 0, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_ADDRGBDECALA TEXEL0, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_ADDRGBFADE TEXEL0, 0, TEXEL0, SHADE, 0, 0, 0, ENVIRONMENT + +#define G_CC_REFLECTRGB ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_REFLECTRGBDECALA ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0 + +#define G_CC_HILITERGB PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +#define G_CC_HILITERGBA PRIMITIVE, SHADE, TEXEL0, SHADE, PRIMITIVE, SHADE, TEXEL0, SHADE +#define G_CC_HILITERGBDECALA PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0 + +#define G_CC_SHADEDECALA 0, 0, 0, SHADE, 0, 0, 0, TEXEL0 +#define G_CC_SHADEFADEA 0, 0, 0, SHADE, 0, 0, 0, ENVIRONMENT + +#define G_CC_BLENDPE PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, SHADE, 0 +#define G_CC_BLENDPEDECALA PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, TEXEL0 + +/* oddball modes */ +#define _G_CC_BLENDPE ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, SHADE, 0 +#define _G_CC_BLENDPEDECALA ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, 0, 0, 0, TEXEL0 +#define _G_CC_TWOCOLORTEX PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE +/* used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD */ +#define _G_CC_SPARSEST PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0, PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_TEMPLERP TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0 + +/* typical CC cycle 1 modes, usually followed by other cycle 2 modes */ +#define G_CC_TRILERP TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0 +#define G_CC_INTERFERENCE TEXEL0, 0, TEXEL1, 0, TEXEL0, 0, TEXEL1, 0 + +/* + * One-cycle color convert operation + */ +#define G_CC_1CYUV2RGB TEXEL0, K4, K5, TEXEL0, 0, 0, 0, SHADE + +/* + * NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock. + * Therefore, CC looks for step1 results in TEXEL1 + */ +#define G_CC_YUV2RGB TEXEL1, K4, K5, TEXEL1, 0, 0, 0, 0 + +/* typical CC cycle 2 modes */ +#define G_CC_PASS2 0, 0, 0, COMBINED, 0, 0, 0, COMBINED +#define G_CC_MODULATEI2 COMBINED, 0, SHADE, 0, 0, 0, 0, SHADE +#define G_CC_MODULATEIA2 COMBINED, 0, SHADE, 0, COMBINED, 0, SHADE, 0 +#define G_CC_MODULATERGB2 G_CC_MODULATEI2 +#define G_CC_MODULATERGBA2 G_CC_MODULATEIA2 +#define G_CC_MODULATEI_PRIM2 COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE +#define G_CC_MODULATEIA_PRIM2 COMBINED, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0 +#define G_CC_MODULATERGB_PRIM2 G_CC_MODULATEI_PRIM2 +#define G_CC_MODULATERGBA_PRIM2 G_CC_MODULATEIA_PRIM2 +#define G_CC_DECALRGB2 0, 0, 0, COMBINED, 0, 0, 0, SHADE +/* + * ? +#define G_CC_DECALRGBA2 COMBINED, SHADE, COMBINED_ALPHA, SHADE, 0, 0, 0, SHADE +*/ +#define G_CC_BLENDI2 ENVIRONMENT, SHADE, COMBINED, SHADE, 0, 0, 0, SHADE +#define G_CC_BLENDIA2 ENVIRONMENT, SHADE, COMBINED, SHADE, COMBINED, 0, SHADE, 0 +#define G_CC_CHROMA_KEY2 TEXEL0, CENTER, SCALE, 0, 0, 0, 0, 0 +#define G_CC_HILITERGB2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, SHADE +#define G_CC_HILITERGBA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, ENVIRONMENT, COMBINED, TEXEL0, COMBINED +#define G_CC_HILITERGBDECALA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, TEXEL0 +#define G_CC_HILITERGBPASSA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, COMBINED + +/* + * G_SETOTHERMODE_L sft: shift count + */ +#define G_MDSFT_ALPHACOMPARE 0 +#define G_MDSFT_ZSRCSEL 2 +#define G_MDSFT_RENDERMODE 3 +#define G_MDSFT_BLENDER 16 + +/* + * G_SETOTHERMODE_H sft: shift count + */ +#define G_MDSFT_BLENDMASK 0 /* unsupported */ +#define G_MDSFT_ALPHADITHER 4 +#define G_MDSFT_RGBDITHER 6 + +#define G_MDSFT_COMBKEY 8 +#define G_MDSFT_TEXTCONV 9 +#define G_MDSFT_TEXTFILT 12 +#define G_MDSFT_TEXTLUT 14 +#define G_MDSFT_TEXTLOD 16 +#define G_MDSFT_TEXTDETAIL 17 +#define G_MDSFT_TEXTPERSP 19 +#define G_MDSFT_CYCLETYPE 20 +#define G_MDSFT_COLORDITHER 22 /* unsupported in HW 2.0 */ +#define G_MDSFT_PIPELINE 23 + +/* G_SETOTHERMODE_H gPipelineMode */ +#define G_PM_1PRIMITIVE (1 << G_MDSFT_PIPELINE) +#define G_PM_NPRIMITIVE (0 << G_MDSFT_PIPELINE) + +/* G_SETOTHERMODE_H gSetCycleType */ +#define G_CYC_1CYCLE (0 << G_MDSFT_CYCLETYPE) +#define G_CYC_2CYCLE (1 << G_MDSFT_CYCLETYPE) +#define G_CYC_COPY (2 << G_MDSFT_CYCLETYPE) +#define G_CYC_FILL (3 << G_MDSFT_CYCLETYPE) + +/* G_SETOTHERMODE_H gSetTexturePersp */ +#define G_TP_NONE (0 << G_MDSFT_TEXTPERSP) +#define G_TP_PERSP (1 << G_MDSFT_TEXTPERSP) + +/* G_SETOTHERMODE_H gSetTextureDetail */ +#define G_TD_CLAMP (0 << G_MDSFT_TEXTDETAIL) +#define G_TD_SHARPEN (1 << G_MDSFT_TEXTDETAIL) +#define G_TD_DETAIL (2 << G_MDSFT_TEXTDETAIL) + +/* G_SETOTHERMODE_H gSetTextureLOD */ +#define G_TL_TILE (0 << G_MDSFT_TEXTLOD) +#define G_TL_LOD (1 << G_MDSFT_TEXTLOD) + +/* G_SETOTHERMODE_H gSetTextureLUT */ +#define G_TT_NONE (0 << G_MDSFT_TEXTLUT) +#define G_TT_RGBA16 (2 << G_MDSFT_TEXTLUT) +#define G_TT_IA16 (3 << G_MDSFT_TEXTLUT) + +/* G_SETOTHERMODE_H gSetTextureFilter */ +#define G_TF_POINT (0 << G_MDSFT_TEXTFILT) +#define G_TF_AVERAGE (3 << G_MDSFT_TEXTFILT) +#define G_TF_BILERP (2 << G_MDSFT_TEXTFILT) + +/* G_SETOTHERMODE_H gSetTextureConvert */ +#define G_TC_CONV (0 << G_MDSFT_TEXTCONV) +#define G_TC_FILTCONV (5 << G_MDSFT_TEXTCONV) +#define G_TC_FILT (6 << G_MDSFT_TEXTCONV) + +/* G_SETOTHERMODE_H gSetCombineKey */ +#define G_CK_NONE (0 << G_MDSFT_COMBKEY) +#define G_CK_KEY (1 << G_MDSFT_COMBKEY) + +/* G_SETOTHERMODE_H gSetColorDither */ +#define G_CD_MAGICSQ (0 << G_MDSFT_RGBDITHER) +#define G_CD_BAYER (1 << G_MDSFT_RGBDITHER) +#define G_CD_NOISE (2 << G_MDSFT_RGBDITHER) + +#ifndef _HW_VERSION_1 +#define G_CD_DISABLE (3 << G_MDSFT_RGBDITHER) +#define G_CD_ENABLE G_CD_NOISE /* HW 1.0 compatibility mode */ +#else +#define G_CD_ENABLE (1 << G_MDSFT_COLORDITHER) +#define G_CD_DISABLE (0 << G_MDSFT_COLORDITHER) +#endif + +/* G_SETOTHERMODE_H gSetAlphaDither */ +#define G_AD_PATTERN (0 << G_MDSFT_ALPHADITHER) +#define G_AD_NOTPATTERN (1 << G_MDSFT_ALPHADITHER) +#define G_AD_NOISE (2 << G_MDSFT_ALPHADITHER) +#define G_AD_DISABLE (3 << G_MDSFT_ALPHADITHER) + +/* G_SETOTHERMODE_L gSetAlphaCompare */ +#define G_AC_NONE (0 << G_MDSFT_ALPHACOMPARE) +#define G_AC_THRESHOLD (1 << G_MDSFT_ALPHACOMPARE) +#define G_AC_DITHER (3 << G_MDSFT_ALPHACOMPARE) + +/* G_SETOTHERMODE_L gSetDepthSource */ +#define G_ZS_PIXEL (0 << G_MDSFT_ZSRCSEL) +#define G_ZS_PRIM (1 << G_MDSFT_ZSRCSEL) + +/* G_SETOTHERMODE_L gSetRenderMode */ +#define AA_EN 0x8 +#define Z_CMP 0x10 +#define Z_UPD 0x20 +#define IM_RD 0x40 +#define CLR_ON_CVG 0x80 +#define CVG_DST_CLAMP 0 +#define CVG_DST_WRAP 0x100 +#define CVG_DST_FULL 0x200 +#define CVG_DST_SAVE 0x300 +#define ZMODE_OPA 0 +#define ZMODE_INTER 0x400 +#define ZMODE_XLU 0x800 +#define ZMODE_DEC 0xc00 +#define CVG_X_ALPHA 0x1000 +#define ALPHA_CVG_SEL 0x2000 +#define FORCE_BL 0x4000 +#define TEX_EDGE 0x0000 /* used to be 0x8000 */ + +#define G_BL_CLR_IN 0 +#define G_BL_CLR_MEM 1 +#define G_BL_CLR_BL 2 +#define G_BL_CLR_FOG 3 +#define G_BL_1MA 0 +#define G_BL_A_MEM 1 +#define G_BL_A_IN 0 +#define G_BL_A_FOG 1 +#define G_BL_A_SHADE 2 +#define G_BL_1 2 +#define G_BL_0 3 + +#define GBL_c1(m1a, m1b, m2a, m2b) \ + (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18 +#define GBL_c2(m1a, m1b, m2a, m2b) \ + (m1a) << 28 | (m1b) << 24 | (m2a) << 20 | (m2b) << 16 + +#define RM_AA_ZB_OPA_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_SURF(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | \ + FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_DECAL(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ALPHA_CVG_SEL | \ + ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_DECAL(clk) \ + AA_EN | Z_CMP | CVG_DST_WRAP | ALPHA_CVG_SEL | \ + ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_DECAL(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | \ + FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + ALPHA_CVG_SEL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_ZB_OPA_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | \ + ALPHA_CVG_SEL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_XLU_INTER(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | \ + FORCE_BL | ZMODE_INTER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_XLU_LINE(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_DEC_LINE(clk) \ + AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_TEX_EDGE(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_TEX_INTER(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_INTER | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_SUB_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_ZB_PCL_SURF(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | G_AC_DITHER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_OPA_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_TEX_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_ZB_SUB_TERR(clk) \ + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + + +#define RM_AA_OPA_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_RA_OPA_SURF(clk) \ + AA_EN | CVG_DST_CLAMP | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_XLU_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | \ + ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_XLU_LINE(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_DEC_LINE(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | CVG_X_ALPHA | \ + ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_TEX_EDGE(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_SUB_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_AA_PCL_SURF(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | G_AC_DITHER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_OPA_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_TEX_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_CLAMP | \ + CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_AA_SUB_TERR(clk) \ + AA_EN | IM_RD | CVG_DST_FULL | \ + ZMODE_OPA | ALPHA_CVG_SEL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + + +#define RM_ZB_OPA_SURF(clk) \ + Z_CMP | Z_UPD | CVG_DST_FULL | ALPHA_CVG_SEL | \ + ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_ZB_XLU_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OPA_DECAL(clk) \ + Z_CMP | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) + +#define RM_ZB_XLU_DECAL(clk) \ + Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_CLD_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_XLU | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_OVL_SURF(clk) \ + Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_DEC | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_ZB_PCL_SURF(clk) \ + Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | \ + G_AC_DITHER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + + +#define RM_OPA_SURF(clk) \ + CVG_DST_CLAMP | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_XLU_SURF(clk) \ + IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_TEX_EDGE(clk) \ + CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL |\ + ZMODE_OPA | TEX_EDGE | AA_EN | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_CLD_SURF(clk) \ + IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) + +#define RM_PCL_SURF(clk) \ + CVG_DST_FULL | FORCE_BL | ZMODE_OPA | \ + G_AC_DITHER | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +#define RM_ADD(clk) \ + IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1) + +#define RM_NOOP(clk) \ + GBL_c##clk(0, 0, 0, 0) + +#define RM_VISCVG(clk) \ + IM_RD | FORCE_BL | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM) + +/* for rendering to an 8-bit framebuffer */ +#define RM_OPA_CI(clk) \ + CVG_DST_CLAMP | ZMODE_OPA | \ + GBL_c##clk(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +/* Custom version of RM_AA_ZB_XLU_SURF with Z_UPD */ +#define RM_CUSTOM_AA_ZB_XLU_SURF(clk) \ + RM_AA_ZB_XLU_SURF(clk) | Z_UPD + + +#define G_RM_AA_ZB_OPA_SURF RM_AA_ZB_OPA_SURF(1) +#define G_RM_AA_ZB_OPA_SURF2 RM_AA_ZB_OPA_SURF(2) +#define G_RM_AA_ZB_XLU_SURF RM_AA_ZB_XLU_SURF(1) +#define G_RM_AA_ZB_XLU_SURF2 RM_AA_ZB_XLU_SURF(2) +#define G_RM_AA_ZB_OPA_DECAL RM_AA_ZB_OPA_DECAL(1) +#define G_RM_AA_ZB_OPA_DECAL2 RM_AA_ZB_OPA_DECAL(2) +#define G_RM_AA_ZB_XLU_DECAL RM_AA_ZB_XLU_DECAL(1) +#define G_RM_AA_ZB_XLU_DECAL2 RM_AA_ZB_XLU_DECAL(2) +#define G_RM_AA_ZB_OPA_INTER RM_AA_ZB_OPA_INTER(1) +#define G_RM_AA_ZB_OPA_INTER2 RM_AA_ZB_OPA_INTER(2) +#define G_RM_AA_ZB_XLU_INTER RM_AA_ZB_XLU_INTER(1) +#define G_RM_AA_ZB_XLU_INTER2 RM_AA_ZB_XLU_INTER(2) +#define G_RM_AA_ZB_XLU_LINE RM_AA_ZB_XLU_LINE(1) +#define G_RM_AA_ZB_XLU_LINE2 RM_AA_ZB_XLU_LINE(2) +#define G_RM_AA_ZB_DEC_LINE RM_AA_ZB_DEC_LINE(1) +#define G_RM_AA_ZB_DEC_LINE2 RM_AA_ZB_DEC_LINE(2) +#define G_RM_AA_ZB_TEX_EDGE RM_AA_ZB_TEX_EDGE(1) +#define G_RM_AA_ZB_TEX_EDGE2 RM_AA_ZB_TEX_EDGE(2) +#define G_RM_AA_ZB_TEX_INTER RM_AA_ZB_TEX_INTER(1) +#define G_RM_AA_ZB_TEX_INTER2 RM_AA_ZB_TEX_INTER(2) +#define G_RM_AA_ZB_SUB_SURF RM_AA_ZB_SUB_SURF(1) +#define G_RM_AA_ZB_SUB_SURF2 RM_AA_ZB_SUB_SURF(2) +#define G_RM_AA_ZB_PCL_SURF RM_AA_ZB_PCL_SURF(1) +#define G_RM_AA_ZB_PCL_SURF2 RM_AA_ZB_PCL_SURF(2) +#define G_RM_AA_ZB_OPA_TERR RM_AA_ZB_OPA_TERR(1) +#define G_RM_AA_ZB_OPA_TERR2 RM_AA_ZB_OPA_TERR(2) +#define G_RM_AA_ZB_TEX_TERR RM_AA_ZB_TEX_TERR(1) +#define G_RM_AA_ZB_TEX_TERR2 RM_AA_ZB_TEX_TERR(2) +#define G_RM_AA_ZB_SUB_TERR RM_AA_ZB_SUB_TERR(1) +#define G_RM_AA_ZB_SUB_TERR2 RM_AA_ZB_SUB_TERR(2) + +#define G_RM_RA_ZB_OPA_SURF RM_RA_ZB_OPA_SURF(1) +#define G_RM_RA_ZB_OPA_SURF2 RM_RA_ZB_OPA_SURF(2) +#define G_RM_RA_ZB_OPA_DECAL RM_RA_ZB_OPA_DECAL(1) +#define G_RM_RA_ZB_OPA_DECAL2 RM_RA_ZB_OPA_DECAL(2) +#define G_RM_RA_ZB_OPA_INTER RM_RA_ZB_OPA_INTER(1) +#define G_RM_RA_ZB_OPA_INTER2 RM_RA_ZB_OPA_INTER(2) + +#define G_RM_AA_OPA_SURF RM_AA_OPA_SURF(1) +#define G_RM_AA_OPA_SURF2 RM_AA_OPA_SURF(2) +#define G_RM_AA_XLU_SURF RM_AA_XLU_SURF(1) +#define G_RM_AA_XLU_SURF2 RM_AA_XLU_SURF(2) +#define G_RM_AA_XLU_LINE RM_AA_XLU_LINE(1) +#define G_RM_AA_XLU_LINE2 RM_AA_XLU_LINE(2) +#define G_RM_AA_DEC_LINE RM_AA_DEC_LINE(1) +#define G_RM_AA_DEC_LINE2 RM_AA_DEC_LINE(2) +#define G_RM_AA_TEX_EDGE RM_AA_TEX_EDGE(1) +#define G_RM_AA_TEX_EDGE2 RM_AA_TEX_EDGE(2) +#define G_RM_AA_SUB_SURF RM_AA_SUB_SURF(1) +#define G_RM_AA_SUB_SURF2 RM_AA_SUB_SURF(2) +#define G_RM_AA_PCL_SURF RM_AA_PCL_SURF(1) +#define G_RM_AA_PCL_SURF2 RM_AA_PCL_SURF(2) +#define G_RM_AA_OPA_TERR RM_AA_OPA_TERR(1) +#define G_RM_AA_OPA_TERR2 RM_AA_OPA_TERR(2) +#define G_RM_AA_TEX_TERR RM_AA_TEX_TERR(1) +#define G_RM_AA_TEX_TERR2 RM_AA_TEX_TERR(2) +#define G_RM_AA_SUB_TERR RM_AA_SUB_TERR(1) +#define G_RM_AA_SUB_TERR2 RM_AA_SUB_TERR(2) + +#define G_RM_RA_OPA_SURF RM_RA_OPA_SURF(1) +#define G_RM_RA_OPA_SURF2 RM_RA_OPA_SURF(2) + +#define G_RM_ZB_OPA_SURF RM_ZB_OPA_SURF(1) +#define G_RM_ZB_OPA_SURF2 RM_ZB_OPA_SURF(2) +#define G_RM_ZB_XLU_SURF RM_ZB_XLU_SURF(1) +#define G_RM_ZB_XLU_SURF2 RM_ZB_XLU_SURF(2) +#define G_RM_ZB_OPA_DECAL RM_ZB_OPA_DECAL(1) +#define G_RM_ZB_OPA_DECAL2 RM_ZB_OPA_DECAL(2) +#define G_RM_ZB_XLU_DECAL RM_ZB_XLU_DECAL(1) +#define G_RM_ZB_XLU_DECAL2 RM_ZB_XLU_DECAL(2) +#define G_RM_ZB_CLD_SURF RM_ZB_CLD_SURF(1) +#define G_RM_ZB_CLD_SURF2 RM_ZB_CLD_SURF(2) +#define G_RM_ZB_OVL_SURF RM_ZB_OVL_SURF(1) +#define G_RM_ZB_OVL_SURF2 RM_ZB_OVL_SURF(2) +#define G_RM_ZB_PCL_SURF RM_ZB_PCL_SURF(1) +#define G_RM_ZB_PCL_SURF2 RM_ZB_PCL_SURF(2) + +#define G_RM_OPA_SURF RM_OPA_SURF(1) +#define G_RM_OPA_SURF2 RM_OPA_SURF(2) +#define G_RM_XLU_SURF RM_XLU_SURF(1) +#define G_RM_XLU_SURF2 RM_XLU_SURF(2) +#define G_RM_CLD_SURF RM_CLD_SURF(1) +#define G_RM_CLD_SURF2 RM_CLD_SURF(2) +#define G_RM_TEX_EDGE RM_TEX_EDGE(1) +#define G_RM_TEX_EDGE2 RM_TEX_EDGE(2) +#define G_RM_PCL_SURF RM_PCL_SURF(1) +#define G_RM_PCL_SURF2 RM_PCL_SURF(2) +#define G_RM_ADD RM_ADD(1) +#define G_RM_ADD2 RM_ADD(2) +#define G_RM_NOOP RM_NOOP(1) +#define G_RM_NOOP2 RM_NOOP(2) +#define G_RM_VISCVG RM_VISCVG(1) +#define G_RM_VISCVG2 RM_VISCVG(2) +#define G_RM_OPA_CI RM_OPA_CI(1) +#define G_RM_OPA_CI2 RM_OPA_CI(2) + +#define G_RM_CUSTOM_AA_ZB_XLU_SURF RM_CUSTOM_AA_ZB_XLU_SURF(1) +#define G_RM_CUSTOM_AA_ZB_XLU_SURF2 RM_CUSTOM_AA_ZB_XLU_SURF(2) + + +#define G_RM_FOG_SHADE_A GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA) +#define G_RM_FOG_PRIM_A GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA) +#define G_RM_PASS GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) + +/* + * G_SETCONVERT: K0-5 + */ +#define G_CV_K0 175 +#define G_CV_K1 -43 +#define G_CV_K2 -89 +#define G_CV_K3 222 +#define G_CV_K4 114 +#define G_CV_K5 42 + +/* + * G_SETSCISSOR: interlace mode + */ +#define G_SC_NON_INTERLACE 0 +#define G_SC_ODD_INTERLACE 3 +#define G_SC_EVEN_INTERLACE 2 + +/* flags to inhibit pushing of the display list (on branch) */ +#define G_DL_PUSH 0x00 +#define G_DL_NOPUSH 0x01 + +/* + * BEGIN C-specific section: (typedef's) + */ +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/* + * Data Structures + * + * NOTE: + * The DMA transfer hardware requires 64-bit aligned, 64-bit multiple- + * sized transfers. This important hardware optimization is unfortunately + * reflected in the programming interface, with some structures + * padded and alignment enforced. + * + * Since structures are aligned to the boundary of the "worst-case" + * element, we can't depend on the C compiler to align things + * properly. + * + * 64-bit structure alignment is enforced by wrapping structures with + * unions that contain a dummy "long long int". Why this works is + * explained in the ANSI C Spec, or on page 186 of the second edition + * of K&R, "The C Programming Language". + * + * The price we pay for this is a little awkwardness referencing the + * structures through the union. There is no memory penalty, since + * all the structures are at least 64-bits the dummy alignment field + * does not increase the size of the union. + * + * Static initialization of these union structures works because + * the ANSI C spec states that static initialization for unions + * works by using the first union element. We put the dummy alignment + * field last for this reason. + * + * (it's possible a newer 64-bit compiler from MIPS might make this + * easier with a flag, but we can't wait for it...) + * + */ + +/* + * Vertex (set up for use with colors) + */ +typedef struct { +#ifndef GBI_FLOATS + short ob[3]; /* x, y, z */ +#else + float ob[3]; /* x, y, z */ +#endif + unsigned short flag; + short tc[2]; /* texture coord */ + unsigned char cn[4]; /* color & alpha */ +} Vtx_t; + +/* + * Vertex (set up for use with normals) + */ +typedef struct { +#ifndef GBI_FLOATS + short ob[3]; /* x, y, z */ +#else + float ob[3]; /* x, y, z */ +#endif + unsigned short flag; + short tc[2]; /* texture coord */ + signed char n[3]; /* normal */ + unsigned char a; /* alpha */ +} Vtx_tn; + +typedef union { + Vtx_t v; /* Use this one for colors */ + Vtx_tn n; /* Use this one for normals */ + long long int force_structure_alignment; +} Vtx; + +/* + * Sprite structure + */ + +typedef struct { + void *SourceImagePointer; + void *TlutPointer; + short Stride; + short SubImageWidth; + short SubImageHeight; + char SourceImageType; + char SourceImageBitSize; + short SourceImageOffsetS; + short SourceImageOffsetT; + /* 20 bytes for above */ + + /* padding to bring structure size to 64 bit allignment */ + char dummy[4]; + +} uSprite_t; + +typedef union { + uSprite_t s; + + /* Need to make sure this is 64 bit aligned */ + long long int force_structure_allignment[3]; +} uSprite; + +/* + * Triangle face + */ +typedef struct { + unsigned char flag; + unsigned char v[3]; +} Tri; + +#ifndef GBI_FLOATS +/* + * 4x4 matrix, fixed point s15.16 format. + * First 8 words are integer portion of the 4x4 matrix + * Last 8 words are the fraction portion of the 4x4 matrix + */ +typedef s32 Mtx_t[4][4]; + +typedef union { + Mtx_t m; + long long int force_structure_alignment; +} Mtx; +#else +typedef struct { + float m[4][4]; +} Mtx; +#endif + +/* + * Viewport + */ + +/* + * + * This magic value is the maximum INTEGER z-range of the hardware + * (there are also 16-bits of fraction, which are introduced during + * any transformations). This is not just a good idea, it's the law. + * Feeding the hardware eventual z-coordinates (after any transforms + * or scaling) bigger than this, will not work. + * + * This number is DIFFERENT than G_MAXFBZ, which is the maximum value + * you want to use to initialize the z-buffer. + * + * The reason these are different is mildly interesting, but too long + * to explain here. It is basically the result of optimizations in the + * hardware. A more generic API might hide this detail from the users, + * but we don't have the ucode to do that... + * + */ +#define G_MAXZ 0x03ff /* 10 bits of integer screen-Z precision */ + +/* + * The viewport structure elements have 2 bits of fraction, necessary + * to accomodate the sub-pixel positioning scaling for the hardware. + * This can also be exploited to handle odd-sized viewports. + * + * Accounting for these fractional bits, using the default projection + * and viewing matrices, the viewport structure is initialized thusly: + * + * (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, G_MAXZ, 0, + * (SCREEN_WD/2)*4, (SCREEN_HT/2)*4, 0, 0, + */ +typedef struct { + short vscale[4]; /* scale, 2 bits fraction */ + short vtrans[4]; /* translate, 2 bits fraction */ + /* both the above arrays are padded to 64-bit boundary */ +} Vp_t; + +typedef union { + Vp_t vp; + long long int force_structure_alignment; +} Vp; + +/* + * MOVEMEM indices + * + * Each of these indexes an entry in a dmem table + * which points to a 1-4 word block of dmem in + * which to store a 1-4 word DMA. + * + */ +#ifdef F3DEX_GBI_2 +/* 0,4 are reserved by G_MTX */ +# define G_MV_MMTX 2 +# define G_MV_PMTX 6 +# define G_MV_VIEWPORT 8 +# define G_MV_LIGHT 10 +# define G_MV_POINT 12 +# define G_MV_MATRIX 14 /* NOTE: this is in moveword table */ +# define G_MVO_LOOKATX (0*24) +# define G_MVO_LOOKATY (1*24) +# define G_MVO_L0 (2*24) +# define G_MVO_L1 (3*24) +# define G_MVO_L2 (4*24) +# define G_MVO_L3 (5*24) +# define G_MVO_L4 (6*24) +# define G_MVO_L5 (7*24) +# define G_MVO_L6 (8*24) +# define G_MVO_L7 (9*24) +#else /* F3DEX_GBI_2 */ +# define G_MV_VIEWPORT 0x80 +# define G_MV_LOOKATY 0x82 +# define G_MV_LOOKATX 0x84 +# define G_MV_L0 0x86 +# define G_MV_L1 0x88 +# define G_MV_L2 0x8a +# define G_MV_L3 0x8c +# define G_MV_L4 0x8e +# define G_MV_L5 0x90 +# define G_MV_L6 0x92 +# define G_MV_L7 0x94 +# define G_MV_TXTATT 0x96 +# define G_MV_MATRIX_1 0x9e /* NOTE: this is in moveword table */ +# define G_MV_MATRIX_2 0x98 +# define G_MV_MATRIX_3 0x9a +# define G_MV_MATRIX_4 0x9c +#endif /* F3DEX_GBI_2 */ + +/* + * MOVEWORD indices + * + * Each of these indexes an entry in a dmem table + * which points to a word in dmem in dmem where + * an immediate word will be stored. + * + */ +#define G_MW_MATRIX 0x00 /* NOTE: also used by movemem */ +#define G_MW_NUMLIGHT 0x02 +#define G_MW_CLIP 0x04 +#define G_MW_SEGMENT 0x06 +#define G_MW_FOG 0x08 +#define G_MW_LIGHTCOL 0x0a +#ifdef F3DEX_GBI_2 +# define G_MW_FORCEMTX 0x0c +#else /* F3DEX_GBI_2 */ +# define G_MW_POINTS 0x0c +#endif /* F3DEX_GBI_2 */ +#define G_MW_PERSPNORM 0x0e + +/* + * These are offsets from the address in the dmem table + */ +#define G_MWO_NUMLIGHT 0x00 +#define G_MWO_CLIP_RNX 0x04 +#define G_MWO_CLIP_RNY 0x0c +#define G_MWO_CLIP_RPX 0x14 +#define G_MWO_CLIP_RPY 0x1c +#define G_MWO_SEGMENT_0 0x00 +#define G_MWO_SEGMENT_1 0x01 +#define G_MWO_SEGMENT_2 0x02 +#define G_MWO_SEGMENT_3 0x03 +#define G_MWO_SEGMENT_4 0x04 +#define G_MWO_SEGMENT_5 0x05 +#define G_MWO_SEGMENT_6 0x06 +#define G_MWO_SEGMENT_7 0x07 +#define G_MWO_SEGMENT_8 0x08 +#define G_MWO_SEGMENT_9 0x09 +#define G_MWO_SEGMENT_A 0x0a +#define G_MWO_SEGMENT_B 0x0b +#define G_MWO_SEGMENT_C 0x0c +#define G_MWO_SEGMENT_D 0x0d +#define G_MWO_SEGMENT_E 0x0e +#define G_MWO_SEGMENT_F 0x0f +#define G_MWO_FOG 0x00 +#define G_MWO_aLIGHT_1 0x00 +#define G_MWO_bLIGHT_1 0x04 +#ifdef F3DEX_GBI_2 +#define G_MWO_aLIGHT_2 0x18 +#define G_MWO_bLIGHT_2 0x1c +#define G_MWO_aLIGHT_3 0x30 +#define G_MWO_bLIGHT_3 0x34 +#define G_MWO_aLIGHT_4 0x48 +#define G_MWO_bLIGHT_4 0x4c +#define G_MWO_aLIGHT_5 0x60 +#define G_MWO_bLIGHT_5 0x64 +#define G_MWO_aLIGHT_6 0x78 +#define G_MWO_bLIGHT_6 0x7c +#define G_MWO_aLIGHT_7 0x90 +#define G_MWO_bLIGHT_7 0x94 +#define G_MWO_aLIGHT_8 0xa8 +#define G_MWO_bLIGHT_8 0xac +#else +#define G_MWO_aLIGHT_2 0x20 +#define G_MWO_bLIGHT_2 0x24 +#define G_MWO_aLIGHT_3 0x40 +#define G_MWO_bLIGHT_3 0x44 +#define G_MWO_aLIGHT_4 0x60 +#define G_MWO_bLIGHT_4 0x64 +#define G_MWO_aLIGHT_5 0x80 +#define G_MWO_bLIGHT_5 0x84 +#define G_MWO_aLIGHT_6 0xa0 +#define G_MWO_bLIGHT_6 0xa4 +#define G_MWO_aLIGHT_7 0xc0 +#define G_MWO_bLIGHT_7 0xc4 +#define G_MWO_aLIGHT_8 0xe0 +#define G_MWO_bLIGHT_8 0xe4 +#endif +#define G_MWO_MATRIX_XX_XY_I 0x00 +#define G_MWO_MATRIX_XZ_XW_I 0x04 +#define G_MWO_MATRIX_YX_YY_I 0x08 +#define G_MWO_MATRIX_YZ_YW_I 0x0c +#define G_MWO_MATRIX_ZX_ZY_I 0x10 +#define G_MWO_MATRIX_ZZ_ZW_I 0x14 +#define G_MWO_MATRIX_WX_WY_I 0x18 +#define G_MWO_MATRIX_WZ_WW_I 0x1c +#define G_MWO_MATRIX_XX_XY_F 0x20 +#define G_MWO_MATRIX_XZ_XW_F 0x24 +#define G_MWO_MATRIX_YX_YY_F 0x28 +#define G_MWO_MATRIX_YZ_YW_F 0x2c +#define G_MWO_MATRIX_ZX_ZY_F 0x30 +#define G_MWO_MATRIX_ZZ_ZW_F 0x34 +#define G_MWO_MATRIX_WX_WY_F 0x38 +#define G_MWO_MATRIX_WZ_WW_F 0x3c +#define G_MWO_POINT_RGBA 0x10 +#define G_MWO_POINT_ST 0x14 +#define G_MWO_POINT_XYSCREEN 0x18 +#define G_MWO_POINT_ZSCREEN 0x1c + +/* + * Light structure. + * + * Note: only directional (infinite) lights are currently supported. + * + * Note: the weird order is for the DMEM alignment benefit of + * the microcode. + * + */ + +typedef struct { + unsigned char col[3]; /* diffuse light value (rgba) */ + char pad1; + unsigned char colc[3]; /* copy of diffuse light value (rgba) */ + char pad2; + signed char dir[3]; /* direction of light (normalized) */ + char pad3; +} Light_t; + +typedef struct { + unsigned char col[3]; /* ambient light value (rgba) */ + char pad1; + unsigned char colc[3]; /* copy of ambient light value (rgba) */ + char pad2; +} Ambient_t; + +typedef struct { + int x1,y1,x2,y2; /* texture offsets for highlight 1/2 */ +} Hilite_t; + +typedef union { + Light_t l; + long long int force_structure_alignment[2]; +} Light; + +typedef union { + Ambient_t l; + long long int force_structure_alignment[1]; +} Ambient; + +typedef struct { + Ambient a; + Light l[7]; +} Lightsn; + +typedef struct { + Ambient a; + Light l[1]; +} Lights0; + +typedef struct { + Ambient a; + Light l[1]; +} Lights1; + +typedef struct { + Ambient a; + Light l[2]; +} Lights2; + +typedef struct { + Ambient a; + Light l[3]; +} Lights3; + +typedef struct { + Ambient a; + Light l[4]; +} Lights4; + +typedef struct { + Ambient a; + Light l[5]; +} Lights5; + +typedef struct { + Ambient a; + Light l[6]; +} Lights6; + +typedef struct { + Ambient a; + Light l[7]; +} Lights7; + +typedef struct { + Light l[2]; +} LookAt; + +typedef union { + Hilite_t h; + long int force_structure_alignment[4]; +} Hilite; + +#define gdSPDefLights0(ar,ag,ab) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ { 0, 0, 0},0,{ 0, 0, 0},0,{ 0, 0, 0},0}}} } +#define gdSPDefLights1(ar,ag,ab,r1,g1,b1,x1,y1,z1) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}} } +#define gdSPDefLights2(ar,ag,ab,r1,g1,b1,x1,y1,z1,r2,g2,b2,x2,y2,z2) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}, \ + {{ {r2,g2,b2},0,{r2,g2,b2},0,{x2,y2,z2},0}}} } +#define gdSPDefLights3(ar,ag,ab,r1,g1,b1,x1,y1,z1,r2,g2,b2,x2,y2,z2,r3,g3,b3,x3,y3,z3) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}, \ + {{ {r2,g2,b2},0,{r2,g2,b2},0,{x2,y2,z2},0}}, \ + {{ {r3,g3,b3},0,{r3,g3,b3},0,{x3,y3,z3},0}}} } +#define gdSPDefLights4(ar,ag,ab,r1,g1,b1,x1,y1,z1,r2,g2,b2,x2,y2,z2,r3,g3,b3,x3,y3,z3,r4,g4,b4,x4,y4,z4) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}, \ + {{ {r2,g2,b2},0,{r2,g2,b2},0,{x2,y2,z2},0}}, \ + {{ {r3,g3,b3},0,{r3,g3,b3},0,{x3,y3,z3},0}}, \ + {{ {r4,g4,b4},0,{r4,g4,b4},0,{x4,y4,z4},0}}} } +#define gdSPDefLights5(ar,ag,ab,r1,g1,b1,x1,y1,z1,r2,g2,b2,x2,y2,z2,r3,g3,b3,x3,y3,z3,r4,g4,b4,x4,y4,z4,r5,g5,b5,x5,y5,z5) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}, \ + {{ {r2,g2,b2},0,{r2,g2,b2},0,{x2,y2,z2},0}}, \ + {{ {r3,g3,b3},0,{r3,g3,b3},0,{x3,y3,z3},0}}, \ + {{ {r4,g4,b4},0,{r4,g4,b4},0,{x4,y4,z4},0}}, \ + {{ {r5,g5,b5},0,{r5,g5,b5},0,{x5,y5,z5},0}}} } + + +#define gdSPDefLights6(ar,ag,ab,r1,g1,b1,x1,y1,z1,r2,g2,b2,x2,y2,z2,r3,g3,b3,x3,y3,z3,r4,g4,b4,x4,y4,z4,r5,g5,b5,x5,y5,z5,r6,g6,b6,x6,y6,z6) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}, \ + {{ {r2,g2,b2},0,{r2,g2,b2},0,{x2,y2,z2},0}}, \ + {{ {r3,g3,b3},0,{r3,g3,b3},0,{x3,y3,z3},0}}, \ + {{ {r4,g4,b4},0,{r4,g4,b4},0,{x4,y4,z4},0}}, \ + {{ {r5,g5,b5},0,{r5,g5,b5},0,{x5,y5,z5},0}}, \ + {{ {r6,g6,b6},0,{r6,g6,b6},0,{x6,y6,z6},0}}} } + + +#define gdSPDefLights7(ar,ag,ab,r1,g1,b1,x1,y1,z1,r2,g2,b2,x2,y2,z2,r3,g3,b3,x3,y3,z3,r4,g4,b4,x4,y4,z4,r5,g5,b5,x5,y5,z5,r6,g6,b6,x6,y6,z6,r7,g7,b7,x7,y7,z7) \ + { {{ {ar,ag,ab},0,{ar,ag,ab},0}}, \ + {{{ {r1,g1,b1},0,{r1,g1,b1},0,{x1,y1,z1},0}}, \ + {{ {r2,g2,b2},0,{r2,g2,b2},0,{x2,y2,z2},0}}, \ + {{ {r3,g3,b3},0,{r3,g3,b3},0,{x3,y3,z3},0}}, \ + {{ {r4,g4,b4},0,{r4,g4,b4},0,{x4,y4,z4},0}}, \ + {{ {r5,g5,b5},0,{r5,g5,b5},0,{x5,y5,z5},0}}, \ + {{ {r6,g6,b6},0,{r6,g6,b6},0,{x6,y6,z6},0}}, \ + {{ {r7,g7,b7},0,{r7,g7,b7},0,{x7,y7,z7},0}}} } + + +#define gdSPDefLookAt(rightx,righty,rightz,upx,upy,upz) \ + { {{ {{0,0,0},0,{0,0,0},0,{rightx,righty,rightz},0}}, \ + { {{0,0x80,0},0,{0,0x80,0},0,{upx,upy,upz},0}}} } + +/* Don't declare these for F3D_OLD to avoid bss reordering */ +#ifndef F3D_OLD +/* + * Graphics DMA Packet + */ +typedef struct { + int cmd:8; + unsigned int par:8; + unsigned int len:16; + uintptr_t addr; +} Gdma; + +/* + * Graphics Immediate Mode Packet types + */ +typedef struct { + int cmd:8; + int pad:24; + Tri tri; +} Gtri; + +typedef struct { + int cmd:8; + int pad1:24; + int pad2:24; + unsigned char param:8; +} Gpopmtx; + +/* + * typedef struct { + * int cmd:8; + * int pad0:24; + * int pad1:4; + * int number:4; + * int base:24; + * } Gsegment; + */ +typedef struct { + int cmd:8; + int pad0:8; + int mw_index:8; + int number:8; + int pad1:8; + int base:24; +} Gsegment; + +typedef struct { + int cmd:8; + int pad0:8; + int sft:8; + int len:8; + unsigned int data:32; +} GsetothermodeL; + +typedef struct { + int cmd:8; + int pad0:8; + int sft:8; + int len:8; + unsigned int data:32; +} GsetothermodeH; + +typedef struct { + unsigned char cmd; + unsigned char lodscale; + unsigned char tile; + unsigned char on; + unsigned short s; + unsigned short t; +} Gtexture; + +typedef struct { + int cmd:8; + int pad:24; + Tri line; +} Gline3D; + +typedef struct { + int cmd:8; + int pad1:24; + short int pad2; + short int scale; +} Gperspnorm; + + +/* + * RDP Packet types + */ +typedef struct { + int cmd:8; + unsigned int fmt:3; + unsigned int siz:2; + unsigned int pad:7; + unsigned int wd:12; /* really only 10 bits, extra */ + uintptr_t dram; /* to account for 1024 */ +} Gsetimg; + +typedef struct { + int cmd:8; + unsigned int muxs0:24; + unsigned int muxs1:32; +} Gsetcombine; + +typedef struct { + int cmd:8; + unsigned char pad; + unsigned char prim_min_level; + unsigned char prim_level; + unsigned long color; +} Gsetcolor; + +typedef struct { + int cmd:8; + int x0:10; + int x0frac:2; + int y0:10; + int y0frac:2; + unsigned int pad:8; + int x1:10; + int x1frac:2; + int y1:10; + int y1frac:2; +} Gfillrect; + +typedef struct { + int cmd:8; + unsigned int fmt:3; + unsigned int siz:2; + unsigned int pad0:1; + unsigned int line:9; + unsigned int tmem:9; + unsigned int pad1:5; + unsigned int tile:3; + unsigned int palette:4; + unsigned int ct:1; + unsigned int mt:1; + unsigned int maskt:4; + unsigned int shiftt:4; + unsigned int cs:1; + unsigned int ms:1; + unsigned int masks:4; + unsigned int shifts:4; +} Gsettile; + +typedef struct { + int cmd:8; + unsigned int sl:12; + unsigned int tl:12; + int pad:5; + unsigned int tile:3; + unsigned int sh:12; + unsigned int th:12; +} Gloadtile; + +typedef Gloadtile Gloadblock; + +typedef Gloadtile Gsettilesize; + +typedef Gloadtile Gloadtlut; + +typedef struct { + unsigned int cmd:8; /* command */ + unsigned int xl:12; /* X coordinate of upper left */ + unsigned int yl:12; /* Y coordinate of upper left */ + unsigned int pad1:5; /* Padding */ + unsigned int tile:3; /* Tile descriptor index */ + unsigned int xh:12; /* X coordinate of lower right */ + unsigned int yh:12; /* Y coordinate of lower right */ + unsigned int s:16; /* S texture coord at top left */ + unsigned int t:16; /* T texture coord at top left */ + unsigned int dsdx:16;/* Change in S per change in X */ + unsigned int dtdy:16;/* Change in T per change in Y */ +} Gtexrect; + +/* + * Textured rectangles are 128 bits not 64 bits + */ +typedef struct { + unsigned long w0; + unsigned long w1; + unsigned long w2; + unsigned long w3; +} TexRect; +#endif + +#define MakeTexRect(xh,yh,flip,tile,xl,yl,s,t,dsdx,dtdy) \ + G_TEXRECT, xh, yh, 0, flip, 0, tile, xl, yl, s, t, dsdx, dtdy + +/* + * Generic Gfx Packet + */ +typedef struct { + uintptr_t w0; + uintptr_t w1; +} Gwords; + +/* + * This union is the fundamental type of the display list. + * It is, by law, exactly 64 bits in size. + * + * (Edit: except on 64-bit, where it is exactly 128 bit. On little-endian or + * 64-bit systems, only the 'words' member may be accessed; the rest of the + * structs don't have matching layouts for now.) + */ +typedef union { + Gwords words; +#if !defined(F3D_OLD) && IS_BIG_ENDIAN && !IS_64_BIT + Gdma dma; + Gtri tri; + Gline3D line; + Gpopmtx popmtx; + Gsegment segment; + GsetothermodeH setothermodeH; + GsetothermodeL setothermodeL; + Gtexture texture; + Gperspnorm perspnorm; + Gsetimg setimg; + Gsetcombine setcombine; + Gsetcolor setcolor; + Gfillrect fillrect; /* use for setscissor also */ + Gsettile settile; + Gloadtile loadtile; /* use for loadblock also, th is dxt */ + Gsettilesize settilesize; + Gloadtlut loadtlut; +#endif + long long int force_structure_alignment; +} Gfx; + +/* + * Macros to assemble the graphics display list + */ + +/* + * DMA macros + */ +#define gDma0p(pkt, c, s, l) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24); \ + _g->words.w1 = (uintptr_t)(s); \ +} + +#define gsDma0p(c, s, l) \ +{{ \ + _SHIFTL((c), 24, 8) | _SHIFTL((l), 0, 24), (uintptr_t)(s) \ +}} + +#define gDma1p(pkt, c, s, l, p) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | \ + _SHIFTL((l), 0, 16)); \ + _g->words.w1 = (uintptr_t)(s); \ +} + +#define gsDma1p(c, s, l, p) \ +{{ \ + (_SHIFTL((c), 24, 8) | _SHIFTL((p), 16, 8) | \ + _SHIFTL((l), 0, 16)), \ + (uintptr_t)(s) \ +}} + +#define gDma2p(pkt, c, adrs, len, idx, ofs) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = (_SHIFTL((c),24,8)|_SHIFTL(((len)-1)/8,19,5)| \ + _SHIFTL((ofs)/8,8,8)|_SHIFTL((idx),0,8)); \ + _g->words.w1 = (uintptr_t)(adrs); \ +} +#define gsDma2p(c, adrs, len, idx, ofs) \ +{{ \ + (_SHIFTL((c),24,8)|_SHIFTL(((len)-1)/8,19,5)| \ + _SHIFTL((ofs)/8,8,8)|_SHIFTL((idx),0,8)), \ + (uintptr_t)(adrs) \ +}} + +#define gSPNoOp(pkt) gDma0p(pkt, G_SPNOOP, 0, 0) +#define gsSPNoOp() gsDma0p(G_SPNOOP, 0, 0) + +#ifdef F3DEX_GBI_2 +# define gSPMatrix(pkt, m, p) \ + gDma2p((pkt),G_MTX,(m),sizeof(Mtx),(p)^G_MTX_PUSH,0) +# define gsSPMatrix(m, p) \ + gsDma2p( G_MTX,(m),sizeof(Mtx),(p)^G_MTX_PUSH,0) +#else /* F3DEX_GBI_2 */ +//# define gSPMatrix(pkt, m, p) gDma1p(pkt, G_MTX, m, sizeof(Mtx), p) +# define gsSPMatrix(m, p) gsDma1p(G_MTX, m, sizeof(Mtx), p) +#endif /* F3DEX_GBI_2 */ + +#if defined(F3DEX_GBI_2) +/* + * F3DEX_GBI_2: G_VTX GBI format was changed. + * + * +--------+----+---+---+----+------+-+ + * G_VTX | cmd:8 |0000| n:8 |0000|v0+n:7|0| + * +-+---+--+----+---+---+----+------+-+ + * | |seg| address | + * +-+---+-----------------------------+ + */ +# define gSPVertex(pkt, v, n, v0) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = \ + _SHIFTL(G_VTX,24,8)|_SHIFTL((n),12,8)|_SHIFTL((v0)+(n),1,7); \ + _g->words.w1 = (uintptr_t)(v); \ +} +# define gsSPVertex(v, n, v0) \ +{{ \ + (_SHIFTL(G_VTX,24,8)|_SHIFTL((n),12,8)|_SHIFTL((v0)+(n),1,7)), \ + (uintptr_t)(v) \ +}} +#elif (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +/* + * F3DEX_GBI: G_VTX GBI format was changed to support 64 vertice. + * + * +--------+--------+------+----------+ + * G_VTX | cmd:8 | v0:8 | n:6 |length:10 | + * +-+---+--+--------+------+----------+ + * | |seg| address | + * +-+---+-----------------------------+ + */ +# define gSPVertex(pkt, v, n, v0) \ + gDma1p((pkt),G_VTX,(v),((n)<<10)|(sizeof(Vtx)*(n)-1),(v0)*2) +# define gsSPVertex(v, n, v0) \ + gsDma1p(G_VTX,(v),((n)<<10)|(sizeof(Vtx)*(n)-1),(v0)*2) +#else +# define gSPVertex(pkt, v, n, v0) \ + gDma1p(pkt, G_VTX, v, sizeof(Vtx)*(n),((n)-1)<<4|(v0)) +# define gsSPVertex(v, n, v0) \ + gsDma1p(G_VTX, v, sizeof(Vtx)*(n), ((n)-1)<<4|(v0)) +#endif + + +#ifdef F3DEX_GBI_2 +# define gSPViewport(pkt, v) \ + gDma2p((pkt), G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT, 0) +# define gsSPViewport(v) \ + gsDma2p( G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT, 0) +#else /* F3DEX_GBI_2 */ +/* +# define gSPViewport(pkt,v) \ + gDma1p((pkt), G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT) +*/ +# define gsSPViewport(v) \ + gsDma1p( G_MOVEMEM, (v), sizeof(Vp), G_MV_VIEWPORT) +#endif /* F3DEX_GBI_2 */ + +/* +#define gSPDisplayList(pkt,dl) gDma1p(pkt,G_DL,dl,0,G_DL_PUSH) +*/ +#define gsSPDisplayList( dl) gsDma1p( G_DL,dl,0,G_DL_PUSH) + +#define gSPBranchList(pkt,dl) gDma1p(pkt,G_DL,dl,0,G_DL_NOPUSH) +#define gsSPBranchList( dl) gsDma1p( G_DL,dl,0,G_DL_NOPUSH) + +#define gSPSprite2DBase(pkt, s) gDma1p(pkt, G_SPRITE2D_BASE, s, sizeof(uSprite), 0) +#define gsSPSprite2DBase(s) gsDma1p(G_SPRITE2D_BASE, s, sizeof(uSprite), 0) + +/* + * RSP short command (no DMA required) macros + */ +#define gImmp0(pkt, c) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ +} + +#define gsImmp0(c) \ +{{ \ + _SHIFTL((c), 24, 8) \ +}} + +#define gImmp1(pkt, c, p0) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + _g->words.w1 = (uintptr_t)(p0); \ +} + +#define gsImmp1(c, p0) \ +{{ \ + _SHIFTL((c), 24, 8), (uintptr_t)(p0) \ +}} + +#define gImmp2(pkt, c, p0, p1) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + _g->words.w1 = _SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8); \ +} + +#define gsImmp2(c, p0, p1) \ +{{ \ + _SHIFTL((c), 24, 8), _SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8)\ +}} + +#define gImmp3(pkt, c, p0, p1, p2) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL((c), 24, 8); \ + _g->words.w1 = (_SHIFTL((p0), 16, 16) | _SHIFTL((p1), 8, 8) | \ + _SHIFTL((p2), 0, 8)); \ +} + +#define gsImmp3(c, p0, p1, p2) \ +{{ \ + _SHIFTL((c), 24, 8), (_SHIFTL((p0), 16, 16) | \ + _SHIFTL((p1), 8, 8) | _SHIFTL((p2), 0, 8))\ +}} + +#define gImmp21(pkt, c, p0, p1, dat) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | \ + _SHIFTL((p1), 0, 8)); \ + _g->words.w1 = (uintptr_t) (dat); \ +} + +#define gsImmp21(c, p0, p1, dat) \ +{{ \ + _SHIFTL((c), 24, 8) | _SHIFTL((p0), 8, 16) | _SHIFTL((p1), 0, 8),\ + (uintptr_t) (dat) \ +}} + +#ifdef F3DEX_GBI_2 +#define gMoveWd(pkt, index, offset, data) \ + gDma1p((pkt), G_MOVEWORD, data, offset, index) +#define gsMoveWd( index, offset, data) \ + gsDma1p( G_MOVEWORD, data, offset, index) +#else /* F3DEX_GBI_2 */ +#define gMoveWd(pkt, index, offset, data) \ + gImmp21((pkt), G_MOVEWORD, offset, index, data) +#define gsMoveWd( index, offset, data) \ + gsImmp21( G_MOVEWORD, offset, index, data) +#endif /* F3DEX_GBI_2 */ + +/* Sprite immediate macros, there is also a sprite dma macro above */ + +#define gSPSprite2DScaleFlip(pkt, sx, sy, fx, fy) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SPRITE2D_SCALEFLIP, 24, 8) | \ + _SHIFTL((fx), 8, 8) | \ + _SHIFTL((fy), 0, 8)); \ + _g->words.w1 = (_SHIFTL((sx), 16, 16) | \ + _SHIFTL((sy), 0, 16)); \ +} + +#define gsSPSprite2DScaleFlip(sx, sy, fx, fy) \ +{{ \ + (_SHIFTL(G_SPRITE2D_SCALEFLIP, 24, 8) | \ + _SHIFTL((fx), 8, 8) | \ + _SHIFTL((fy), 0, 8)), \ + (_SHIFTL((sx), 16, 16) | \ + _SHIFTL((sy), 0, 16)) \ +}} + +#define gSPSprite2DDraw(pkt, px, py) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SPRITE2D_DRAW, 24, 8)); \ + _g->words.w1 = (_SHIFTL((px), 16, 16) | \ + _SHIFTL((py), 0, 16)); \ +} + +#define gsSPSprite2DDraw(px, py) \ +{{ \ + (_SHIFTL(G_SPRITE2D_DRAW, 24, 8)), \ + (_SHIFTL((px), 16, 16) | \ + _SHIFTL((py), 0, 16)) \ +}} + + +/* + * Note: the SP1Triangle() and line macros multiply the vertex indices + * by 10, this is an optimization for the microcode. + */ +#if (defined(F3DLP_GBI)||defined(F3DEX_GBI)) +# define __gsSP1Triangle_w1(v0, v1, v2) \ + (_SHIFTL((v0)*2,16,8)|_SHIFTL((v1)*2,8,8)|_SHIFTL((v2)*2,0,8)) +# define __gsSP1Triangle_w1f(v0, v1, v2, flag) \ + (((flag) == 0) ? __gsSP1Triangle_w1(v0, v1, v2): \ + ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v0): \ + __gsSP1Triangle_w1(v2, v0, v1)) +# define __gsSPLine3D_w1(v0, v1, wd) \ + (_SHIFTL((v0)*2,16,8)|_SHIFT((v1)*2,8,8)|_SHIFT((wd),0,8)) +# define __gsSPLine3D_w1f(v0, v1, wd, flag) \ + (((flag) == 0) ? __gsSPLine3D_w1(v0, v1, wd): \ + __gsSPLine3D_w1(v1, v0, wd)) +# define __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag) \ + (((flag) == 0) ? __gsSP1Triangle_w1(v0, v1, v2): \ + ((flag) == 1) ? __gsSP1Triangle_w1(v1, v2, v3): \ + ((flag) == 2) ? __gsSP1Triangle_w1(v2, v3, v0): \ + __gsSP1Triangle_w1(v3, v0, v1)) +# define __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag) \ + (((flag) == 0) ? __gsSP1Triangle_w1(v0, v2, v3): \ + ((flag) == 1) ? __gsSP1Triangle_w1(v1, v3, v0): \ + ((flag) == 2) ? __gsSP1Triangle_w1(v2, v0, v1): \ + __gsSP1Triangle_w1(v3, v1, v2)) +#else +# define __gsSP1Triangle_w1f(v0, v1, v2, flag) \ + (_SHIFTL((flag), 24,8)|_SHIFTL((v0)*10,16,8)| \ + _SHIFTL((v1)*10, 8,8)|_SHIFTL((v2)*10, 0,8)) +# define __gsSPLine3D_w1f(v0, v1, wd, flag) \ + (_SHIFTL((flag), 24,8)|_SHIFTL((v0)*10,16,8)| \ + _SHIFTL((v1)*10, 8,8)|_SHIFTL((wd), 0,8)) +#endif + +#ifdef F3DEX_GBI_2 +/*** + *** 1 Triangle + ***/ +#define gSP1Triangle(pkt, v0, v1, v2, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_TRI1, 24, 8)| \ + __gsSP1Triangle_w1f(v0, v1, v2, flag); \ + _g->words.w1 = 0; \ +} +#define gsSP1Triangle(v0, v1, v2, flag) \ +{{ \ + _SHIFTL(G_TRI1, 24, 8)|__gsSP1Triangle_w1f(v0, v1, v2, flag), \ + 0 \ +}} + +/*** + *** Line + ***/ +#define gSPLine3D(pkt, v0, v1, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8)| \ + __gsSPLine3D_w1f(v0, v1, 0, flag); \ + _g->words.w1 = 0; \ +} +#define gsSPLine3D(v0, v1, flag) \ +{{ \ + _SHIFTL(G_LINE3D, 24, 8)|__gsSPLine3D_w1f(v0, v1, 0, flag), \ + 0 \ +}} + +/*** + *** LineW + ***/ +/* these macros are the same as SPLine3D, except they have an + * additional parameter for width. The width is added to the "minimum" + * thickness, which is 1.5 pixels. The units for width are in + * half-pixel units, so a width of 1 translates to (.5 + 1.5) or + * a 2.0 pixels wide line. + */ +#define gSPLineW3D(pkt, v0, v1, wd, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8)| \ + __gsSPLine3D_w1f(v0, v1, wd, flag); \ + _g->words.w1 = 0; \ +} +#define gsSPLineW3D(v0, v1, wd, flag) \ +{{ \ + _SHIFTL(G_LINE3D, 24, 8)|__gsSPLine3D_w1f(v0, v1, wd, flag), \ + 0 \ +}} + +/*** + *** 1 Quadrangle + ***/ +#define gSP1Quadrangle(pkt, v0, v1, v2, v3, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_QUAD, 24, 8)| \ + __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)); \ + _g->words.w1 = __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag); \ +} + +#define gsSP1Quadrangle(v0, v1, v2, v3, flag) \ +{{ \ + (_SHIFTL(G_QUAD, 24, 8)| \ + __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)), \ + __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag) \ +}} +#else /* F3DEX_GBI_2 */ + +/*** + *** 1 Triangle + ***/ +#define gSP1Triangle(pkt, v0, v1, v2, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_TRI1, 24, 8); \ + _g->words.w1 = __gsSP1Triangle_w1f(v0, v1, v2, flag); \ +} +#define gsSP1Triangle(v0, v1, v2, flag) \ +{{ \ + _SHIFTL(G_TRI1, 24, 8), \ + __gsSP1Triangle_w1f(v0, v1, v2, flag) \ +}} + +/*** + *** Line + ***/ +#define gSPLine3D(pkt, v0, v1, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8); \ + _g->words.w1 = __gsSPLine3D_w1f(v0, v1, 0, flag); \ +} +#define gsSPLine3D(v0, v1, flag) \ +{{ \ + _SHIFTL(G_LINE3D, 24, 8), \ + __gsSPLine3D_w1f(v0, v1, 0, flag) \ +}} + +/*** + *** LineW + ***/ +/* these macros are the same as SPLine3D, except they have an + * additional parameter for width. The width is added to the "minimum" + * thickness, which is 1.5 pixels. The units for width are in + * half-pixel units, so a width of 1 translates to (.5 + 1.5) or + * a 2.0 pixels wide line. + */ +#define gSPLineW3D(pkt, v0, v1, wd, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_LINE3D, 24, 8); \ + _g->words.w1 = __gsSPLine3D_w1f(v0, v1, wd, flag); \ +} +#define gsSPLineW3D(v0, v1, wd, flag) \ +{{ \ + _SHIFTL(G_LINE3D, 24, 8), \ + __gsSPLine3D_w1f(v0, v1, wd, flag) \ +}} + +/*** + *** 1 Quadrangle + ***/ +#define gSP1Quadrangle(pkt, v0, v1, v2, v3, flag) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TRI2, 24, 8)| \ + __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)); \ + _g->words.w1 = __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag); \ +} + +#define gsSP1Quadrangle(v0, v1, v2, v3, flag) \ +{{ \ + (_SHIFTL(G_TRI2, 24, 8)| \ + __gsSP1Quadrangle_w1f(v0, v1, v2, v3, flag)), \ + __gsSP1Quadrangle_w2f(v0, v1, v2, v3, flag) \ +}} +#endif /* F3DEX_GBI_2 */ + +#if (defined(F3DLP_GBI)||defined(F3DEX_GBI)) +/*** + *** 2 Triangles + ***/ +#define gSP2Triangles(pkt, v00, v01, v02, flag0, v10, v11, v12, flag1) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TRI2, 24, 8)| \ + __gsSP1Triangle_w1f(v00, v01, v02, flag0)); \ + _g->words.w1 = __gsSP1Triangle_w1f(v10, v11, v12, flag1); \ +} + +#define gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) \ +{{ \ + (_SHIFTL(G_TRI2, 24, 8)| \ + __gsSP1Triangle_w1f(v00, v01, v02, flag0)), \ + __gsSP1Triangle_w1f(v10, v11, v12, flag1) \ +}} +#else +#define gSP2Triangles(pkt, v00, v01, v02, flag0, v10, v11, v12, flag1) \ +{ \ + gSP1Triangle(pkt, v00, v01, v02, flag0); \ + gSP1Triangle(pkt, v10, v11, v12, flag1); \ +} +#define gsSP2Triangles(v00, v01, v02, flag0, v10, v11, v12, flag1) \ + gsSP1Triangle(v00, v01, v02, flag0), \ + gsSP1Triangle(v10, v11, v12, flag1) +#endif /* F3DEX_GBI/F3DLP_GBI */ + +#if (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +#define gSPCullDisplayList(pkt,vstart,vend) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | \ + _SHIFTL((vstart)*2, 0, 16); \ + _g->words.w1 = _SHIFTL((vend)*2, 0, 16); \ +} + +#define gsSPCullDisplayList(vstart,vend) \ +{{ \ + _SHIFTL(G_CULLDL, 24, 8) | _SHIFTL((vstart)*2, 0, 16), \ + _SHIFTL((vend)*2, 0, 16) \ +}} + +#else +#define gSPCullDisplayList(pkt,vstart,vend) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_CULLDL, 24, 8) | \ + ((0x0f & (vstart))*40); \ + _g->words.w1 = (unsigned int)((0x0f & ((vend)+1))*40); \ +} + +#define gsSPCullDisplayList(vstart,vend) \ +{{ \ + _SHIFTL(G_CULLDL, 24, 8) | ((0x0f & (vstart))*40), \ + ((0x0f & ((vend)+1))*40) \ +}} +#endif + +#define gSPSegment(pkt, segment, base) \ + gMoveWd(pkt, G_MW_SEGMENT, (segment)*4, base) +#define gsSPSegment(segment, base) \ + gsMoveWd( G_MW_SEGMENT, (segment)*4, base) + +/* + * Clipping Macros + */ +#define FR_NEG_FRUSTRATIO_1 0x00000001 +#define FR_POS_FRUSTRATIO_1 0x0000ffff +#define FR_NEG_FRUSTRATIO_2 0x00000002 +#define FR_POS_FRUSTRATIO_2 0x0000fffe +#define FR_NEG_FRUSTRATIO_3 0x00000003 +#define FR_POS_FRUSTRATIO_3 0x0000fffd +#define FR_NEG_FRUSTRATIO_4 0x00000004 +#define FR_POS_FRUSTRATIO_4 0x0000fffc +#define FR_NEG_FRUSTRATIO_5 0x00000005 +#define FR_POS_FRUSTRATIO_5 0x0000fffb +#define FR_NEG_FRUSTRATIO_6 0x00000006 +#define FR_POS_FRUSTRATIO_6 0x0000fffa +/* + * r should be one of: FRUSTRATIO_1, FRUSTRATIO_2, FRUSTRATIO_3, ... FRUSTRATIO_6 + */ +#define gSPClipRatio(pkt, r) \ +{ \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RNX, FR_NEG_##r); \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RNY, FR_NEG_##r); \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RPX, FR_POS_##r); \ + gMoveWd(pkt, G_MW_CLIP, G_MWO_CLIP_RPY, FR_POS_##r); \ +} + +#define gsSPClipRatio(r) \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNX, FR_NEG_##r), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RNY, FR_NEG_##r), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPX, FR_POS_##r), \ + gsMoveWd(G_MW_CLIP, G_MWO_CLIP_RPY, FR_POS_##r) + +/* + * Insert values into Matrix + * + * where = element of matrix (byte offset) + * num = new element (32 bit value replacing 2 int or 2 frac matrix + * componants + */ +#ifdef F3DEX_GBI_2 +#define gSPInsertMatrix(pkt, where, num) \ + ERROR!! gSPInsertMatrix is no longer supported. +#define gsSPInsertMatrix(where, num) \ + ERROR!! gsSPInsertMatrix is no longer supported. +#else +#define gSPInsertMatrix(pkt, where, num) \ + gMoveWd(pkt, G_MW_MATRIX, where, num) +#define gsSPInsertMatrix(where, num) \ + gsMoveWd(G_MW_MATRIX, where, num) +#endif + +/* + * Load new matrix directly + * + * mptr = pointer to matrix + */ +#ifdef F3DEX_GBI_2 +#define gSPForceMatrix(pkt, mptr) \ +{ gDma2p((pkt),G_MOVEMEM,(mptr),sizeof(Mtx),G_MV_MATRIX,0); \ + gMoveWd((pkt), G_MW_FORCEMTX,0,0x00010000); \ +} +#define gsSPForceMatrix(mptr) \ + gsDma2p(G_MOVEMEM,(mptr),sizeof(Mtx),G_MV_MATRIX,0), \ + gsMoveWd(G_MW_FORCEMTX,0,0x00010000) + +#else /* F3DEX_GBI_2 */ +#define gSPForceMatrix(pkt, mptr) \ +{ \ + gDma1p(pkt, G_MOVEMEM, mptr, 16, G_MV_MATRIX_1); \ + gDma1p(pkt, G_MOVEMEM, (char *)(mptr)+16, 16, G_MV_MATRIX_2); \ + gDma1p(pkt, G_MOVEMEM, (char *)(mptr)+32, 16, G_MV_MATRIX_3); \ + gDma1p(pkt, G_MOVEMEM, (char *)(mptr)+48, 16, G_MV_MATRIX_4); \ +} +#define gsSPForceMatrix(mptr) \ + gsDma1p( G_MOVEMEM, mptr, 16, G_MV_MATRIX_1), \ + gsDma1p( G_MOVEMEM, (char *)(mptr)+16, 16, G_MV_MATRIX_2), \ + gsDma1p( G_MOVEMEM, (char *)(mptr)+32, 16, G_MV_MATRIX_3), \ + gsDma1p( G_MOVEMEM, (char *)(mptr)+48, 16, G_MV_MATRIX_4) +#endif /* F3DEX_GBI_2 */ + +/* + * Insert values into Points + * + * point = point number 0-15 + * where = which element of point to modify (byte offset into point) + * num = new value (32 bit) + */ +#if (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +# define gSPModifyVertex(pkt, vtx, where, val) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = (_SHIFTL(G_MODIFYVTX,24,8)| \ + _SHIFTL((where),16,8)|_SHIFTL((vtx)*2,0,16)); \ + _g->words.w1 = (unsigned int)(val); \ +} +# define gsSPModifyVertex(vtx, where, val) \ +{{ \ + _SHIFTL(G_MODIFYVTX,24,8)| \ + _SHIFTL((where),16,8)|_SHIFTL((vtx)*2,0,16), \ + (unsigned int)(val) \ +}} +#else +# define gSPModifyVertex(pkt, vtx, where, val) \ + gMoveWd(pkt, G_MW_POINTS, (vtx)*40+(where), val) +# define gsSPModifyVertex(vtx, where, val) \ + gsMoveWd(G_MW_POINTS, (vtx)*40+(where), val) +#endif + +#if (defined(F3DEX_GBI)||defined(F3DLP_GBI)) +/* + * gSPBranchLessZ Branch DL if (vtx.z) less than or equal (zval). + * + * dl = DL branch to + * vtx = Vertex + * zval = Screen depth + * near = Near plane + * far = Far plane + * flag = G_BZ_PERSP or G_BZ_ORTHO + */ + +#define G_BZ_PERSP 0 +#define G_BZ_ORTHO 1 + +#define G_DEPTOZSrg(zval, near, far, flag, zmin, zmax) \ +(((unsigned int)FTOFIX32(((flag) == G_BZ_PERSP ? \ + (1.0f-(float)(near)/(float)(zval)) / \ + (1.0f-(float)(near)/(float)(far )) : \ + ((float)(zval) - (float)(near)) / \ + ((float)(far ) - (float)(near))))) * \ + (((int)((zmax) - (zmin)))&~1) + (int)FTOFIX32(zmin)) + +#define G_DEPTOZS(zval, near, far, flag) \ + G_DEPTOZSrg(zval, near, far, flag, 0, G_MAXZ) + +#define gSPBranchLessZrg(pkt, dl, vtx, zval, near, far, flag, zmin, zmax) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1,24,8); \ + _g->words.w1 = (uintptr_t)(dl); \ + _g = (Gfx *)(pkt); \ + _g->words.w0 = (_SHIFTL(G_BRANCH_Z,24,8)| \ + _SHIFTL((vtx)*5,12,12)|_SHIFTL((vtx)*2,0,12)); \ + _g->words.w1 = G_DEPTOZSrg(zval, near, far, flag, zmin, zmax); \ +} + +#define gsSPBranchLessZrg(dl, vtx, zval, near, far, flag, zmin, zmax) \ +{{ _SHIFTL(G_RDPHALF_1,24,8), \ + (uintptr_t)(dl), }}, \ +{{ _SHIFTL(G_BRANCH_Z,24,8)|_SHIFTL((vtx)*5,12,12)|_SHIFTL((vtx)*2,0,12),\ + G_DEPTOZSrg(zval, near, far, flag, zmin, zmax), }} + +#define gSPBranchLessZ(pkt, dl, vtx, zval, near, far, flag) \ + gSPBranchLessZrg(pkt, dl, vtx, zval, near, far, flag, 0, G_MAXZ) +#define gsSPBranchLessZ(dl, vtx, zval, near, far, flag) \ + gsSPBranchLessZrg(dl, vtx, zval, near, far, flag, 0, G_MAXZ) + +/* + * gSPBranchLessZraw Branch DL if (vtx.z) less than or equal (raw zval). + * + * dl = DL branch to + * vtx = Vertex + * zval = Raw value of screen depth + */ +#define gSPBranchLessZraw(pkt, dl, vtx, zval) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1,24,8); \ + _g->words.w1 = (uintptr_t)(dl); \ + _g = (Gfx *)(pkt); \ + _g->words.w0 = (_SHIFTL(G_BRANCH_Z,24,8)| \ + _SHIFTL((vtx)*5,12,12)|_SHIFTL((vtx)*2,0,12)); \ + _g->words.w1 = (unsigned int)(zval); \ +} + +#define gsSPBranchLessZraw(dl, vtx, zval) \ +{{ _SHIFTL(G_RDPHALF_1,24,8), \ + (uintptr_t)(dl), }}, \ +{{ _SHIFTL(G_BRANCH_Z,24,8)|_SHIFTL((vtx)*5,12,12)|_SHIFTL((vtx)*2,0,12),\ + (unsigned int)(zval), }} + +/* + * gSPLoadUcode RSP loads specified ucode. + * + * uc_start = ucode text section start + * uc_dstart = ucode data section start + */ +#define gSPLoadUcodeEx(pkt, uc_start, uc_dstart, uc_dsize) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1,24,8); \ + _g->words.w1 = (uintptr_t)(uc_dstart); \ + _g = (Gfx *)(pkt); \ + _g->words.w0 = (_SHIFTL(G_LOAD_UCODE,24,8)| \ + _SHIFTL((int)(uc_dsize)-1,0,16)); \ + _g->words.w1 = (uintptr_t)(uc_start); \ +} + +#define gsSPLoadUcodeEx(uc_start, uc_dstart, uc_dsize) \ +{{ _SHIFTL(G_RDPHALF_1,24,8), \ + (uintptr_t)(uc_dstart), }}, \ +{{ _SHIFTL(G_LOAD_UCODE,24,8)| \ + _SHIFTL((int)(uc_dsize)-1,0,16), \ + (uintptr_t)(uc_start), }} + +#define gSPLoadUcode(pkt, uc_start, uc_dstart) \ + gSPLoadUcodeEx((pkt), (uc_start), (uc_dstart), SP_UCODE_DATA_SIZE) +#define gsSPLoadUcode(uc_start, uc_dstart) \ + gsSPLoadUcodeEx((uc_start), (uc_dstart), SP_UCODE_DATA_SIZE) + +#define gSPLoadUcodeL(pkt, ucode) \ + gSPLoadUcode((pkt), OS_K0_TO_PHYSICAL(&##ucode##TextStart), \ + OS_K0_TO_PHYSICAL(&##ucode##DataStart)) +#define gsSPLoadUcodeL(ucode) \ + gsSPLoadUcode(OS_K0_TO_PHYSICAL(&##ucode##TextStart), \ + OS_K0_TO_PHYSICAL(&##ucode##DataStart)) +#endif + +#ifdef F3DEX_GBI_2 +/* + * gSPDma_io DMA to/from DMEM/IMEM for DEBUG. + */ +#define gSPDma_io(pkt, flag, dmem, dram, size) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_DMA_IO,24,8)|_SHIFTL((flag),23,1)| \ + _SHIFTL((dmem)/8,13,10)|_SHIFTL((size)-1,0,12); \ + _g->words.w1 = (uintptr_t)(dram); \ +} + +#define gsSPDma_io(flag, dmem, dram, size) \ +{{ \ + _SHIFTL(G_DMA_IO,24,8)|_SHIFTL((flag),23,1)| \ + _SHIFTL((dmem)/8,13,10)|_SHIFTL((size)-1,0,12), \ + (uintptr_t)(dram) \ +}} + +#define gSPDmaRead(pkt,dmem,dram,size) gSPDma_io((pkt),0,(dmem),(dram),(size)) +#define gsSPDmaRead(dmem,dram,size) gsSPDma_io(0,(dmem),(dram),(size)) +#define gSPDmaWrite(pkt,dmem,dram,size) gSPDma_io((pkt),1,(dmem),(dram),(size)) +#define gsSPDmaWrite(dmem,dram,size) gsSPDma_io(1,(dmem),(dram),(size)) +#endif + +/* + * Lighting Macros + */ +#ifdef F3DEX_GBI_2 +# define NUML(n) ((n)*24) +#else +# define NUML(n) (((n)+1)*32 + 0x80000000) +#endif +#define NUMLIGHTS_0 1 +#define NUMLIGHTS_1 1 +#define NUMLIGHTS_2 2 +#define NUMLIGHTS_3 3 +#define NUMLIGHTS_4 4 +#define NUMLIGHTS_5 5 +#define NUMLIGHTS_6 6 +#define NUMLIGHTS_7 7 +/* + * n should be one of: NUMLIGHTS_0, NUMLIGHTS_1, ..., NUMLIGHTS_7 + * NOTE: in addition to the number of directional lights specified, + * there is always 1 ambient light + */ +#define gSPNumLights(pkt, n) \ + gMoveWd(pkt, G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) +#define gsSPNumLights(n) \ + gsMoveWd( G_MW_NUMLIGHT, G_MWO_NUMLIGHT, NUML(n)) + +#define LIGHT_1 1 +#define LIGHT_2 2 +#define LIGHT_3 3 +#define LIGHT_4 4 +#define LIGHT_5 5 +#define LIGHT_6 6 +#define LIGHT_7 7 +#define LIGHT_8 8 +/* + * l should point to a Light struct + * n should be one of: LIGHT_1, LIGHT_2, ..., LIGHT_8 + * NOTE: the highest numbered light is always the ambient light (eg if there are + * 3 directional lights defined: gsSPNumLights(NUMLIGHTS_3), then lights + * LIGHT_1 through LIGHT_3 will be the directional lights and light + * LIGHT_4 will be the ambient light. + */ +#ifdef F3DEX_GBI_2 +# define gSPLight(pkt, l, n) \ + gDma2p((pkt),G_MOVEMEM,(l),sizeof(Light),G_MV_LIGHT,(n)*24+24) +# define gsSPLight(l, n) \ + gsDma2p( G_MOVEMEM,(l),sizeof(Light),G_MV_LIGHT,(n)*24+24) +#else /* F3DEX_GBI_2 */ +# define gSPLight(pkt, l, n) \ + gDma1p(pkt, G_MOVEMEM, l, sizeof(Light),((n)-1)*2+G_MV_L0) +# define gsSPLight(l, n) \ + gsDma1p( G_MOVEMEM, l, sizeof(Light),((n)-1)*2+G_MV_L0) +#endif /* F3DEX_GBI_2 */ + +/* + * gSPLightColor changes color of light without recalculating light direction + * col is a 32 bit word with r,g,b,a (alpha is ignored) + * n should be one of LIGHT_1, LIGHT_2, ..., LIGHT_8 + */ +#define gSPLightColor(pkt, n, col) \ +{ \ + gMoveWd(pkt, G_MW_LIGHTCOL, G_MWO_a##n, col); \ + gMoveWd(pkt, G_MW_LIGHTCOL, G_MWO_b##n, col); \ +} +#define gsSPLightColor(n, col) \ + gsMoveWd(G_MW_LIGHTCOL, G_MWO_a##n, col), \ + gsMoveWd(G_MW_LIGHTCOL, G_MWO_b##n, col) + +/* These macros use a structure "name" which is init'd with the gdSPDefLights macros*/ + +#define gSPSetLights0(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_0); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.a,2); \ +} +#define gsSPSetLights0(name) \ + gsSPNumLights(NUMLIGHTS_0), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.a,2) + +#define gSPSetLights1(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_1); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.a,2); \ +} +#define gsSPSetLights1(name) \ + gsSPNumLights(NUMLIGHTS_1), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.a,2) + +#define gSPSetLights2(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_2); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.l[1],2); \ + gSPLight(pkt,&name.a,3); \ +} +#define gsSPSetLights2(name) \ + gsSPNumLights(NUMLIGHTS_2), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.l[1],2), \ + gsSPLight(&name.a,3) + +#define gSPSetLights3(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_3); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.l[1],2); \ + gSPLight(pkt,&name.l[2],3); \ + gSPLight(pkt,&name.a,4); \ +} +#define gsSPSetLights3(name) \ + gsSPNumLights(NUMLIGHTS_3), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.l[1],2), \ + gsSPLight(&name.l[2],3), \ + gsSPLight(&name.a,4) + +#define gSPSetLights4(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_4); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.l[1],2); \ + gSPLight(pkt,&name.l[2],3); \ + gSPLight(pkt,&name.l[3],4); \ + gSPLight(pkt,&name.a,5); \ +} +#define gsSPSetLights4(name) \ + gsSPNumLights(NUMLIGHTS_4), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.l[1],2), \ + gsSPLight(&name.l[2],3), \ + gsSPLight(&name.l[3],4), \ + gsSPLight(&name.a,5) + +#define gSPSetLights5(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_5); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.l[1],2); \ + gSPLight(pkt,&name.l[2],3); \ + gSPLight(pkt,&name.l[3],4); \ + gSPLight(pkt,&name.l[4],5); \ + gSPLight(pkt,&name.a,6); \ +} + +#define gsSPSetLights5(name) \ + gsSPNumLights(NUMLIGHTS_5), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.l[1],2), \ + gsSPLight(&name.l[2],3), \ + gsSPLight(&name.l[3],4), \ + gsSPLight(&name.l[4],5), \ + gsSPLight(&name.a,6) + +#define gSPSetLights6(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_6); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.l[1],2); \ + gSPLight(pkt,&name.l[2],3); \ + gSPLight(pkt,&name.l[3],4); \ + gSPLight(pkt,&name.l[4],5); \ + gSPLight(pkt,&name.l[5],6); \ + gSPLight(pkt,&name.a,7); \ +} + +#define gsSPSetLights6(name) \ + gsSPNumLights(NUMLIGHTS_6), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.l[1],2), \ + gsSPLight(&name.l[2],3), \ + gsSPLight(&name.l[3],4), \ + gsSPLight(&name.l[4],5), \ + gsSPLight(&name.l[5],6), \ + gsSPLight(&name.a,7) + +#define gSPSetLights7(pkt,name) \ +{ \ + gSPNumLights(pkt,NUMLIGHTS_7); \ + gSPLight(pkt,&name.l[0],1); \ + gSPLight(pkt,&name.l[1],2); \ + gSPLight(pkt,&name.l[2],3); \ + gSPLight(pkt,&name.l[3],4); \ + gSPLight(pkt,&name.l[4],5); \ + gSPLight(pkt,&name.l[5],6); \ + gSPLight(pkt,&name.l[6],7); \ + gSPLight(pkt,&name.a,8); \ +} + +#define gsSPSetLights7(name) \ + gsSPNumLights(NUMLIGHTS_7), \ + gsSPLight(&name.l[0],1), \ + gsSPLight(&name.l[1],2), \ + gsSPLight(&name.l[2],3), \ + gsSPLight(&name.l[3],4), \ + gsSPLight(&name.l[4],5), \ + gsSPLight(&name.l[5],6), \ + gsSPLight(&name.l[6],7), \ + gsSPLight(&name.a,8) + +/* + * Reflection/Hiliting Macros + */ +#ifdef F3DEX_GBI_2 +# define gSPLookAtX(pkt, l) \ + gDma2p((pkt),G_MOVEMEM,(l),sizeof(Light),G_MV_LIGHT,G_MVO_LOOKATX) +# define gsSPLookAtX(l) \ + gsDma2p( G_MOVEMEM,(l),sizeof(Light),G_MV_LIGHT,G_MVO_LOOKATX) +# define gSPLookAtY(pkt, l) \ + gDma2p((pkt),G_MOVEMEM,(l),sizeof(Light),G_MV_LIGHT,G_MVO_LOOKATY) +# define gsSPLookAtY(l) \ + gsDma2p( G_MOVEMEM,(l),sizeof(Light),G_MV_LIGHT,G_MVO_LOOKATY) +#else /* F3DEX_GBI_2 */ +# define gSPLookAtX(pkt, l) \ + gDma1p(pkt, G_MOVEMEM, l, sizeof(Light),G_MV_LOOKATX) +# define gsSPLookAtX(l) \ + gsDma1p( G_MOVEMEM, l, sizeof(Light),G_MV_LOOKATX) +# define gSPLookAtY(pkt, l) \ + gDma1p(pkt, G_MOVEMEM, l, sizeof(Light),G_MV_LOOKATY) +# define gsSPLookAtY(l) \ + gsDma1p( G_MOVEMEM, l, sizeof(Light),G_MV_LOOKATY) +#endif /* F3DEX_GBI_2 */ + +#define gSPLookAt(pkt, la) \ +{ \ + gSPLookAtX(pkt,la) \ + gSPLookAtY(pkt,(char *)(la)+16) \ +} +#define gsSPLookAt(la) \ + gsSPLookAtX(la), \ + gsSPLookAtY((char *)(la)+16) + +#define gDPSetHilite1Tile(pkt, tile, hilite, width, height) \ + gDPSetTileSize(pkt, tile, (hilite)->h.x1 & 0xfff, (hilite)->h.y1 & 0xfff, \ + ((((width)-1)*4)+(hilite)->h.x1) & 0xfff, ((((height)-1)*4)+(hilite)->h.y1) & 0xfff) + +#define gDPSetHilite2Tile(pkt, tile, hilite, width, height) \ + gDPSetTileSize(pkt, tile, (hilite)->h.x2 & 0xfff, (hilite)->h.y2 & 0xfff, \ + ((((width)-1)*4)+(hilite)->h.x2) & 0xfff, ((((height)-1)*4)+(hilite)->h.y2) & 0xfff) + + +/* + * FOG macros + * fm = z multiplier + * fo = z offset + * FOG FORMULA: alpha(fog) = (eyespace z) * fm + fo CLAMPED 0 to 255 + * note: (eyespace z) ranges -1 to 1 + * + * Alternate method of setting fog: + * min, max: range 0 to 1000: 0=nearplane, 1000=farplane + * min is where fog begins (usually less than max and often 0) + * max is where fog is thickest (usually 1000) + * + */ +#define gSPFogFactor(pkt, fm, fo) \ + gMoveWd(pkt, G_MW_FOG, G_MWO_FOG, \ + (_SHIFTL(fm,16,16) | _SHIFTL(fo,0,16))) + +#define gsSPFogFactor(fm, fo) \ + gsMoveWd(G_MW_FOG, G_MWO_FOG, \ + (_SHIFTL(fm,16,16) | _SHIFTL(fo,0,16))) + +#define gSPFogPosition(pkt, min, max) \ + gMoveWd(pkt, G_MW_FOG, G_MWO_FOG, \ + (_SHIFTL((128000/((max)-(min))),16,16) | \ + _SHIFTL(((500-(min))*256/((max)-(min))),0,16))) + +#define gsSPFogPosition(min, max) \ + gsMoveWd(G_MW_FOG, G_MWO_FOG, \ + (_SHIFTL((128000/((max)-(min))),16,16) | \ + _SHIFTL(((500-(min))*256/((max)-(min))),0,16))) + +#ifdef F3DEX_GBI_2 +/* + * Macros to turn texture on/off + */ +# define gSPTexture(pkt, s, t, level, tile, on) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE,24,8) | \ + _SHIFTL(BOWTIE_VAL,16,8) | \ + _SHIFTL((level),11,3) | _SHIFTL((tile),8,3) | \ + _SHIFTL((on),1,7)); \ + _g->words.w1 = (_SHIFTL((s),16,16) | _SHIFTL((t),0,16)); \ +} +# define gsSPTexture(s, t, level, tile, on) \ +{{ \ + (_SHIFTL(G_TEXTURE,24,8) | _SHIFTL(BOWTIE_VAL,16,8) | \ + _SHIFTL((level),11,3) | _SHIFTL((tile),8,3) | _SHIFTL((on),1,7)),\ + (_SHIFTL((s),16,16) | _SHIFTL((t),0,16)) \ +}} +/* + * Different version of SPTexture macro, has an additional parameter + * which is currently reserved in the microcode. + */ +# define gSPTextureL(pkt, s, t, level, xparam, tile, on) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE,24,8) | \ + _SHIFTL((xparam),16,8) | \ + _SHIFTL((level),11,3) | _SHIFTL((tile),8,3) | \ + _SHIFTL((on),1,7)); \ + _g->words.w1 = (_SHIFTL((s),16,16) | _SHIFTL((t),0,16)); \ +} +# define gsSPTextureL(s, t, level, xparam, tile, on) \ +{{ \ + (_SHIFTL(G_TEXTURE,24,8) | _SHIFTL((xparam),16,8) | \ + _SHIFTL((level),11,3) | _SHIFTL((tile),8,3) | _SHIFTL((on),1,7)),\ + (_SHIFTL((s),16,16) | _SHIFTL((t),0,16)) \ +}} +#else +/* + * Macros to turn texture on/off + */ +# define gSPTexture(pkt, s, t, level, tile, on) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE,24,8)|_SHIFTL(BOWTIE_VAL,16,8)|\ + _SHIFTL((level),11,3)|_SHIFTL((tile),8,3)| \ + _SHIFTL((on),0,8)); \ + _g->words.w1 = (_SHIFTL((s),16,16)|_SHIFTL((t),0,16)); \ +} +# define gsSPTexture(s, t, level, tile, on) \ +{{ \ + (_SHIFTL(G_TEXTURE,24,8)|_SHIFTL(BOWTIE_VAL,16,8)| \ + _SHIFTL((level),11,3)|_SHIFTL((tile),8,3)|_SHIFTL((on),0,8)), \ + (_SHIFTL((s),16,16)|_SHIFTL((t),0,16)) \ +}} +/* + * Different version of SPTexture macro, has an additional parameter + * which is currently reserved in the microcode. + */ +# define gSPTextureL(pkt, s, t, level, xparam, tile, on) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXTURE,24,8)|_SHIFTL((xparam),16,8)| \ + _SHIFTL((level),11,3)|_SHIFTL((tile),8,3)| \ + _SHIFTL((on),0,8)); \ + _g->words.w1 = (_SHIFTL((s),16,16)|_SHIFTL((t),0,16)); \ +} +# define gsSPTextureL(s, t, level, xparam, tile, on) \ +{{ \ + (_SHIFTL(G_TEXTURE,24,8)|_SHIFTL((xparam),16,8)| \ + _SHIFTL((level),11,3)|_SHIFTL((tile),8,3)|_SHIFTL((on),0,8)), \ + (_SHIFTL((s),16,16)|_SHIFTL((t),0,16)) \ +}} +#endif + +#ifndef F3D_OLD +//# define gSPPerspNormalize(pkt, s) gMoveWd(pkt, G_MW_PERSPNORM, 0, (s)) +# define gsSPPerspNormalize(s) gsMoveWd( G_MW_PERSPNORM, 0, (s)) +#else +# define gSPPerspNormalize(pkt, s) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8); \ + _g->words.w1 = (s); \ +} +# define gsSPPerspNormalize(s) \ +{{ \ + _SHIFTL(G_RDPHALF_1, 24, 8), \ + (s) \ +}} +#endif + +#ifdef F3DEX_GBI_2 +# define gSPPopMatrixN(pkt, n, num) gDma2p((pkt),G_POPMTX,(num)*64,64,2,0) +# define gsSPPopMatrixN(n, num) gsDma2p( G_POPMTX,(num)*64,64,2,0) +# define gSPPopMatrix(pkt, n) gSPPopMatrixN((pkt), (n), 1) +# define gsSPPopMatrix(n) gsSPPopMatrixN( (n), 1) +#else /* F3DEX_GBI_2 */ +# define gSPPopMatrix(pkt, n) gImmp1(pkt, G_POPMTX, n) +# define gsSPPopMatrix(n) gsImmp1( G_POPMTX, n) +#endif /* F3DEX_GBI_2 */ + +#define gSPEndDisplayList(pkt) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_ENDDL, 24, 8); \ + _g->words.w1 = 0; \ +} + +#define gsSPEndDisplayList() \ +{{ \ + _SHIFTL(G_ENDDL, 24, 8), 0 \ +}} + +#ifdef F3DEX_GBI_2 +/* + * One gSPGeometryMode(pkt,c,s) GBI is equal to these two GBIs. + * + * gSPClearGeometryMode(pkt,c) + * gSPSetGeometryMode(pkt,s) + * + * gSPLoadGeometryMode(pkt, word) sets GeometryMode directly. + */ +#define gSPGeometryMode(pkt, c, s) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_GEOMETRYMODE,24,8)|_SHIFTL(~(u32)(c),0,24);\ + _g->words.w1 = (u32)(s); \ +} + +#define gsSPGeometryMode(c, s) \ +{{ \ + (_SHIFTL(G_GEOMETRYMODE,24,8)|_SHIFTL(~(u32)(c),0,24)),(u32)(s) \ +}} +#define gSPSetGeometryMode(pkt, word) gSPGeometryMode((pkt),0,(word)) +#define gsSPSetGeometryMode(word) gsSPGeometryMode(0,(word)) +#define gSPClearGeometryMode(pkt, word) gSPGeometryMode((pkt),(word),0) +#define gsSPClearGeometryMode(word) gsSPGeometryMode((word),0) +#define gSPLoadGeometryMode(pkt, word) gSPGeometryMode((pkt),-1,(word)) +#define gsSPLoadGeometryMode(word) gsSPGeometryMode(-1,(word)) +#define gsSPGeometryModeSetFirst(c, s) gsSPGeometryMode(c, s) +#else /* F3DEX_GBI_2 */ +/* +#define gSPSetGeometryMode(pkt, word) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETGEOMETRYMODE, 24, 8); \ + _g->words.w1 = (unsigned int)(word); \ +} +*/ + +#define gsSPSetGeometryMode(word) \ +{{ \ + _SHIFTL(G_SETGEOMETRYMODE, 24, 8), (unsigned int)(word) \ +}} + +/* +#define gSPClearGeometryMode(pkt, word) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8); \ + _g->words.w1 = (unsigned int)(word); \ +} +*/ + +#define gsSPClearGeometryMode(word) \ +{{ \ + _SHIFTL(G_CLEARGEOMETRYMODE, 24, 8), (unsigned int)(word) \ +}} + +/* + * gsSPGeometryMode + * In Fast3DEX2 it is better to use this, as the RSP geometry mode + * is able to be set and cleared in a single command. + */ +#define gsSPGeometryMode(c, s) \ + gsSPClearGeometryMode(c), \ + gsSPSetGeometryMode(s) +#define gsSPGeometryModeSetFirst(c, s) \ + gsSPSetGeometryMode(s), \ + gsSPClearGeometryMode(c) +#endif /* F3DEX_GBI_2 */ + +#ifdef F3DEX_GBI_2 +#define gSPSetOtherMode(pkt, cmd, sft, len, data) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = (_SHIFTL(cmd,24,8)|_SHIFTL(32-(sft)-(len),8,8)| \ + _SHIFTL((len)-1,0,8)); \ + _g->words.w1 = (unsigned int)(data); \ +} + +#define gsSPSetOtherMode(cmd, sft, len, data) \ +{{ \ + _SHIFTL(cmd,24,8)|_SHIFTL(32-(sft)-(len),8,8)|_SHIFTL((len)-1,0,8), \ + (unsigned int)(data) \ +}} +#else +#define gSPSetOtherMode(pkt, cmd, sft, len, data) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | \ + _SHIFTL(len, 0, 8)); \ + _g->words.w1 = (unsigned int)(data); \ +} + +#define gsSPSetOtherMode(cmd, sft, len, data) \ +{{ \ + _SHIFTL(cmd, 24, 8) | _SHIFTL(sft, 8, 8) | _SHIFTL(len, 0, 8), \ + (unsigned int)(data) \ +}} +#endif + +/* + * RDP setothermode register commands - register shadowed in RSP + */ +#define gDPPipelineMode(pkt, mode) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) +#define gsDPPipelineMode(mode) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_PIPELINE, 1, mode) + +#define gDPSetCycleType(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_CYCLETYPE, 2, type) +#define gsDPSetCycleType(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_CYCLETYPE, 2, type) + +#define gDPSetTexturePersp(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTPERSP, 1, type) +#define gsDPSetTexturePersp(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTPERSP, 1, type) + +#define gDPSetTextureDetail(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTDETAIL, 2, type) +#define gsDPSetTextureDetail(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTDETAIL, 2, type) + +#define gDPSetTextureLOD(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTLOD, 1, type) +#define gsDPSetTextureLOD(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTLOD, 1, type) + +#define gDPSetTextureLUT(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTLUT, 2, type) +#define gsDPSetTextureLUT(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTLUT, 2, type) + +#define gDPSetTextureFilter(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTFILT, 2, type) +#define gsDPSetTextureFilter(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTFILT, 2, type) + +#define gDPSetTextureConvert(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_TEXTCONV, 3, type) +#define gsDPSetTextureConvert(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_TEXTCONV, 3, type) + +#define gDPSetCombineKey(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_COMBKEY, 1, type) +#define gsDPSetCombineKey(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_COMBKEY, 1, type) + +#ifndef _HW_VERSION_1 +#define gDPSetColorDither(pkt, mode) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_RGBDITHER, 2, mode) +#define gsDPSetColorDither(mode) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_RGBDITHER, 2, mode) +#else +#define gDPSetColorDither(pkt, mode) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_COLORDITHER, 1, mode) +#define gsDPSetColorDither(mode) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_COLORDITHER, 1, mode) +#endif + +#ifndef _HW_VERSION_1 +#define gDPSetAlphaDither(pkt, mode) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 2, mode) +#define gsDPSetAlphaDither(mode) \ + gsSPSetOtherMode(G_SETOTHERMODE_H, G_MDSFT_ALPHADITHER, 2, mode) +#endif + +/* 'blendmask' is not supported anymore. + * The bits are reserved for future use. + * Fri May 26 13:45:55 PDT 1995 + */ +#define gDPSetBlendMask(pkt, mask) gDPNoOp(pkt) +#define gsDPSetBlendMask(mask) gsDPNoOp() + +#define gDPSetAlphaCompare(pkt, type) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_ALPHACOMPARE, 2, type) +#define gsDPSetAlphaCompare(type) \ + gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_ALPHACOMPARE, 2, type) + +#define gDPSetDepthSource(pkt, src) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_ZSRCSEL, 1, src) +#define gsDPSetDepthSource(src) \ + gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_ZSRCSEL, 1, src) + +/* +#define gDPSetRenderMode(pkt, c0, c1) \ + gSPSetOtherMode(pkt, G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, \ + (c0) | (c1)) +*/ +#define gsDPSetRenderMode(c0, c1) \ + gsSPSetOtherMode(G_SETOTHERMODE_L, G_MDSFT_RENDERMODE, 29, \ + (c0) | (c1)) + +#define gSetImage(pkt, cmd, fmt, siz, width, i) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | \ + _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12); \ + _g->words.w1 = (uintptr_t)(i); \ +} + +#define gsSetImage(cmd, fmt, siz, width, i) \ +{{ \ + _SHIFTL(cmd, 24, 8) | _SHIFTL(fmt, 21, 3) | \ + _SHIFTL(siz, 19, 2) | _SHIFTL((width)-1, 0, 12), \ + (uintptr_t)(i) \ +}} + +#define gDPSetColorImage(pkt, f, s, w, i) gSetImage(pkt, G_SETCIMG, f, s, w, i) +#define gsDPSetColorImage(f, s, w, i) gsSetImage(G_SETCIMG, f, s, w, i) + + +/* use these for new code */ +#define gDPSetDepthImage(pkt, i) gSetImage(pkt, G_SETZIMG, 0, 0, 1, i) +#define gsDPSetDepthImage(i) gsSetImage(G_SETZIMG, 0, 0, 1, i) +/* kept for compatibility */ +#define gDPSetMaskImage(pkt, i) gDPSetDepthImage(pkt, i) +#define gsDPSetMaskImage(i) gsDPSetDepthImage(i) + +#define gDPSetTextureImage(pkt, f, s, w, i) gSetImage(pkt, G_SETTIMG, f, s, w, i) +#define gsDPSetTextureImage(f, s, w, i) gsSetImage(G_SETTIMG, f, s, w, i) + +/* + * RDP macros + */ + +#define gDPSetCombine(pkt, muxs0, muxs1) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24);\ + _g->words.w1 = (unsigned int)(muxs1); \ +} + +#define gsDPSetCombine(muxs0, muxs1) \ +{{ \ + _SHIFTL(G_SETCOMBINE, 24, 8) | _SHIFTL(muxs0, 0, 24), \ + (unsigned int)(muxs1) \ +}} + +#define GCCc0w0(saRGB0, mRGB0, saA0, mA0) \ + (_SHIFTL((saRGB0), 20, 4) | _SHIFTL((mRGB0), 15, 5) | \ + _SHIFTL((saA0), 12, 3) | _SHIFTL((mA0), 9, 3)) + +#define GCCc1w0(saRGB1, mRGB1) \ + (_SHIFTL((saRGB1), 5, 4) | _SHIFTL((mRGB1), 0, 5)) + +#define GCCc0w1(sbRGB0, aRGB0, sbA0, aA0) \ + (_SHIFTL((sbRGB0), 28, 4) | _SHIFTL((aRGB0), 15, 3) | \ + _SHIFTL((sbA0), 12, 3) | _SHIFTL((aA0), 9, 3)) + +#define GCCc1w1(sbRGB1, saA1, mA1, aRGB1, sbA1, aA1) \ + (_SHIFTL((sbRGB1), 24, 4) | _SHIFTL((saA1), 21, 3) | \ + _SHIFTL((mA1), 18, 3) | _SHIFTL((aRGB1), 6, 3) | \ + _SHIFTL((sbA1), 3, 3) | _SHIFTL((aA1), 0, 3)) + +#define gDPSetCombineLERP(pkt, a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, \ + a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETCOMBINE, 24, 8) | \ + _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, \ + G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \ + GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1), \ + 0, 24); \ + _g->words.w1 = (unsigned int)(GCCc0w1(G_CCMUX_##b0, \ + G_CCMUX_##d0, \ + G_ACMUX_##Ab0, \ + G_ACMUX_##Ad0) | \ + GCCc1w1(G_CCMUX_##b1, \ + G_ACMUX_##Aa1, \ + G_ACMUX_##Ac1, \ + G_CCMUX_##d1, \ + G_ACMUX_##Ab1, \ + G_ACMUX_##Ad1)); \ +} + +#define gsDPSetCombineLERP(a0, b0, c0, d0, Aa0, Ab0, Ac0, Ad0, \ + a1, b1, c1, d1, Aa1, Ab1, Ac1, Ad1) \ +{{ \ + _SHIFTL(G_SETCOMBINE, 24, 8) | \ + _SHIFTL(GCCc0w0(G_CCMUX_##a0, G_CCMUX_##c0, \ + G_ACMUX_##Aa0, G_ACMUX_##Ac0) | \ + GCCc1w0(G_CCMUX_##a1, G_CCMUX_##c1), 0, 24), \ + (unsigned int)(GCCc0w1(G_CCMUX_##b0, G_CCMUX_##d0, \ + G_ACMUX_##Ab0, G_ACMUX_##Ad0) | \ + GCCc1w1(G_CCMUX_##b1, G_ACMUX_##Aa1, \ + G_ACMUX_##Ac1, G_CCMUX_##d1, \ + G_ACMUX_##Ab1, G_ACMUX_##Ad1)) \ +}} + +/* + * SetCombineMode macros are NOT redunant. It allow the C preprocessor + * to substitute single parameter which includes commas in the token and + * rescan for higher parameter count macro substitution. + * + * eg. gsDPSetCombineMode(G_CC_MODULATE, G_CC_MODULATE) turns into + * gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, + * TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0) + */ + +#define gDPSetCombineMode(pkt, a, b) gDPSetCombineLERP(pkt, a, b) +#define gsDPSetCombineMode(a, b) gsDPSetCombineLERP(a, b) + +#define gDPSetColor(pkt, c, d) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(c, 24, 8); \ + _g->words.w1 = (unsigned int)(d); \ +} + +#define gsDPSetColor(c, d) \ +{{ \ + _SHIFTL(c, 24, 8), (unsigned int)(d) \ +}} + +#define DPRGBColor(pkt, cmd, r, g, b, a) \ + gDPSetColor(pkt, cmd, \ + (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | \ + _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8))) +#define sDPRGBColor(cmd, r, g, b, a) \ + gsDPSetColor(cmd, \ + (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | \ + _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8))) + +#define gDPSetEnvColor(pkt, r, g, b, a) \ + DPRGBColor(pkt, G_SETENVCOLOR, r,g,b,a) +#define gsDPSetEnvColor(r, g, b, a) \ + sDPRGBColor(G_SETENVCOLOR, r,g,b,a) +#define gDPSetBlendColor(pkt, r, g, b, a) \ + DPRGBColor(pkt, G_SETBLENDCOLOR, r,g,b,a) +#define gsDPSetBlendColor(r, g, b, a) \ + sDPRGBColor(G_SETBLENDCOLOR, r,g,b,a) +#define gDPSetFogColor(pkt, r, g, b, a) \ + DPRGBColor(pkt, G_SETFOGCOLOR, r,g,b,a) +#define gsDPSetFogColor(r, g, b, a) \ + sDPRGBColor(G_SETFOGCOLOR, r,g,b,a) +#define gDPSetFillColor(pkt, d) \ + gDPSetColor(pkt, G_SETFILLCOLOR, (d)) +#define gsDPSetFillColor(d) \ + gsDPSetColor(G_SETFILLCOLOR, (d)) + +#define gDPSetPrimDepth(pkt, z, dz) \ + gDPSetColor(pkt, G_SETPRIMDEPTH, \ + _SHIFTL(z, 16, 16) | _SHIFTL(dz, 0, 16)) +#define gsDPSetPrimDepth(z, dz) \ + gsDPSetColor(G_SETPRIMDEPTH, _SHIFTL(z, 16, 16) | \ + _SHIFTL(dz, 0, 16)) + +#define gDPSetPrimColor(pkt, m, l, r, g, b, a) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETPRIMCOLOR, 24, 8) | \ + _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8)); \ + _g->words.w1 = (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | \ + _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)); \ +} + +#define gsDPSetPrimColor(m, l, r, g, b, a) \ +{{ \ + (_SHIFTL(G_SETPRIMCOLOR, 24, 8) | _SHIFTL(m, 8, 8) | \ + _SHIFTL(l, 0, 8)), \ + (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | _SHIFTL(b, 8, 8) | \ + _SHIFTL(a, 0, 8)) \ +}} + +/* + * gDPSetOtherMode (This is for expert user.) + * + * This command makes all othermode parameters set. + * Do not use this command in the same DL with another g*SPSetOtherMode DLs. + * + * [Usage] + * gDPSetOtherMode(pkt, modeA, modeB) + * + * 'modeA' is described all parameters of GroupA GBI command. + * 'modeB' is also described all parameters of GroupB GBI command. + * + * GroupA: + * gDPPipelineMode, gDPSetCycleType, gSPSetTexturePersp, + * gDPSetTextureDetail, gDPSetTextureLOD, gDPSetTextureLUT, + * gDPSetTextureFilter, gDPSetTextureConvert, gDPSetCombineKey, + * gDPSetColorDither, gDPSetAlphaDither + * + * GroupB: + * gDPSetAlphaCompare, gDPSetDepthSource, gDPSetRenderMode + * + * Use 'OR' operation to get modeA and modeB. + * + * modeA = G_PM_* | G_CYC_* | G_TP_* | G_TD_* | G_TL_* | G_TT_* | G_TF_* + * G_TC_* | G_CK_* | G_CD_* | G_AD_*; + * + * modeB = G_AC_* | G_ZS_* | G_RM_* | G_RM_*2; + */ +#define gDPSetOtherMode(pkt, mode0, mode1) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_RDPSETOTHERMODE,24,8)|_SHIFTL(mode0,0,24);\ + _g->words.w1 = (unsigned int)(mode1); \ +} + +#define gsDPSetOtherMode(mode0, mode1) \ +{{ \ + _SHIFTL(G_RDPSETOTHERMODE,24,8)|_SHIFTL(mode0,0,24), \ + (unsigned int)(mode1) \ +}} + +/* + * Texturing macros + */ + +/* These are also defined defined above for Sprite Microcode */ + +#define G_TX_LOADTILE 7 +#define G_TX_RENDERTILE 0 + +#define G_TX_NOMIRROR 0 +#define G_TX_WRAP 0 +#define G_TX_MIRROR 0x1 +#define G_TX_CLAMP 0x2 +#define G_TX_NOMASK 0 +#define G_TX_NOLOD 0 + + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +/* + * Dxt is the inverse of the number of 64-bit words in a line of + * the texture being loaded using the load_block command. If + * there are any 1's to the right of the 11th fractional bit, + * dxt should be rounded up. The following macros accomplish + * this. The 4b macros are a special case since 4-bit textures + * are loaded as 8-bit textures. Dxt is fixed point 1.11. RJM + */ +#define G_TX_DXT_FRAC 11 + +/* + * For RCP 2.0, the maximum number of texels that can be loaded + * using a load_block command is 2048. In order to load the total + * 4kB of Tmem, change the texel size when loading to be G_IM_SIZ_16b, + * then change the tile to the proper texel size after the load. + * The g*DPLoadTextureBlock macros already do this, so this change + * will be transparent if you use these macros. If you use + * the g*DPLoadBlock macros directly, you will need to handle this + * tile manipulation yourself. RJM. + */ +#ifdef _HW_VERSION_1 +#define G_TX_LDBLK_MAX_TXL 4095 +#else +#define G_TX_LDBLK_MAX_TXL 2047 +#endif /* _HW_VERSION_1 */ + +#define TXL2WORDS(txls, b_txl) MAX(1, ((txls)*(b_txl)/8)) +#define CALC_DXT(width, b_txl) \ + (((1 << G_TX_DXT_FRAC) + TXL2WORDS(width, b_txl) - 1) / \ + TXL2WORDS(width, b_txl)) + +#define TXL2WORDS_4b(txls) MAX(1, ((txls)/16)) +#define CALC_DXT_4b(width) \ + (((1 << G_TX_DXT_FRAC) + TXL2WORDS_4b(width) - 1) / \ + TXL2WORDS_4b(width)) + +#define gDPLoadTileGeneric(pkt, c, tile, uls, ult, lrs, lrt) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | \ + _SHIFTL(ult, 0, 12); \ + _g->words.w1 = _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | \ + _SHIFTL(lrt, 0, 12); \ +} + +#define gsDPLoadTileGeneric(c, tile, uls, ult, lrs, lrt) \ +{{ \ + _SHIFTL(c, 24, 8) | _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12), \ + _SHIFTL(tile, 24, 3) | _SHIFTL(lrs, 12, 12) | _SHIFTL(lrt, 0, 12)\ +}} + +#define gDPSetTileSize(pkt, t, uls, ult, lrs, lrt) \ + gDPLoadTileGeneric(pkt, G_SETTILESIZE, t, uls, ult, lrs, lrt) +#define gsDPSetTileSize(t, uls, ult, lrs, lrt) \ + gsDPLoadTileGeneric(G_SETTILESIZE, t, uls, ult, lrs, lrt) +#define gDPLoadTile(pkt, t, uls, ult, lrs, lrt) \ + gDPLoadTileGeneric(pkt, G_LOADTILE, t, uls, ult, lrs, lrt) +#define gsDPLoadTile(t, uls, ult, lrs, lrt) \ + gsDPLoadTileGeneric(G_LOADTILE, t, uls, ult, lrs, lrt) + +#define gDPSetTile(pkt, fmt, siz, line, tmem, tile, palette, cmt, \ + maskt, shiftt, cms, masks, shifts) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) |\ + _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) | \ + _SHIFTL(tmem, 0, 9); \ + _g->words.w1 = _SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | \ + _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \ + _SHIFTL(shiftt, 10, 4) |_SHIFTL(cms, 8, 2) | \ + _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4); \ +} + +#define gsDPSetTile(fmt, siz, line, tmem, tile, palette, cmt, \ + maskt, shiftt, cms, masks, shifts) \ +{{ \ + (_SHIFTL(G_SETTILE, 24, 8) | _SHIFTL(fmt, 21, 3) | \ + _SHIFTL(siz, 19, 2) | _SHIFTL(line, 9, 9) | _SHIFTL(tmem, 0, 9)),\ + (_SHIFTL(tile, 24, 3) | _SHIFTL(palette, 20, 4) | \ + _SHIFTL(cmt, 18, 2) | _SHIFTL(maskt, 14, 4) | \ + _SHIFTL(shiftt, 10, 4) | _SHIFTL(cms, 8, 2) | \ + _SHIFTL(masks, 4, 4) | _SHIFTL(shifts, 0, 4)) \ +}} + +/* + * For RCP 2.0, the maximum number of texels that can be loaded + * using a load_block command is 2048. In order to load the total + * 4kB of Tmem, change the texel size when loading to be G_IM_SIZ_16b, + * then change the tile to the proper texel size after the load. + * The g*DPLoadTextureBlock macros already do this, so this change + * will be transparent if you use these macros. If you use + * the g*DPLoadBlock macros directly, you will need to handle this + * tile manipulation yourself. RJM. + */ +#define gDPLoadBlock(pkt, tile, uls, ult, lrs, dxt) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_LOADBLOCK, 24, 8) | \ + _SHIFTL(uls, 12, 12) | _SHIFTL(ult, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | \ + _SHIFTL((MIN(lrs,G_TX_LDBLK_MAX_TXL)), 12, 12) |\ + _SHIFTL(dxt, 0, 12)); \ +} + +#define gsDPLoadBlock(tile, uls, ult, lrs, dxt) \ +{{ \ + (_SHIFTL(G_LOADBLOCK, 24, 8) | _SHIFTL(uls, 12, 12) | \ + _SHIFTL(ult, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | \ + _SHIFTL((MIN(lrs,G_TX_LDBLK_MAX_TXL)), 12, 12) | \ + _SHIFTL(dxt, 0, 12)) \ +}} + +#define gDPLoadTLUTCmd(pkt, tile, count) \ +{ \ + Gfx *_g = (Gfx *)pkt; \ + \ + _g->words.w0 = _SHIFTL(G_LOADTLUT, 24, 8); \ + _g->words.w1 = _SHIFTL((tile), 24, 3) | _SHIFTL((count), 14, 10);\ +} + +#define gsDPLoadTLUTCmd(tile, count) \ +{{ \ + _SHIFTL(G_LOADTLUT, 24, 8), \ + _SHIFTL((tile), 24, 3) | _SHIFTL((count), 14, 10) \ +}} + +#define gDPLoadTextureBlock(pkt, timg, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT) -1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, \ + (((width) * siz##_LINE_BYTES)+7)>>3, 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +#define gDPLoadTextureBlockYuv(pkt, timg, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT) -1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, \ + (((width) * 1)+7)>>3, 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* Load fix rww 27jun95 */ +/* The S at the end means odd lines are already word Swapped */ + +#define gDPLoadTextureBlockS(pkt, timg, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1,0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, \ + (((width) * siz##_LINE_BYTES)+7)>>3, 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* + * Allow tmem address and render tile to be specified. + * The S at the end means odd lines are already word Swapped + */ +#define gDPLoadMultiBlockS(pkt, timg, tmem, rtile, fmt, siz, width, \ + height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1,0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, \ + (((width) * siz##_LINE_BYTES)+7)>>3, tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + + +#define gDPLoadTextureBlockYuvS(pkt, timg, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1,0); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, \ + (((width) * 1)+7)>>3, 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* + * allows tmem address to be specified + */ +#define _gDPLoadTextureBlock(pkt, timg, tmem, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, \ + 0, cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width) * siz##_LINE_BYTES)+7)>>3, \ + tmem, G_TX_RENDERTILE, pal, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* + * allows tmem address and render tile to be specified + */ +#define _gDPLoadTextureBlockTile(pkt, timg, tmem, rtile, fmt, siz, width, \ + height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0,\ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width) * siz##_LINE_BYTES)+7)>>3, \ + tmem, rtile, pal, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* + * allows tmem address and render tile to be specified + */ +#define gDPLoadMultiBlock(pkt, timg, tmem, rtile, fmt, siz, width, \ + height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz##_LOAD_BLOCK, 1, timg); \ + gDPSetTile(pkt, fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0,\ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width) * siz##_LINE_BYTES)+7)>>3, \ + tmem, rtile, pal, cmt, \ + maskt, shiftt, cms, masks, shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +#define gsDPLoadTextureBlock(timg, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, 0, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width) * siz##_LINE_BYTES)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* Here is the static form of the pre-swapped texture block loading */ +/* See gDPLoadTextureBlockS() for reference. Basically, just don't + calculate DxT, use 0 */ + +#define gsDPLoadTextureBlockS(timg, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, 0, G_TX_LOADTILE, 0 , \ + cmt, maskt,shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, 0 ),\ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width) * siz##_LINE_BYTES)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allow tmem address to be specified + */ +#define _gsDPLoadTextureBlock(timg, tmem, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, \ + ((((width) * siz##_LINE_BYTES)+7)>>3), tmem, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + + +/* + * Allow tmem address and render_tile to be specified + */ +#define _gsDPLoadTextureBlockTile(timg, tmem, rtile, fmt, siz, width, \ + height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, \ + ((((width) * siz##_LINE_BYTES)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + + +/* + * Allow tmem address and render_tile to be specified, useful when loading + * mutilple tiles at a time. + */ +#define gsDPLoadMultiBlock(timg, tmem, rtile, fmt, siz, width, \ + height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, \ + 0 , cmt, maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, \ + CALC_DXT(width, siz##_BYTES)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, \ + ((((width) * siz##_LINE_BYTES)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * Allows tmem and render tile to be specified. Useful when loading + * several tiles at a time. + * + * Here is the static form of the pre-swapped texture block loading + * See gDPLoadTextureBlockS() for reference. Basically, just don't + * calculate DxT, use 0 + */ + +#define gsDPLoadMultiBlockS(timg, tmem, rtile, fmt, siz, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, siz##_LOAD_BLOCK, 1, timg), \ + gsDPSetTile(fmt, siz##_LOAD_BLOCK, 0, tmem, G_TX_LOADTILE, 0 , \ + cmt, maskt,shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, \ + (((width)*(height) + siz##_INCR) >> siz##_SHIFT)-1, 0 ),\ + gsDPPipeSync(), \ + gsDPSetTile(fmt, siz, ((((width) * siz##_LINE_BYTES)+7)>>3), tmem,\ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + + +#define gDPLoadTextureBlock_4b(pkt, timg, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height)+3)>>2)-1, \ + CALC_DXT_4b(width)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* Load fix rww 27jun95 */ +/* The S at the end means odd lines are already word Swapped */ + +#define gDPLoadTextureBlock_4bS(pkt, timg, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height)+3)>>2)-1, 0 ); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* + * 4-bit load block. Useful when loading multiple tiles + */ +#define gDPLoadMultiBlock_4b(pkt, timg, tmem, rtile, fmt, width, height,\ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height)+3)>>2)-1, \ + CALC_DXT_4b(width)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +/* + * 4-bit load block. Allows tmem and render tile to be specified. Useful when + * loading multiple tiles. The S means odd lines are already word swapped. + */ +#define gDPLoadMultiBlock_4bS(pkt, timg, tmem, rtile, fmt, width, height,\ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height)+3)>>2)-1, 0 ); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + + +#define _gDPLoadTextureBlock_4b(pkt, timg, tmem, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, G_IM_SIZ_16b, 1, timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0, \ + cmt, maskt, shiftt, cms, masks, shifts); \ + gDPLoadSync(pkt); \ + gDPLoadBlock(pkt, G_TX_LOADTILE, 0, 0, \ + (((width)*(height)+3)>>2)-1, \ + CALC_DXT_4b(width)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), tmem, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) \ +} + +#define gsDPLoadTextureBlock_4b(timg, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0 , cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width)*(height)+3)>>2)-1, \ + CALC_DXT_4b(width)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +#define gsDPLoadTextureBlock_4bS(timg, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0 , cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width)*(height)+3)>>2)-1,0),\ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +/* + * 4-bit load block. Allows tmem address and render tile to be specified. + * Useful when loading multiple tiles. + */ +#define gsDPLoadMultiBlock_4b(timg, tmem, rtile, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0 , cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width)*(height)+3)>>2)-1, \ + CALC_DXT_4b(width)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + + +/* + * 4-bit load block. Allows tmem address and render tile to be specified. + * Useful when loading multiple tiles. S means odd lines are already swapped. + */ +#define gsDPLoadMultiBlock_4bS(timg, tmem, rtile, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0 , cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width)*(height)+3)>>2)-1,0),\ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + + +/* + * Allows tmem address to be specified + */ +#define _gsDPLoadTextureBlock_4b(timg, tmem, fmt, width, height, \ + pal, cms, cmt, masks, maskt, shifts, shiftt) \ + \ + gsDPSetTextureImage(fmt, G_IM_SIZ_16b, 1, timg), \ + gsDPSetTile(fmt, G_IM_SIZ_16b, 0, tmem, G_TX_LOADTILE, 0 , cmt, \ + maskt, shiftt, cms, masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, (((width)*(height)+3)>>2)-1, \ + CALC_DXT_4b(width)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, ((((width)>>1)+7)>>3), tmem, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, \ + ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC) + +#ifndef _HW_VERSION_1 + +#define gDPLoadTextureTile(pkt, timg, fmt, siz, width, height, \ + uls, ult, lrs, lrt, pal, \ + cms, cmt, masks, maskt, shifts, shiftt) \ +{ \ + gDPSetTextureImage(pkt, fmt, siz, width, timg); \ + gDPSetTile(pkt, fmt, siz, \ + (((((lrs)-(uls)+1) * siz##_TILE_BYTES)+7)>>3), 0, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile( pkt, G_TX_LOADTILE, \ + (uls)<>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, \ + (uls)<>3), tmem, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile( pkt, G_TX_LOADTILE, \ + (uls)<>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, rtile, \ + (uls)<>3), 0, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile( G_TX_LOADTILE, \ + (uls)<>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks,\ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, \ + (uls)<>3), \ + tmem, G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile( G_TX_LOADTILE, \ + (uls)<>3), \ + tmem, rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, \ + (uls)<>1), timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_8b, \ + (((((lrs)-(uls)+1)>>1)+7)>>3), 0, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile( pkt, G_TX_LOADTILE, \ + (uls)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (ult)<<(G_TEXTURE_IMAGE_FRAC), \ + (lrs)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (lrt)<<(G_TEXTURE_IMAGE_FRAC)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, \ + (((((lrs)-(uls)+1)>>1)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, \ + (uls)<>1), timg); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_8b, \ + (((((lrs)-(uls)+1)>>1)+7)>>3), tmem, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPLoadSync(pkt); \ + gDPLoadTile( pkt, G_TX_LOADTILE, \ + (uls)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (ult)<<(G_TEXTURE_IMAGE_FRAC), \ + (lrs)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (lrt)<<(G_TEXTURE_IMAGE_FRAC)); \ + gDPPipeSync(pkt); \ + gDPSetTile(pkt, fmt, G_IM_SIZ_4b, \ + (((((lrs)-(uls)+1)>>1)+7)>>3), tmem, \ + rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts); \ + gDPSetTileSize(pkt, rtile, \ + (uls)<>1), timg), \ + gsDPSetTile(fmt, G_IM_SIZ_8b, (((((lrs)-(uls)+1)>>1)+7)>>3), 0, \ + G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile( G_TX_LOADTILE, \ + (uls)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (ult)<<(G_TEXTURE_IMAGE_FRAC), \ + (lrs)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (lrt)<<(G_TEXTURE_IMAGE_FRAC)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, (((((lrs)-(uls)+1)>>1)+7)>>3), 0, \ + G_TX_RENDERTILE, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(G_TX_RENDERTILE, \ + (uls)<>1), timg), \ + gsDPSetTile(fmt, G_IM_SIZ_8b, (((((lrs)-(uls)+1)>>1)+7)>>3), \ + tmem, G_TX_LOADTILE, 0 , cmt, maskt, shiftt, cms, \ + masks, shifts), \ + gsDPLoadSync(), \ + gsDPLoadTile( G_TX_LOADTILE, \ + (uls)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (ult)<<(G_TEXTURE_IMAGE_FRAC), \ + (lrs)<<(G_TEXTURE_IMAGE_FRAC-1), \ + (lrt)<<(G_TEXTURE_IMAGE_FRAC)), \ + gsDPPipeSync(), \ + gsDPSetTile(fmt, G_IM_SIZ_4b, (((((lrs)-(uls)+1)>>1)+7)>>3), \ + tmem, rtile, pal, cmt, maskt, shiftt, cms, masks, \ + shifts), \ + gsDPSetTileSize(rtile, \ + (uls)<words.w0 = _SHIFTL(G_SETSCISSOR, 24, 8) | \ + _SHIFTL((int)((float)(ulx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(uly)*4.0F), 0, 12); \ + _g->words.w1 = _SHIFTL(mode, 24, 2) | \ + _SHIFTL((int)((float)(lrx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(lry)*4.0F), 0, 12); \ +} + + +#define gDPSetScissorFrac(pkt, mode, ulx, uly, lrx, lry) \ +{ \ + Gfx *_g = (Gfx *)pkt; \ + \ + _g->words.w0 = _SHIFTL(G_SETSCISSOR, 24, 8) | \ + _SHIFTL((int)((ulx)), 12, 12) | \ + _SHIFTL((int)((uly)), 0, 12); \ + _g->words.w1 = _SHIFTL(mode, 24, 2) | \ + _SHIFTL((int)((lrx)), 12, 12) | \ + _SHIFTL((int)((lry)), 0, 12); \ +} + +#define gsDPSetScissor(mode, ulx, uly, lrx, lry) \ +{{ \ + _SHIFTL(G_SETSCISSOR, 24, 8) | \ + _SHIFTL((int)((float)(ulx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(uly)*4.0F), 0, 12), \ + _SHIFTL(mode, 24, 2) | \ + _SHIFTL((int)((float)(lrx)*4.0F), 12, 12) | \ + _SHIFTL((int)((float)(lry)*4.0F), 0, 12) \ +}} + +#define gsDPSetScissorFrac(mode, ulx, uly, lrx, lry) \ +{{ \ + _SHIFTL(G_SETSCISSOR, 24, 8) | \ + _SHIFTL((int)((ulx)), 12, 12) | \ + _SHIFTL((int)((uly)), 0, 12), \ + _SHIFTL(mode, 24, 2) | \ + _SHIFTL((int)(lrx), 12, 12) | \ + _SHIFTL((int)(lry), 0, 12) \ +}} + +/* Fraction never used in fill */ +#ifdef F3DEX_GBI_2E +#define gDPFillRectangle(pkt, ulx, uly, lrx, lry) \ +{ \ + Gfx *_g0 = (Gfx *)(pkt), *_g1 = (Gfx *)(pkt); \ + _g0->words.w0 = _SHIFTL(G_FILLRECT, 24, 8) | \ + _SHIFTL((lrx), 2, 22); \ + _g0->words.w1 = _SHIFTL((lry), 2, 22); \ + _g1->words.w0 = _SHIFTL(G_RDPHALF_1, 24, 8) | \ + _SHIFTL((ulx), 2, 22); \ + _g1->words.w1 = _SHIFTL((uly), 2, 22); \ +} +#define gsDPFillRectangle(ulx, uly, lrx, lry) \ +{{ \ + (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL((lrx), 2, 22)), \ + _SHIFTL((lry), 2, 22), \ +}}, \ +{{ \ + (_SHIFTL(G_RDPHALF_1, 24, 8) | _SHIFTL((ulx), 2, 22)), \ + _SHIFTL((uly), 2, 22), \ +}} +#else +/* +#define gDPFillRectangle(pkt, ulx, uly, lrx, lry) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_FILLRECT, 24, 8) | \ + _SHIFTL((lrx), 14, 10) | _SHIFTL((lry), 2, 10));\ + _g->words.w1 = (_SHIFTL((ulx), 14, 10) | _SHIFTL((uly), 2, 10));\ +} +*/ +#define gsDPFillRectangle(ulx, uly, lrx, lry) \ +{{ \ + (_SHIFTL(G_FILLRECT, 24, 8) | _SHIFTL((lrx), 14, 10) | \ + _SHIFTL((lry), 2, 10)), \ + (_SHIFTL((ulx), 14, 10) | _SHIFTL((uly), 2, 10)) \ +}} +#endif + +/* like gDPFillRectangle but accepts negative arguments */ +#ifndef F3DEX_GBI_2E +#define gDPScisFillRectangle(pkt, ulx, uly, lrx, lry) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_FILLRECT, 24, 8) | \ + _SHIFTL(MAX((lrx),0), 14, 10) | \ + _SHIFTL(MAX((lry),0), 2, 10)); \ + _g->words.w1 = (_SHIFTL(MAX((ulx),0), 14, 10) | \ + _SHIFTL(MAX((uly),0), 2, 10)); \ +} +#endif + +#define gDPSetConvert(pkt, k0, k1, k2, k3, k4, k5) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETCONVERT, 24, 8) | \ + _SHIFTL(k0, 13, 9) | _SHIFTL(k1, 4, 9) | \ + _SHIFTR(k2, 5, 4)); \ + _g->words.w1 = (_SHIFTL(k2, 27, 5) | _SHIFTL(k3, 18, 9) | \ + _SHIFTL(k4, 9, 9) | _SHIFTL(k5, 0, 9)); \ +} + +#define gsDPSetConvert(k0, k1, k2, k3, k4, k5) \ +{{ \ + (_SHIFTL(G_SETCONVERT, 24, 8) | \ + _SHIFTL(k0, 13, 9) | _SHIFTL(k1, 4, 9) | _SHIFTR(k2, 5, 4)), \ + (_SHIFTL(k2, 27, 5) | _SHIFTL(k3, 18, 9) | _SHIFTL(k4, 9, 9) | \ + _SHIFTL(k5, 0, 9)) \ +}} + +#define gDPSetKeyR(pkt, cR, sR, wR) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETKEYR, 24, 8); \ + _g->words.w1 = (_SHIFTL(wR, 16, 12) | _SHIFTL(cR, 8, 8) | \ + _SHIFTL(sR, 0, 8)); \ +} + +#define gsDPSetKeyR(cR, sR, wR) \ +{{ \ + _SHIFTL(G_SETKEYR, 24, 8), \ + _SHIFTL(wR, 16, 12) | _SHIFTL(cR, 8, 8) | _SHIFTL(sR, 0, 8) \ +}} + +#define gDPSetKeyGB(pkt, cG, sG, wG, cB, sB, wB) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETKEYGB, 24, 8) | \ + _SHIFTL(wG, 12, 12) | _SHIFTL(wB, 0, 12)); \ + _g->words.w1 = (_SHIFTL(cG, 24, 8) | _SHIFTL(sG, 16, 8) | \ + _SHIFTL(cB, 8, 8) | _SHIFTL(sB, 0, 8)); \ +} + +#define gsDPSetKeyGB(cG, sG, wG, cB, sB, wB) \ +{{ \ + (_SHIFTL(G_SETKEYGB, 24, 8) | _SHIFTL(wG, 12, 12) | \ + _SHIFTL(wB, 0, 12)), \ + (_SHIFTL(cG, 24, 8) | _SHIFTL(sG, 16, 8) | _SHIFTL(cB, 8, 8) | \ + _SHIFTL(sB, 0, 8)) \ +}} + +#define gDPNoParam(pkt, cmd) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(cmd, 24, 8); \ + _g->words.w1 = 0; \ +} + +#define gsDPNoParam(cmd) \ +{{ \ + _SHIFTL(cmd, 24, 8), 0 \ +}} + +#define gDPParam(pkt, cmd, param) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = _SHIFTL(cmd, 24, 8); \ + _g->words.w1 = (param); \ +} + +#define gsDPParam(cmd, param) \ +{{ \ + _SHIFTL(cmd, 24, 8), (param) \ +}} + +/* Notice that textured rectangles are 128-bit commands, therefore + * gsDPTextureRectangle() should not be used in display lists + * under normal circumstances (use gsSPTextureRectangle()). + * That is also why there is no gDPTextureRectangle() macros. + */ +#define gsDPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{{ \ + (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)), \ +}}, \ +{{ \ + _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), \ + _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \ +}} + +#define gDPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)\ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + if (pkt); \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | \ + _SHIFTL(yl, 0, 12)); \ + _g ++; \ + _g->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ +} + +#define gsDPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{{ \ + (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12)), \ +}}, \ +{{ \ + _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), \ + _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \ +}} + +#define gDPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)\ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + if (pkt); \ + _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | \ + _SHIFTL(yl, 0, 12)); \ + _g ++; \ + _g->words.w0 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ +} + +#ifdef F3D_OLD +# define gSPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)\ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | \ + _SHIFTL(yl, 0, 12)); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))); \ + gImmp1(pkt, G_RDPHALF_CONT, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)));\ +} + +#define gsSPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + {{(_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)),\ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12))}}, \ + gsImmp1(G_RDPHALF_2, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))), \ + gsImmp1(G_RDPHALF_CONT, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))) + +/* like gSPTextureRectangle but accepts negative position arguments */ +# define gSPScisTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | \ + _SHIFTL(MAX((s16)(xh),0), 12, 12) | \ + _SHIFTL(MAX((s16)(yh),0), 0, 12)); \ + _g->words.w1 = (_SHIFTL((tile), 24, 3) | \ + _SHIFTL(MAX((s16)(xl),0), 12, 12) | \ + _SHIFTL(MAX((s16)(yl),0), 0, 12)); \ + gImmp1(pkt, G_RDPHALF_2, \ + (_SHIFTL(((s) - \ + (((s16)(xl) < 0) ? \ + (((s16)(dsdx) < 0) ? \ + (MAX((((s16)(xl)*(s16)(dsdx))>>7),0)) : \ + (MIN((((s16)(xl)*(s16)(dsdx))>>7),0))) : 0)), \ + 16, 16) | \ + _SHIFTL(((t) - \ + (((yl) < 0) ? \ + (((s16)(dtdy) < 0) ? \ + (MAX((((s16)(yl)*(s16)(dtdy))>>7),0)) : \ + (MIN((((s16)(yl)*(s16)(dtdy))>>7),0))) : 0)), \ + 0, 16))); \ + gImmp1(pkt, G_RDPHALF_CONT, (_SHIFTL((dsdx), 16, 16) | \ + _SHIFTL((dtdy), 0, 16))); \ +} + +# define gsSPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + {{(_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12))}}, \ + gsImmp1(G_RDPHALF_2, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))), \ + gsImmp1(G_RDPHALF_CONT, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))) + +# define gSPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) |\ + _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | \ + _SHIFTL(yl, 0, 12)); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))); \ + gImmp1(pkt, G_RDPHALF_CONT, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))); \ +} +#elif defined(F3DEX_GBI_2E) +# define gSPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)\ +{ \ + Gfx *_g0 = (Gfx *)(pkt), *_g1 = (Gfx *)(pkt), *_g2 = (Gfx *)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_TEXRECT, 24, 8) | \ + _SHIFTL((xh), 0, 24); \ + _g0->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL((yh), 0, 24)); \ + _g1->words.w0 = (_SHIFTL(G_RDPHALF_1, 24, 8) | \ + _SHIFTL((xl), 0, 24)); \ + _g1->words.w1 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g2->words.w0 = _SHIFTL(G_RDPHALF_2, 24, 8) | \ + _SHIFTL((yl), 0, 24); \ + _g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ +} + +# define gsSPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{{ \ + (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL((xh), 0, 24)), \ + (_SHIFTL((tile), 24, 3) | _SHIFTL((yh), 0, 24)), \ +}}, \ +{{ \ + (_SHIFTL((G_RDPHALF_1), 24, 8) | _SHIFTL((xl), 0, 24)), \ + _SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16), \ +}}, \ +{{ \ + (_SHIFTL((G_RDPHALF_2), 24, 8) | _SHIFTL((yl), 0, 24)), \ + _SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16) \ +}} + +# define gSPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{ \ + Gfx *_g0 = (Gfx *)(pkt), *_g1 = (Gfx *)(pkt), *_g2 = (Gfx *)(pkt); \ + \ + _g0->words.w0 = _SHIFTL(G_TEXRECTFLIP, 24, 8) | \ + _SHIFTL((xh), 0, 24); \ + _g0->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL((yh), 0, 24)); \ + _g1->words.w0 = (_SHIFTL(G_RDPHALF_1, 24, 8) | \ + _SHIFTL((xl), 0, 24)); \ + _g1->words.w1 = (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16)); \ + _g2->words.w0 = _SHIFTL(G_RDPHALF_2, 24, 8) | \ + _SHIFTL((yl), 0, 24); \ + _g2->words.w1 = (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)); \ +} +#else +# define gSPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy)\ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | \ + _SHIFTL(yl, 0, 12)); \ + gImmp1(pkt, G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16)));\ +} + +#define gsSPTextureRectangle(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + {{(_SHIFTL(G_TEXRECT, 24, 8) | _SHIFTL(xh, 12, 12) | _SHIFTL(yh, 0, 12)),\ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12))}}, \ + gsImmp1(G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))), \ + gsImmp1(G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))) + +/* like gSPTextureRectangle but accepts negative position arguments */ +# define gSPScisTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECT, 24, 8) | \ + _SHIFTL(MAX((s16)(xh),0), 12, 12) | \ + _SHIFTL(MAX((s16)(yh),0), 0, 12)); \ + _g->words.w1 = (_SHIFTL((tile), 24, 3) | \ + _SHIFTL(MAX((s16)(xl),0), 12, 12) | \ + _SHIFTL(MAX((s16)(yl),0), 0, 12)); \ + gImmp1(pkt, G_RDPHALF_1, \ + (_SHIFTL(((s) - \ + (((s16)(xl) < 0) ? \ + (((s16)(dsdx) < 0) ? \ + (MAX((((s16)(xl)*(s16)(dsdx))>>7),0)) : \ + (MIN((((s16)(xl)*(s16)(dsdx))>>7),0))) : 0)), \ + 16, 16) | \ + _SHIFTL(((t) - \ + (((yl) < 0) ? \ + (((s16)(dtdy) < 0) ? \ + (MAX((((s16)(yl)*(s16)(dtdy))>>7),0)) : \ + (MIN((((s16)(yl)*(s16)(dtdy))>>7),0))) : 0)), \ + 0, 16))); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL((dsdx), 16, 16) | \ + _SHIFTL((dtdy), 0, 16))); \ +} + +# define gsSPTextureRectangleFlip(xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ + {{(_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) | \ + _SHIFTL(yh, 0, 12)), \ + (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | _SHIFTL(yl, 0, 12))}}, \ + gsImmp1(G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))), \ + gsImmp1(G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))) + +# define gSPTextureRectangleFlip(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_TEXRECTFLIP, 24, 8) | _SHIFTL(xh, 12, 12) |\ + _SHIFTL(yh, 0, 12)); \ + _g->words.w1 = (_SHIFTL(tile, 24, 3) | _SHIFTL(xl, 12, 12) | \ + _SHIFTL(yl, 0, 12)); \ + gImmp1(pkt, G_RDPHALF_1, (_SHIFTL(s, 16, 16) | _SHIFTL(t, 0, 16))); \ + gImmp1(pkt, G_RDPHALF_2, (_SHIFTL(dsdx, 16, 16) | _SHIFTL(dtdy, 0, 16))); \ +} +#endif + +#define gsDPWord(wordhi, wordlo) \ + gsImmp1(G_RDPHALF_1, (uintptr_t)(wordhi)), \ + gsImmp1(G_RDPHALF_2, (uintptr_t)(wordlo)) + +#define gDPWord(pkt, wordhi, wordlo) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + gImmp1(pkt, G_RDPHALF_1, (uintptr_t)(wordhi)); \ + gImmp1(pkt, G_RDPHALF_2, (uintptr_t)(wordlo)); \ +} + +#define gDPFullSync(pkt) gDPNoParam(pkt, G_RDPFULLSYNC) +#define gsDPFullSync() gsDPNoParam(G_RDPFULLSYNC) +#define gDPTileSync(pkt) gDPNoParam(pkt, G_RDPTILESYNC) +#define gsDPTileSync() gsDPNoParam(G_RDPTILESYNC) +//#define gDPPipeSync(pkt) gDPNoParam(pkt, G_RDPPIPESYNC) +#define gsDPPipeSync() gsDPNoParam(G_RDPPIPESYNC) +#define gDPLoadSync(pkt) gDPNoParam(pkt, G_RDPLOADSYNC) +#define gsDPLoadSync() gsDPNoParam(G_RDPLOADSYNC) +#define gDPNoOp(pkt) gDPNoParam(pkt, G_NOOP) +#define gsDPNoOp() gsDPNoParam(G_NOOP) +#define gDPNoOpTag(pkt, tag) gDPParam(pkt, G_NOOP, tag) +#define gsDPNoOpTag(tag) gsDPParam(G_NOOP, tag) + +#endif /* _LANGUAGE_C */ + + +#endif /* _GBI_H_ */ diff --git a/src/include/PR/os_cont.h b/src/include/PR/os_cont.h new file mode 100644 index 0000000..bd6ff1f --- /dev/null +++ b/src/include/PR/os_cont.h @@ -0,0 +1,207 @@ + +/*==================================================================== + * os_cont.h + * + * Copyright 1995, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +/*---------------------------------------------------------------------* + Copyright (C) 1998 Nintendo. (Originated by SGI) + + $RCSfile: os_cont.h,v $ + $Revision: 1.1 $ + $Date: 1998/10/09 08:01:05 $ + *---------------------------------------------------------------------*/ + +#ifndef _OS_CONT_H_ +#define _OS_CONT_H_ + +#ifdef _LANGUAGE_C_PLUS_PLUS +extern "C" { +#endif + +#include "ultratypes.h" +//#include "os_message.h" + + +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) + +/************************************************************************** + * + * Type definitions + * + */ + +/* + * Structure for controllers + */ + +typedef struct { + u16 type; /* Controller Type */ + u8 status; /* Controller status */ + u8 errnum; +}OSContStatus; + +typedef struct { + u16 button; + s8 stick_x; /* -80 <= stick_x <= 80 */ + s8 stick_y; /* -80 <= stick_y <= 80 */ + u8 errnum; +} OSContPad; + +typedef struct { + void *address; /* Ram pad Address: 11 bits */ + u8 databuffer[32]; /* address of the data buffer */ + u8 addressCrc; /* CRC code for address */ + u8 dataCrc; /* CRC code for data */ + u8 errnum; +} OSContRamIo; + + +#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ + +/************************************************************************** + * + * Global definitions + * + */ + +/* + * Controllers number + */ + +#ifndef _HW_VERSION_1 +#define MAXCONTROLLERS 4 +#else +#define MAXCONTROLLERS 6 +#endif + +/* controller errors */ +#define CONT_NO_RESPONSE_ERROR 0x8 +#define CONT_OVERRUN_ERROR 0x4 +#ifdef _HW_VERSION_1 +#define CONT_FRAME_ERROR 0x2 +#define CONT_COLLISION_ERROR 0x1 +#endif + +/* Controller type */ + +#define CONT_ABSOLUTE 0x0001 +#define CONT_RELATIVE 0x0002 +#define CONT_JOYPORT 0x0004 +#define CONT_EEPROM 0x8000 +#define CONT_EEP16K 0x4000 +#define CONT_TYPE_MASK 0x1f07 +#define CONT_TYPE_NORMAL 0x0005 +#define CONT_TYPE_MOUSE 0x0002 +#define CONT_TYPE_VOICE 0x0100 + +/* Controller status */ + +#define CONT_CARD_ON 0x01 +#define CONT_CARD_PULL 0x02 +#define CONT_ADDR_CRC_ER 0x04 +#define CONT_EEPROM_BUSY 0x80 + +/* Buttons */ + +#define CONT_A 0x8000 +#define CONT_B 0x4000 +#define CONT_G 0x2000 +#define CONT_START 0x1000 +#define CONT_UP 0x0800 +#define CONT_DOWN 0x0400 +#define CONT_LEFT 0x0200 +#define CONT_RIGHT 0x0100 +#define CONT_L 0x0020 +#define CONT_R 0x0010 +#define CONT_E 0x0008 +#define CONT_D 0x0004 +#define CONT_C 0x0002 +#define CONT_F 0x0001 + +/* Nintendo's official button names */ + +#define A_BUTTON CONT_A +#define B_BUTTON CONT_B +#define L_TRIG CONT_L +#define R_TRIG CONT_R +#define Z_TRIG CONT_G +#define START_BUTTON CONT_START +#define U_JPAD CONT_UP +#define L_JPAD CONT_LEFT +#define R_JPAD CONT_RIGHT +#define D_JPAD CONT_DOWN +#define U_CBUTTONS CONT_E +#define L_CBUTTONS CONT_C +#define R_CBUTTONS CONT_F +#define D_CBUTTONS CONT_D + +/* Controller error number */ + +#define CONT_ERR_NO_CONTROLLER PFS_ERR_NOPACK /* 1 */ +#define CONT_ERR_CONTRFAIL CONT_OVERRUN_ERROR /* 4 */ +#define CONT_ERR_INVALID PFS_ERR_INVALID /* 5 */ +#define CONT_ERR_DEVICE PFS_ERR_DEVICE /* 11 */ +#define CONT_ERR_NOT_READY 12 +#define CONT_ERR_VOICE_MEMORY 13 +#define CONT_ERR_VOICE_WORD 14 +#define CONT_ERR_VOICE_NO_RESPONSE 15 + +// +// #if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) +// +// /************************************************************************** +// * +// * Macro definitions +// * +// */ +// +// +// /************************************************************************** +// * +// * Extern variables +// * +// */ +// +// +// /************************************************************************** +// * +// * Function prototypes +// * +// */ +// +// /* Controller interface */ +// +// extern s32 osContInit(OSMesgQueue *, u8 *, OSContStatus *); +// extern s32 osContReset(OSMesgQueue *, OSContStatus *); +// extern s32 osContStartQuery(OSMesgQueue *); +// extern s32 osContStartReadData(OSMesgQueue *); +// #ifndef _HW_VERSION_1 +// extern s32 osContSetCh(u8); +// #endif +// extern void osContGetQuery(OSContStatus *); +// extern void osContGetReadData(OSContPad *); +// +// +// #endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ +// +// #ifdef _LANGUAGE_C_PLUS_PLUS +// } +// #endif + +#endif /* !_OS_CONT_H_ */ \ No newline at end of file diff --git a/src/include/PR/ultratypes.h b/src/include/PR/ultratypes.h new file mode 100644 index 0000000..8a00490 --- /dev/null +++ b/src/include/PR/ultratypes.h @@ -0,0 +1,44 @@ +#ifndef _ULTRA64_TYPES_H_ +#define _ULTRA64_TYPES_H_ + +#ifndef NULL +#define NULL (void *)0 +#endif + +#define TRUE 1 +#define FALSE 0 + +typedef signed char s8; +typedef unsigned char u8; +typedef signed short int s16; +typedef unsigned short int u16; +typedef signed int s32; +typedef unsigned int u32; +typedef signed long long int s64; +typedef unsigned long long int u64; + +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; + +typedef float f32; +typedef double f64; + +#ifdef TARGET_N64 +typedef u32 size_t; +typedef s32 ssize_t; +typedef u32 uintptr_t; +typedef s32 intptr_t; +typedef s32 ptrdiff_t; +#else +#include +#include +typedef ptrdiff_t ssize_t; +#endif + +#endif diff --git a/src/include/audio_defines.h b/src/include/audio_defines.h new file mode 100644 index 0000000..021d2d8 --- /dev/null +++ b/src/include/audio_defines.h @@ -0,0 +1,561 @@ +#ifndef AUDIO_DEFINES_H +#define AUDIO_DEFINES_H + +// Sound Magic Definition: +// First Byte (Upper Nibble): Sound Bank (not the same as audio bank!) +// First Byte (Lower Nibble): Bitflags for audio playback? +// Second Byte: Sound ID +// Third Byte: Priority +// Fourth Byte (Upper Nibble): More bitflags +// Fourth Byte (Lower Nibble): Sound Status (this is set to SOUND_STATUS_PLAYING when passed to the audio driver.) +#define SOUND_ARG_LOAD(bank, playFlags, soundID, priority, flags2) (((u32) (bank) << 28) | \ + ((u32) (playFlags) << 24) | ((u32) (soundID) << 16) | ((u32) (priority) << 8) | \ + ((u32) (flags2) << 4) | SOUND_STATUS_STARTING) + +#define SOUNDARGS_MASK_BANK 0xF0000000 +#define SOUNDARGS_MASK_SOUNDID 0x00FF0000 +#define SOUNDARGS_MASK_PRIORITY 0x0000FF00 +#define SOUNDARGS_MASK_STATUS 0x0000000F + +#define SOUNDARGS_SHIFT_BANK 28 +#define SOUNDARGS_SHIFT_SOUNDID 16 +#define SOUNDARGS_SHIFT_PRIORITY 8 + +/* Audio Status */ +#define SOUND_STATUS_STOPPED 0 +#define SOUND_STATUS_STARTING 1 +#define SOUND_STATUS_PLAYING 2 + +/* Audio lower bitflags. TODO: Figure out what these mean and use them below. */ +#define SOUND_LO_BITFLAG_UNK1 0x10 // fade in? +#define SOUND_NO_ECHO 0x20 // not in JP +#define SOUND_LO_BITFLAG_UNK8 0x80 // restart playing on each play_sound call? + +/* Audio playback bitflags. */ +#define SOUND_NO_VOLUME_LOSS 0x1000000 // No volume loss with distance +#define SOUND_VIBRATO 0x2000000 // Randomly alter frequency each audio frame +#define SOUND_NO_PRIORITY_LOSS 0x4000000 // Do not prioritize closer sounds +#define SOUND_NO_FREQUENCY_LOSS 0x8000000 // Frequency scale does not change with distance + +// silence +#define NO_SOUND 0 + +/** + * The table below defines all sounds that exist in the game, and which flags + * they are used with. If a sound is used with multiple sets of flags (e.g. + * different priorities), they are gives distinguishing suffixes. + * A _2 suffix means the sound is the same despite a different sound ID. + * Some sounds are unused by the game, and given as hexadecimal literals rather + * than SOUND_ARG_LOAD calls. + */ + +/* Terrain sounds */ + +/** + * Terrain-dependent action sounds. mario_get_terrain_sound_addend computes a + * sound terrain type between 0 and 7, depending on the terrain type of the + * level and the floor type that Mario is standing on. That value is then added + * to the sound ID for the TERRAIN_* sounds. + */ +#define SOUND_TERRAIN_DEFAULT 0 // e.g. air +#define SOUND_TERRAIN_GRASS 1 +#define SOUND_TERRAIN_WATER 2 +#define SOUND_TERRAIN_STONE 3 +#define SOUND_TERRAIN_SPOOKY 4 // squeaky floor +#define SOUND_TERRAIN_SNOW 5 +#define SOUND_TERRAIN_ICE 6 +#define SOUND_TERRAIN_SAND 7 + +#define SOUND_ACTION_TERRAIN_JUMP SOUND_ARG_LOAD(0, 4, 0x00, 0x80, 8) +#define SOUND_ACTION_TERRAIN_LANDING SOUND_ARG_LOAD(0, 4, 0x08, 0x80, 8) +#define SOUND_ACTION_TERRAIN_STEP SOUND_ARG_LOAD(0, 6, 0x10, 0x80, 8) +#define SOUND_ACTION_TERRAIN_BODY_HIT_GROUND SOUND_ARG_LOAD(0, 4, 0x18, 0x80, 8) +#define SOUND_ACTION_TERRAIN_STEP_TIPTOE SOUND_ARG_LOAD(0, 6, 0x20, 0x80, 8) +#define SOUND_ACTION_TERRAIN_STUCK_IN_GROUND SOUND_ARG_LOAD(0, 4, 0x48, 0x80, 8) +#define SOUND_ACTION_TERRAIN_HEAVY_LANDING SOUND_ARG_LOAD(0, 4, 0x60, 0x80, 8) + +#define SOUND_ACTION_METAL_JUMP SOUND_ARG_LOAD(0, 4, 0x28, 0x90, 8) +#define SOUND_ACTION_METAL_LANDING SOUND_ARG_LOAD(0, 4, 0x29, 0x90, 8) +#define SOUND_ACTION_METAL_STEP SOUND_ARG_LOAD(0, 4, 0x2A, 0x90, 8) +#define SOUND_ACTION_METAL_HEAVY_LANDING SOUND_ARG_LOAD(0, 4, 0x2B, 0x90, 8) +#define SOUND_ACTION_CLAP_HANDS_COLD SOUND_ARG_LOAD(0, 6, 0x2C, 0x00, 8) +#define SOUND_ACTION_HANGING_STEP SOUND_ARG_LOAD(0, 4, 0x2D, 0xA0, 8) +#define SOUND_ACTION_QUICKSAND_STEP SOUND_ARG_LOAD(0, 4, 0x2E, 0x00, 8) +#define SOUND_ACTION_METAL_STEP_TIPTOE SOUND_ARG_LOAD(0, 4, 0x2F, 0x90, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN430 SOUND_ARG_LOAD(0, 4, 0x30, 0xC0, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN431 SOUND_ARG_LOAD(0, 4, 0x31, 0x60, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN432 SOUND_ARG_LOAD(0, 4, 0x32, 0x80, 8) +#define SOUND_ACTION_SWIM SOUND_ARG_LOAD(0, 4, 0x33, 0x80, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN434 SOUND_ARG_LOAD(0, 4, 0x34, 0x80, 8) +#define SOUND_ACTION_THROW SOUND_ARG_LOAD(0, 4, 0x35, 0x80, 8) +#define SOUND_ACTION_KEY_SWISH SOUND_ARG_LOAD(0, 4, 0x36, 0x80, 8) +#define SOUND_ACTION_SPIN SOUND_ARG_LOAD(0, 4, 0x37, 0x80, 8) +#define SOUND_ACTION_TWIRL SOUND_ARG_LOAD(0, 4, 0x38, 0x80, 8) // same sound as spin +/* not verified */ #define SOUND_ACTION_CLIMB_UP_TREE SOUND_ARG_LOAD(0, 4, 0x3A, 0x80, 8) +/* not verified */ #define SOUND_ACTION_CLIMB_DOWN_TREE 0x003B +/* not verified */ #define SOUND_ACTION_UNK3C 0x003C +/* not verified */ #define SOUND_ACTION_UNKNOWN43D SOUND_ARG_LOAD(0, 4, 0x3D, 0x80, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN43E SOUND_ARG_LOAD(0, 4, 0x3E, 0x80, 8) +/* not verified */ #define SOUND_ACTION_PAT_BACK SOUND_ARG_LOAD(0, 4, 0x3F, 0x80, 8) +#define SOUND_ACTION_BRUSH_HAIR SOUND_ARG_LOAD(0, 4, 0x40, 0x80, 8) +/* not verified */ #define SOUND_ACTION_CLIMB_UP_POLE SOUND_ARG_LOAD(0, 4, 0x41, 0x80, 8) +#define SOUND_ACTION_METAL_BONK SOUND_ARG_LOAD(0, 4, 0x42, 0x80, 8) +#define SOUND_ACTION_UNSTUCK_FROM_GROUND SOUND_ARG_LOAD(0, 4, 0x43, 0x80, 8) +/* not verified */ #define SOUND_ACTION_HIT SOUND_ARG_LOAD(0, 4, 0x44, 0xC0, 8) +/* not verified */ #define SOUND_ACTION_HIT_2 SOUND_ARG_LOAD(0, 4, 0x44, 0xB0, 8) +/* not verified */ #define SOUND_ACTION_HIT_3 SOUND_ARG_LOAD(0, 4, 0x44, 0xA0, 8) +#define SOUND_ACTION_BONK SOUND_ARG_LOAD(0, 4, 0x45, 0xA0, 8) +#define SOUND_ACTION_SHRINK_INTO_BBH SOUND_ARG_LOAD(0, 4, 0x46, 0xA0, 8) +#define SOUND_ACTION_SWIM_FAST SOUND_ARG_LOAD(0, 4, 0x47, 0xA0, 8) +#define SOUND_ACTION_METAL_JUMP_WATER SOUND_ARG_LOAD(0, 4, 0x50, 0x90, 8) +#define SOUND_ACTION_METAL_LAND_WATER SOUND_ARG_LOAD(0, 4, 0x51, 0x90, 8) +#define SOUND_ACTION_METAL_STEP_WATER SOUND_ARG_LOAD(0, 4, 0x52, 0x90, 8) +/* not verified */ #define SOUND_ACTION_UNK53 0x0053 +/* not verified */ #define SOUND_ACTION_UNK54 0x0054 +/* not verified */ #define SOUND_ACTION_UNK55 0x0055 +/* not verified */ #define SOUND_ACTION_FLYING_FAST SOUND_ARG_LOAD(0, 4, 0x56, 0x80, 8) // "swoop"? +#define SOUND_ACTION_TELEPORT SOUND_ARG_LOAD(0, 4, 0x57, 0xC0, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN458 SOUND_ARG_LOAD(0, 4, 0x58, 0xA0, 8) +/* not verified */ #define SOUND_ACTION_BOUNCE_OFF_OBJECT SOUND_ARG_LOAD(0, 4, 0x59, 0xB0, 8) +/* not verified */ #define SOUND_ACTION_SIDE_FLIP_UNK SOUND_ARG_LOAD(0, 4, 0x5A, 0x80, 8) +#define SOUND_ACTION_READ_SIGN SOUND_ARG_LOAD(0, 4, 0x5B, 0xFF, 8) +/* not verified */ #define SOUND_ACTION_UNKNOWN45C SOUND_ARG_LOAD(0, 4, 0x5C, 0x80, 8) +/* not verified */ #define SOUND_ACTION_UNK5D 0x005D +/* not verified */ #define SOUND_ACTION_INTRO_UNK45E SOUND_ARG_LOAD(0, 4, 0x5E, 0x80, 8) +/* not verified */ #define SOUND_ACTION_INTRO_UNK45F SOUND_ARG_LOAD(0, 4, 0x5F, 0x80, 8) + +/* Moving Sound Effects */ + +// Terrain-dependent moving sounds; a value 0-7 is added to the sound ID before +// playing. See higher up for the different terrain types. +#define SOUND_MOVING_TERRAIN_SLIDE SOUND_ARG_LOAD(1, 4, 0x00, 0x00, 0) +#define SOUND_MOVING_TERRAIN_RIDING_SHELL SOUND_ARG_LOAD(1, 4, 0x20, 0x00, 0) + +#define SOUND_MOVING_LAVA_BURN SOUND_ARG_LOAD(1, 4, 0x10, 0x00, 0) // ? +#define SOUND_MOVING_SLIDE_DOWN_POLE SOUND_ARG_LOAD(1, 4, 0x11, 0x00, 0) // ? +#define SOUND_MOVING_SLIDE_DOWN_TREE SOUND_ARG_LOAD(1, 4, 0x12, 0x80, 0) +#define SOUND_MOVING_QUICKSAND_DEATH SOUND_ARG_LOAD(1, 4, 0x14, 0x00, 0) +#define SOUND_MOVING_SHOCKED SOUND_ARG_LOAD(1, 4, 0x16, 0x00, 0) +#define SOUND_MOVING_FLYING SOUND_ARG_LOAD(1, 4, 0x17, 0x00, 0) +#define SOUND_MOVING_ALMOST_DROWNING SOUND_ARG_LOAD(1, 0xC, 0x18, 0x00, 0) +#define SOUND_MOVING_AIM_CANNON SOUND_ARG_LOAD(1, 0xD, 0x19, 0x20, 0) +#define SOUND_MOVING_UNK1A 0x101A // ? +#define SOUND_MOVING_RIDING_SHELL_LAVA SOUND_ARG_LOAD(1, 4, 0x28, 0x00, 0) + +/* Mario Sound Effects */ +// A random number 0-2 is added to the sound ID before playing, producing Yah/Wah/Hoo +#define SOUND_MARIO_YAH_WAH_HOO SOUND_ARG_LOAD(2, 4, 0x00, 0x80, 8) +/* not verified */ #define SOUND_MARIO_HOOHOO SOUND_ARG_LOAD(2, 4, 0x03, 0x80, 8) +/* not verified */ #define SOUND_MARIO_YAHOO SOUND_ARG_LOAD(2, 4, 0x04, 0x80, 8) +/* not verified */ #define SOUND_MARIO_UH SOUND_ARG_LOAD(2, 4, 0x05, 0x80, 8) +/* not verified */ #define SOUND_MARIO_HRMM SOUND_ARG_LOAD(2, 4, 0x06, 0x80, 8) +/* not verified */ #define SOUND_MARIO_WAH2 SOUND_ARG_LOAD(2, 4, 0x07, 0x80, 8) +/* not verified */ #define SOUND_MARIO_WHOA SOUND_ARG_LOAD(2, 4, 0x08, 0xC0, 8) +/* not verified */ #define SOUND_MARIO_EEUH SOUND_ARG_LOAD(2, 4, 0x09, 0x80, 8) +/* not verified */ #define SOUND_MARIO_ATTACKED SOUND_ARG_LOAD(2, 4, 0x0A, 0xFF, 8) +/* not verified */ #define SOUND_MARIO_OOOF SOUND_ARG_LOAD(2, 4, 0x0B, 0x80, 8) +/* not verified */ #define SOUND_MARIO_OOOF2 SOUND_ARG_LOAD(2, 4, 0x0B, 0xD0, 8) +#define SOUND_MARIO_HERE_WE_GO SOUND_ARG_LOAD(2, 4, 0x0C, 0x80, 8) +/* not verified */ #define SOUND_MARIO_YAWNING SOUND_ARG_LOAD(2, 4, 0x0D, 0x80, 8) +#define SOUND_MARIO_SNORING1 SOUND_ARG_LOAD(2, 4, 0x0E, 0x80, 8) +#define SOUND_MARIO_SNORING2 SOUND_ARG_LOAD(2, 4, 0x0F, 0x80, 8) +/* not verified */ #define SOUND_MARIO_WAAAOOOW SOUND_ARG_LOAD(2, 4, 0x10, 0xC0, 8) +/* not verified */ #define SOUND_MARIO_HAHA SOUND_ARG_LOAD(2, 4, 0x11, 0x80, 8) +/* not verified */ #define SOUND_MARIO_HAHA_2 SOUND_ARG_LOAD(2, 4, 0x11, 0xF0, 8) +/* not verified */ #define SOUND_MARIO_UH2 SOUND_ARG_LOAD(2, 4, 0x13, 0xD0, 8) +/* not verified */ #define SOUND_MARIO_UH2_2 SOUND_ARG_LOAD(2, 4, 0x13, 0x80, 8) +/* not verified */ #define SOUND_MARIO_ON_FIRE SOUND_ARG_LOAD(2, 4, 0x14, 0xA0, 8) +/* not verified */ #define SOUND_MARIO_DYING SOUND_ARG_LOAD(2, 4, 0x15, 0xFF, 8) +#define SOUND_MARIO_PANTING_COLD SOUND_ARG_LOAD(2, 4, 0x16, 0x80, 8) + +// A random number 0-2 is added to the sound ID before playing +#define SOUND_MARIO_PANTING SOUND_ARG_LOAD(2, 4, 0x18, 0x80, 8) + +#define SOUND_MARIO_COUGHING1 SOUND_ARG_LOAD(2, 4, 0x1B, 0x80, 8) +#define SOUND_MARIO_COUGHING2 SOUND_ARG_LOAD(2, 4, 0x1C, 0x80, 8) +#define SOUND_MARIO_COUGHING3 SOUND_ARG_LOAD(2, 4, 0x1D, 0x80, 8) +#define SOUND_MARIO_PUNCH_YAH SOUND_ARG_LOAD(2, 4, 0x1E, 0x80, 8) +#define SOUND_MARIO_PUNCH_HOO SOUND_ARG_LOAD(2, 4, 0x1F, 0x80, 8) +#define SOUND_MARIO_MAMA_MIA SOUND_ARG_LOAD(2, 4, 0x20, 0x80, 8) +#define SOUND_MARIO_OKEY_DOKEY 0x2021 +#define SOUND_MARIO_GROUND_POUND_WAH SOUND_ARG_LOAD(2, 4, 0x22, 0x80, 8) +#define SOUND_MARIO_DROWNING SOUND_ARG_LOAD(2, 4, 0x23, 0xF0, 8) +#define SOUND_MARIO_PUNCH_WAH SOUND_ARG_LOAD(2, 4, 0x24, 0x80, 8) + +/* Mario Sound Effects (US/EU only) */ +#define SOUND_PEACH_DEAR_MARIO SOUND_ARG_LOAD(2, 4, 0x28, 0xFF, 8) + +// A random number 0-4 is added to the sound ID before playing, producing one of +// Yahoo! (60% chance), Waha! (20%), or Yippee! (20%). +#define SOUND_MARIO_YAHOO_WAHA_YIPPEE SOUND_ARG_LOAD(2, 4, 0x2B, 0x80, 8) + +#define SOUND_MARIO_DOH SOUND_ARG_LOAD(2, 4, 0x30, 0x80, 8) +#define SOUND_MARIO_GAME_OVER SOUND_ARG_LOAD(2, 4, 0x31, 0xFF, 8) +#define SOUND_MARIO_HELLO SOUND_ARG_LOAD(2, 4, 0x32, 0xFF, 8) +#define SOUND_MARIO_PRESS_START_TO_PLAY SOUND_ARG_LOAD(2, 4, 0x33, 0xFF, 0xA) +#define SOUND_MARIO_TWIRL_BOUNCE SOUND_ARG_LOAD(2, 4, 0x34, 0x80, 8) +#define SOUND_MARIO_SNORING3 SOUND_ARG_LOAD(2, 4, 0x35, 0xFF, 8) +#define SOUND_MARIO_SO_LONGA_BOWSER SOUND_ARG_LOAD(2, 4, 0x36, 0x80, 8) +#define SOUND_MARIO_IMA_TIRED SOUND_ARG_LOAD(2, 4, 0x37, 0x80, 8) + +/* Princess Peach Sound Effects (US/EU only) */ +#define SOUND_PEACH_MARIO SOUND_ARG_LOAD(2, 4, 0x38, 0xFF, 8) +#define SOUND_PEACH_POWER_OF_THE_STARS SOUND_ARG_LOAD(2, 4, 0x39, 0xFF, 8) +#define SOUND_PEACH_THANKS_TO_YOU SOUND_ARG_LOAD(2, 4, 0x3A, 0xFF, 8) +#define SOUND_PEACH_THANK_YOU_MARIO SOUND_ARG_LOAD(2, 4, 0x3B, 0xFF, 8) +#define SOUND_PEACH_SOMETHING_SPECIAL SOUND_ARG_LOAD(2, 4, 0x3C, 0xFF, 8) +#define SOUND_PEACH_BAKE_A_CAKE SOUND_ARG_LOAD(2, 4, 0x3D, 0xFF, 8) +#define SOUND_PEACH_FOR_MARIO SOUND_ARG_LOAD(2, 4, 0x3E, 0xFF, 8) +#define SOUND_PEACH_MARIO2 SOUND_ARG_LOAD(2, 4, 0x3F, 0xFF, 8) + +/* General Sound Effects */ +#define SOUND_GENERAL_ACTIVATE_CAP_SWITCH SOUND_ARG_LOAD(3, 0, 0x00, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_FLAME_OUT SOUND_ARG_LOAD(3, 0, 0x03, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_OPEN_WOOD_DOOR SOUND_ARG_LOAD(3, 0, 0x04, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_CLOSE_WOOD_DOOR SOUND_ARG_LOAD(3, 0, 0x05, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_OPEN_IRON_DOOR SOUND_ARG_LOAD(3, 0, 0x06, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_CLOSE_IRON_DOOR SOUND_ARG_LOAD(3, 0, 0x07, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_BUBBLES 0x3008 +/* not verified */ #define SOUND_GENERAL_MOVING_WATER SOUND_ARG_LOAD(3, 0, 0x09, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_SWISH_WATER SOUND_ARG_LOAD(3, 0, 0x0A, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_QUIET_BUBBLE SOUND_ARG_LOAD(3, 0, 0x0B, 0x00, 8) +#define SOUND_GENERAL_VOLCANO_EXPLOSION SOUND_ARG_LOAD(3, 0, 0x0C, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_QUIET_BUBBLE2 SOUND_ARG_LOAD(3, 0, 0x0D, 0x00, 8) +#define SOUND_GENERAL_CASTLE_TRAP_OPEN SOUND_ARG_LOAD(3, 0, 0x0E, 0x80, 8) +#define SOUND_GENERAL_WALL_EXPLOSION SOUND_ARG_LOAD(3, 0, 0x0F, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_COIN SOUND_ARG_LOAD(3, 8, 0x11, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_COIN_WATER SOUND_ARG_LOAD(3, 8, 0x12, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_SHORT_STAR SOUND_ARG_LOAD(3, 0, 0x16, 0x00, 9) +/* not verified */ #define SOUND_GENERAL_BIG_CLOCK SOUND_ARG_LOAD(3, 0, 0x17, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_LOUD_POUND 0x3018 // _TERRAIN? +/* not verified */ #define SOUND_GENERAL_LOUD_POUND2 0x3019 +/* not verified */ #define SOUND_GENERAL_SHORT_POUND1 0x301A +/* not verified */ #define SOUND_GENERAL_SHORT_POUND2 0x301B +/* not verified */ #define SOUND_GENERAL_SHORT_POUND3 0x301C +/* not verified */ #define SOUND_GENERAL_SHORT_POUND4 0x301D +/* not verified */ #define SOUND_GENERAL_SHORT_POUND5 0x301E +/* not verified */ #define SOUND_GENERAL_SHORT_POUND6 0x301F +#define SOUND_GENERAL_OPEN_CHEST SOUND_ARG_LOAD(3, 1, 0x20, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_CLAM_SHELL1 SOUND_ARG_LOAD(3, 1, 0x22, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_BOX_LANDING SOUND_ARG_LOAD(3, 0, 0x24, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_BOX_LANDING_2 SOUND_ARG_LOAD(3, 2, 0x24, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN1 SOUND_ARG_LOAD(3, 0, 0x25, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN1_2 SOUND_ARG_LOAD(3, 2, 0x25, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_CLAM_SHELL2 SOUND_ARG_LOAD(3, 0, 0x26, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_CLAM_SHELL3 SOUND_ARG_LOAD(3, 0, 0x27, 0x40, 8) +#ifdef VERSION_JP +#define SOUND_GENERAL_PAINTING_EJECT SOUND_ARG_LOAD(3, 8, 0x28, 0x00, 8) +#else +#define SOUND_GENERAL_PAINTING_EJECT SOUND_ARG_LOAD(3, 9, 0x28, 0x00, 8) +#endif +#define SOUND_GENERAL_LEVEL_SELECT_CHANGE SOUND_ARG_LOAD(3, 0, 0x2B, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_PLATFORM SOUND_ARG_LOAD(3, 0, 0x2D, 0x80, 8) +#define SOUND_GENERAL_DONUT_PLATFORM_EXPLOSION SOUND_ARG_LOAD(3, 0, 0x2E, 0x20, 8) +#define SOUND_GENERAL_BOWSER_BOMB_EXPLOSION SOUND_ARG_LOAD(3, 1, 0x2F, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_COIN_SPURT SOUND_ARG_LOAD(3, 0, 0x30, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_COIN_SPURT_2 SOUND_ARG_LOAD(3, 8, 0x30, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_COIN_SPURT_EU SOUND_ARG_LOAD(3, 8, 0x30, 0x20, 8) + +/* not verified */ #define SOUND_GENERAL_EXPLOSION6 0x3031 +/* not verified */ #define SOUND_GENERAL_UNK32 0x3032 +/* not verified */ #define SOUND_GENERAL_BOAT_TILT1 SOUND_ARG_LOAD(3, 0, 0x34, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_BOAT_TILT2 SOUND_ARG_LOAD(3, 0, 0x35, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_COIN_DROP SOUND_ARG_LOAD(3, 0, 0x36, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN3_LOWPRIO SOUND_ARG_LOAD(3, 0, 0x37, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN3 SOUND_ARG_LOAD(3, 0, 0x37, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN3_2 SOUND_ARG_LOAD(3, 8, 0x37, 0x80, 8) +#define SOUND_GENERAL_PENDULUM_SWING SOUND_ARG_LOAD(3, 0, 0x38, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_CHAIN_CHOMP1 SOUND_ARG_LOAD(3, 0, 0x39, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_CHAIN_CHOMP2 SOUND_ARG_LOAD(3, 0, 0x3A, 0x00, 8) +#define SOUND_GENERAL_DOOR_TURN_KEY SOUND_ARG_LOAD(3, 0, 0x3B, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_MOVING_IN_SAND SOUND_ARG_LOAD(3, 0, 0x3C, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN4_LOWPRIO SOUND_ARG_LOAD(3, 0, 0x3D, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNKNOWN4 SOUND_ARG_LOAD(3, 0, 0x3D, 0x80, 8) +#define SOUND_GENERAL_MOVING_PLATFORM_SWITCH SOUND_ARG_LOAD(3, 0, 0x3E, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_CAGE_OPEN SOUND_ARG_LOAD(3, 0, 0x3F, 0xA0, 8) +/* not verified */ #define SOUND_GENERAL_QUIET_POUND1_LOWPRIO SOUND_ARG_LOAD(3, 0, 0x40, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_QUIET_POUND1 SOUND_ARG_LOAD(3, 0, 0x40, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_BREAK_BOX SOUND_ARG_LOAD(3, 0, 0x41, 0xC0, 8) +#define SOUND_GENERAL_DOOR_INSERT_KEY SOUND_ARG_LOAD(3, 0, 0x42, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_QUIET_POUND2 SOUND_ARG_LOAD(3, 0, 0x43, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_BIG_POUND SOUND_ARG_LOAD(3, 0, 0x44, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNK45 SOUND_ARG_LOAD(3, 0, 0x45, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNK46_LOWPRIO SOUND_ARG_LOAD(3, 0, 0x46, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_UNK46 SOUND_ARG_LOAD(3, 0, 0x46, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_CANNON_UP SOUND_ARG_LOAD(3, 0, 0x47, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_GRINDEL_ROLL SOUND_ARG_LOAD(3, 0, 0x48, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_EXPLOSION7 0x3049 +/* not verified */ #define SOUND_GENERAL_SHAKE_COFFIN 0x304A +/* not verified */ #define SOUND_GENERAL_RACE_GUN_SHOT SOUND_ARG_LOAD(3, 1, 0x4D, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_STAR_DOOR_OPEN SOUND_ARG_LOAD(3, 0, 0x4E, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_STAR_DOOR_CLOSE SOUND_ARG_LOAD(3, 0, 0x4F, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_POUND_ROCK SOUND_ARG_LOAD(3, 0, 0x56, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_STAR_APPEARS SOUND_ARG_LOAD(3, 0, 0x57, 0xFF, 9) +#define SOUND_GENERAL_COLLECT_1UP SOUND_ARG_LOAD(3, 0, 0x58, 0xFF, 8) +/* not verified */ #define SOUND_GENERAL_BUTTON_PRESS_LOWPRIO SOUND_ARG_LOAD(3, 0, 0x5A, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_BUTTON_PRESS SOUND_ARG_LOAD(3, 0, 0x5A, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_BUTTON_PRESS_2_LOWPRIO SOUND_ARG_LOAD(3, 1, 0x5A, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_BUTTON_PRESS_2 SOUND_ARG_LOAD(3, 1, 0x5A, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_ELEVATOR_MOVE SOUND_ARG_LOAD(3, 0, 0x5B, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_ELEVATOR_MOVE_2 SOUND_ARG_LOAD(3, 1, 0x5B, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_SWISH_AIR SOUND_ARG_LOAD(3, 0, 0x5C, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_SWISH_AIR_2 SOUND_ARG_LOAD(3, 1, 0x5C, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_HAUNTED_CHAIR SOUND_ARG_LOAD(3, 0, 0x5D, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_SOFT_LANDING SOUND_ARG_LOAD(3, 0, 0x5E, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_HAUNTED_CHAIR_MOVE SOUND_ARG_LOAD(3, 0, 0x5F, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_BOWSER_PLATFORM SOUND_ARG_LOAD(3, 0, 0x62, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_BOWSER_PLATFORM_2 SOUND_ARG_LOAD(3, 1, 0x62, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_HEART_SPIN SOUND_ARG_LOAD(3, 0, 0x64, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_POUND_WOOD_POST SOUND_ARG_LOAD(3, 0, 0x65, 0xC0, 8) +/* not verified */ #define SOUND_GENERAL_WATER_LEVEL_TRIG SOUND_ARG_LOAD(3, 0, 0x66, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_SWITCH_DOOR_OPEN SOUND_ARG_LOAD(3, 0, 0x67, 0xA0, 8) +/* not verified */ #define SOUND_GENERAL_RED_COIN SOUND_ARG_LOAD(3, 0, 0x68, 0x90, 8) +/* not verified */ #define SOUND_GENERAL_BIRDS_FLY_AWAY SOUND_ARG_LOAD(3, 0, 0x69, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_METAL_POUND SOUND_ARG_LOAD(3, 0, 0x6B, 0x80, 8) +/* not verified */ #define SOUND_GENERAL_BOING1 SOUND_ARG_LOAD(3, 0, 0x6C, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_BOING2_LOWPRIO SOUND_ARG_LOAD(3, 0, 0x6D, 0x20, 8) +/* not verified */ #define SOUND_GENERAL_BOING2 SOUND_ARG_LOAD(3, 0, 0x6D, 0x40, 8) +/* not verified */ #define SOUND_GENERAL_YOSHI_WALK SOUND_ARG_LOAD(3, 0, 0x6E, 0x20, 8) +/* not verified */ #define SOUND_GENERAL_ENEMY_ALERT1 SOUND_ARG_LOAD(3, 0, 0x6F, 0x30, 8) +/* not verified */ #define SOUND_GENERAL_YOSHI_TALK SOUND_ARG_LOAD(3, 0, 0x70, 0x30, 8) +/* not verified */ #define SOUND_GENERAL_SPLATTERING SOUND_ARG_LOAD(3, 0, 0x71, 0x30, 8) +/* not verified */ #define SOUND_GENERAL_BOING3 0x3072 +/* not verified */ #define SOUND_GENERAL_GRAND_STAR SOUND_ARG_LOAD(3, 0, 0x73, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_GRAND_STAR_JUMP SOUND_ARG_LOAD(3, 0, 0x74, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_BOAT_ROCK SOUND_ARG_LOAD(3, 0, 0x75, 0x00, 8) +/* not verified */ #define SOUND_GENERAL_VANISH_SFX SOUND_ARG_LOAD(3, 0, 0x76, 0x20, 8) + +/* Environment Sound Effects */ +/* not verified */ #define SOUND_ENV_WATERFALL1 SOUND_ARG_LOAD(4, 0, 0x00, 0x00, 0) +/* not verified */ #define SOUND_ENV_WATERFALL2 SOUND_ARG_LOAD(4, 0, 0x01, 0x00, 0) +/* not verified */ #define SOUND_ENV_ELEVATOR1 SOUND_ARG_LOAD(4, 0, 0x02, 0x00, 0) +/* not verified */ #define SOUND_ENV_DRONING1 SOUND_ARG_LOAD(4, 1, 0x03, 0x00, 0) +/* not verified */ #define SOUND_ENV_DRONING2 SOUND_ARG_LOAD(4, 0, 0x04, 0x00, 0) +/* not verified */ #define SOUND_ENV_WIND1 SOUND_ARG_LOAD(4, 0, 0x05, 0x00, 0) +/* not verified */ #define SOUND_ENV_MOVING_SAND_SNOW 0x4006 +/* not verified */ #define SOUND_ENV_UNK07 0x4007 +/* not verified */ #define SOUND_ENV_ELEVATOR2 SOUND_ARG_LOAD(4, 0, 0x08, 0x00, 0) +/* not verified */ #define SOUND_ENV_WATER SOUND_ARG_LOAD(4, 0, 0x09, 0x00, 0) +/* not verified */ #define SOUND_ENV_UNKNOWN2 SOUND_ARG_LOAD(4, 0, 0x0A, 0x00, 0) +/* not verified */ #define SOUND_ENV_BOAT_ROCKING1 SOUND_ARG_LOAD(4, 0, 0x0B, 0x00, 0) +/* not verified */ #define SOUND_ENV_ELEVATOR3 SOUND_ARG_LOAD(4, 0, 0x0C, 0x00, 0) +/* not verified */ #define SOUND_ENV_ELEVATOR4 SOUND_ARG_LOAD(4, 0, 0x0D, 0x00, 0) +/* not verified */ #define SOUND_ENV_ELEVATOR4_2 SOUND_ARG_LOAD(4, 1, 0x0D, 0x00, 0) +/* not verified */ #define SOUND_ENV_MOVINGSAND SOUND_ARG_LOAD(4, 0, 0x0E, 0x00, 0) +/* not verified */ #define SOUND_ENV_MERRY_GO_ROUND_CREAKING SOUND_ARG_LOAD(4, 0, 0x0F, 0x40, 0) +/* not verified */ #define SOUND_ENV_WIND2 SOUND_ARG_LOAD(4, 0, 0x10, 0x80, 0) +/* not verified */ #define SOUND_ENV_UNK12 0x4012 +/* not verified */ #define SOUND_ENV_SLIDING SOUND_ARG_LOAD(4, 0, 0x13, 0x00, 0) +/* not verified */ #define SOUND_ENV_STAR SOUND_ARG_LOAD(4, 0, 0x14, 0x00, 1) +/* not verified */ #define SOUND_ENV_UNKNOWN4 SOUND_ARG_LOAD(4, 1, 0x15, 0x00, 0) +/* not verified */ #define SOUND_ENV_WATER_DRAIN SOUND_ARG_LOAD(4, 1, 0x16, 0x00, 0) +/* not verified */ #define SOUND_ENV_METAL_BOX_PUSH SOUND_ARG_LOAD(4, 0, 0x17, 0x80, 0) +/* not verified */ #define SOUND_ENV_SINK_QUICKSAND SOUND_ARG_LOAD(4, 0, 0x18, 0x80, 0) + +/* Object Sound Effects */ +#define SOUND_OBJ_SUSHI_SHARK_WATER_SOUND SOUND_ARG_LOAD(5, 0, 0x00, 0x80, 8) +#define SOUND_OBJ_MRI_SHOOT SOUND_ARG_LOAD(5, 0, 0x01, 0x00, 8) +#define SOUND_OBJ_BABY_PENGUIN_WALK SOUND_ARG_LOAD(5, 0, 0x02, 0x00, 8) +#define SOUND_OBJ_BOWSER_WALK SOUND_ARG_LOAD(5, 0, 0x03, 0x00, 8) +#define SOUND_OBJ_BOWSER_TAIL_PICKUP SOUND_ARG_LOAD(5, 0, 0x05, 0x00, 8) +#define SOUND_OBJ_BOWSER_DEFEATED SOUND_ARG_LOAD(5, 0, 0x06, 0x00, 8) +#define SOUND_OBJ_BOWSER_SPINNING SOUND_ARG_LOAD(5, 0, 0x07, 0x00, 8) +#define SOUND_OBJ_BOWSER_INHALING SOUND_ARG_LOAD(5, 0, 0x08, 0x00, 8) +#define SOUND_OBJ_BIG_PENGUIN_WALK SOUND_ARG_LOAD(5, 0, 0x09, 0x80, 8) +#define SOUND_OBJ_BOO_BOUNCE_TOP SOUND_ARG_LOAD(5, 0, 0x0A, 0x00, 8) +#define SOUND_OBJ_BOO_LAUGH_SHORT SOUND_ARG_LOAD(5, 0, 0x0B, 0x00, 8) +#define SOUND_OBJ_THWOMP SOUND_ARG_LOAD(5, 0, 0x0C, 0xA0, 8) +/* not verified */ #define SOUND_OBJ_CANNON1 SOUND_ARG_LOAD(5, 0, 0x0D, 0xF0, 8) +/* not verified */ #define SOUND_OBJ_CANNON2 SOUND_ARG_LOAD(5, 0, 0x0E, 0xF0, 8) +/* not verified */ #define SOUND_OBJ_CANNON3 SOUND_ARG_LOAD(5, 0, 0x0F, 0xF0, 8) +/* not verified */ #define SOUND_OBJ_JUMP_WALK_WATER 0x5012 +/* not verified */ #define SOUND_OBJ_UNKNOWN2 SOUND_ARG_LOAD(5, 0, 0x13, 0x00, 8) +#define SOUND_OBJ_MRI_DEATH SOUND_ARG_LOAD(5, 0, 0x14, 0x00, 8) +/* not verified */ #define SOUND_OBJ_POUNDING1 SOUND_ARG_LOAD(5, 0, 0x15, 0x50, 8) +/* not verified */ #define SOUND_OBJ_POUNDING1_HIGHPRIO SOUND_ARG_LOAD(5, 0, 0x15, 0x80, 8) +#define SOUND_OBJ_WHOMP_LOWPRIO SOUND_ARG_LOAD(5, 0, 0x16, 0x60, 8) +#define SOUND_OBJ_KING_BOBOMB SOUND_ARG_LOAD(5, 0, 0x16, 0x80, 8) +/* not verified */ #define SOUND_OBJ_BULLY_METAL SOUND_ARG_LOAD(5, 0, 0x17, 0x80, 8) +/* not verified */ #define SOUND_OBJ_BULLY_EXPLODE SOUND_ARG_LOAD(5, 0, 0x18, 0xA0, 8) +/* not verified */ #define SOUND_OBJ_BULLY_EXPLODE_2 SOUND_ARG_LOAD(5, 1, 0x18, 0xA0, 8) +/* not verified */ #define SOUND_OBJ_POUNDING_CANNON SOUND_ARG_LOAD(5, 0, 0x1A, 0x50, 8) +/* not verified */ #define SOUND_OBJ_BULLY_WALK SOUND_ARG_LOAD(5, 0, 0x1B, 0x30, 8) +/* not verified */ #define SOUND_OBJ_UNKNOWN3 SOUND_ARG_LOAD(5, 0, 0x1D, 0x80, 8) +/* not verified */ #define SOUND_OBJ_UNKNOWN4 SOUND_ARG_LOAD(5, 0, 0x1E, 0xA0, 8) +#define SOUND_OBJ_BABY_PENGUIN_DIVE SOUND_ARG_LOAD(5, 0, 0x1F, 0x40, 8) +#define SOUND_OBJ_GOOMBA_WALK SOUND_ARG_LOAD(5, 0, 0x20, 0x00, 8) +#define SOUND_OBJ_UKIKI_CHATTER_LONG SOUND_ARG_LOAD(5, 0, 0x21, 0x00, 8) +#define SOUND_OBJ_MONTY_MOLE_ATTACK SOUND_ARG_LOAD(5, 0, 0x22, 0x00, 8) +#define SOUND_OBJ_EVIL_LAKITU_THROW SOUND_ARG_LOAD(5, 0, 0x22, 0x20, 8) +/* not verified */ #define SOUND_OBJ_UNK23 0x5023 +#define SOUND_OBJ_DYING_ENEMY1 SOUND_ARG_LOAD(5, 0, 0x24, 0x40, 8) +/* not verified */ #define SOUND_OBJ_CANNON4 SOUND_ARG_LOAD(5, 0, 0x25, 0x40, 8) +/* not verified */ #define SOUND_OBJ_DYING_ENEMY2 0x5026 +#define SOUND_OBJ_BOBOMB_WALK SOUND_ARG_LOAD(5, 0, 0x27, 0x00, 8) +/* not verified */ #define SOUND_OBJ_SOMETHING_LANDING SOUND_ARG_LOAD(5, 0, 0x28, 0x80, 8) +/* not verified */ #define SOUND_OBJ_DIVING_IN_WATER SOUND_ARG_LOAD(5, 0, 0x29, 0xA0, 8) +/* not verified */ #define SOUND_OBJ_SNOW_SAND1 SOUND_ARG_LOAD(5, 0, 0x2A, 0x00, 8) +/* not verified */ #define SOUND_OBJ_SNOW_SAND2 SOUND_ARG_LOAD(5, 0, 0x2B, 0x00, 8) +#define SOUND_OBJ_DEFAULT_DEATH SOUND_ARG_LOAD(5, 0, 0x2C, 0x80, 8) +#define SOUND_OBJ_BIG_PENGUIN_YELL SOUND_ARG_LOAD(5, 0, 0x2D, 0x00, 8) +#define SOUND_OBJ_WATER_BOMB_BOUNCING SOUND_ARG_LOAD(5, 0, 0x2E, 0x80, 8) +#define SOUND_OBJ_GOOMBA_ALERT SOUND_ARG_LOAD(5, 0, 0x2F, 0x00, 8) +#define SOUND_OBJ_WIGGLER_JUMP SOUND_ARG_LOAD(5, 0, 0x2F, 0x60, 8) +/* not verified */ #define SOUND_OBJ_STOMPED SOUND_ARG_LOAD(5, 0, 0x30, 0x80, 8) +/* not verified */ #define SOUND_OBJ_UNKNOWN6 SOUND_ARG_LOAD(5, 0, 0x31, 0x00, 8) +/* not verified */ #define SOUND_OBJ_DIVING_INTO_WATER SOUND_ARG_LOAD(5, 0, 0x32, 0x40, 8) +#define SOUND_OBJ_PIRANHA_PLANT_SHRINK SOUND_ARG_LOAD(5, 0, 0x33, 0x40, 8) +#define SOUND_OBJ_KOOPA_THE_QUICK_WALK SOUND_ARG_LOAD(5, 0, 0x34, 0x20, 8) +#define SOUND_OBJ_KOOPA_WALK SOUND_ARG_LOAD(5, 0, 0x35, 0x00, 8) +#define SOUND_OBJ_BULLY_WALKING SOUND_ARG_LOAD(5, 0, 0x36, 0x60, 8) +#define SOUND_OBJ_DORRIE SOUND_ARG_LOAD(5, 0, 0x37, 0x60, 8) +#define SOUND_OBJ_BOWSER_LAUGH SOUND_ARG_LOAD(5, 0, 0x38, 0x80, 8) +#define SOUND_OBJ_UKIKI_CHATTER_SHORT SOUND_ARG_LOAD(5, 0, 0x39, 0x00, 8) +#define SOUND_OBJ_UKIKI_CHATTER_IDLE SOUND_ARG_LOAD(5, 0, 0x3A, 0x00, 8) +#define SOUND_OBJ_UKIKI_STEP_DEFAULT SOUND_ARG_LOAD(5, 0, 0x3B, 0x00, 8) +#define SOUND_OBJ_UKIKI_STEP_LEAVES SOUND_ARG_LOAD(5, 0, 0x3C, 0x00, 8) +#define SOUND_OBJ_KOOPA_TALK SOUND_ARG_LOAD(5, 0, 0x3D, 0xA0, 8) +#define SOUND_OBJ_KOOPA_DAMAGE SOUND_ARG_LOAD(5, 0, 0x3E, 0xA0, 8) +/* not verified */ #define SOUND_OBJ_KLEPTO1 SOUND_ARG_LOAD(5, 0, 0x3F, 0x40, 8) +/* not verified */ #define SOUND_OBJ_KLEPTO2 SOUND_ARG_LOAD(5, 0, 0x40, 0x60, 8) +#define SOUND_OBJ_KING_BOBOMB_TALK SOUND_ARG_LOAD(5, 0, 0x41, 0x00, 8) +#define SOUND_OBJ_KING_BOBOMB_JUMP SOUND_ARG_LOAD(5, 0, 0x46, 0x80, 8) +#define SOUND_OBJ_KING_WHOMP_DEATH SOUND_ARG_LOAD(5, 1, 0x47, 0xC0, 8) +#define SOUND_OBJ_BOO_LAUGH_LONG SOUND_ARG_LOAD(5, 0, 0x48, 0x00, 8) +/* not verified */ #define SOUND_OBJ_EEL SOUND_ARG_LOAD(5, 0, 0x4A, 0x00, 8) +/* not verified */ #define SOUND_OBJ_EEL_2 SOUND_ARG_LOAD(5, 2, 0x4A, 0x00, 8) +#define SOUND_OBJ_EYEROK_SHOW_EYE SOUND_ARG_LOAD(5, 2, 0x4B, 0x00, 8) +#define SOUND_OBJ_MR_BLIZZARD_ALERT SOUND_ARG_LOAD(5, 0, 0x4C, 0x00, 8) +#define SOUND_OBJ_SNUFIT_SHOOT SOUND_ARG_LOAD(5, 0, 0x4D, 0x00, 8) +#define SOUND_OBJ_SKEETER_WALK SOUND_ARG_LOAD(5, 0, 0x4E, 0x00, 8) +/* not verified */ #define SOUND_OBJ_WALKING_WATER SOUND_ARG_LOAD(5, 0, 0x4F, 0x00, 8) +#define SOUND_OBJ_BIRD_CHIRP3 SOUND_ARG_LOAD(5, 0, 0x51, 0x40, 0) +#define SOUND_OBJ_PIRANHA_PLANT_APPEAR SOUND_ARG_LOAD(5, 0, 0x54, 0x20, 8) +#define SOUND_OBJ_FLAME_BLOWN SOUND_ARG_LOAD(5, 0, 0x55, 0x80, 8) +#define SOUND_OBJ_MAD_PIANO_CHOMPING SOUND_ARG_LOAD(5, 2, 0x56, 0x40, 8) +#define SOUND_OBJ_BOBOMB_BUDDY_TALK SOUND_ARG_LOAD(5, 0, 0x58, 0x40, 8) +/* not verified */ #define SOUND_OBJ_SPINY_UNK59 SOUND_ARG_LOAD(5, 0, 0x59, 0x10, 8) +#define SOUND_OBJ_WIGGLER_HIGH_PITCH SOUND_ARG_LOAD(5, 0, 0x5C, 0x40, 8) +#define SOUND_OBJ_HEAVEHO_TOSSED SOUND_ARG_LOAD(5, 0, 0x5D, 0x40, 8) +/* not verified */ #define SOUND_OBJ_WIGGLER_DEATH 0x505E +#define SOUND_OBJ_BOWSER_INTRO_LAUGH SOUND_ARG_LOAD(5, 0, 0x5F, 0x80, 9) +/* not verified */ #define SOUND_OBJ_ENEMY_DEATH_HIGH SOUND_ARG_LOAD(5, 0, 0x60, 0xB0, 8) +/* not verified */ #define SOUND_OBJ_ENEMY_DEATH_LOW SOUND_ARG_LOAD(5, 0, 0x61, 0xB0, 8) +#define SOUND_OBJ_SWOOP_DEATH SOUND_ARG_LOAD(5, 0, 0x62, 0xB0, 8) +#define SOUND_OBJ_KOOPA_FLYGUY_DEATH SOUND_ARG_LOAD(5, 0, 0x63, 0xB0, 8) +#define SOUND_OBJ_POKEY_DEATH SOUND_ARG_LOAD(5, 0, 0x63, 0xC0, 8) +/* not verified */ #define SOUND_OBJ_SNOWMAN_BOUNCE SOUND_ARG_LOAD(5, 0, 0x64, 0xC0, 8) +#define SOUND_OBJ_SNOWMAN_EXPLODE SOUND_ARG_LOAD(5, 0, 0x65, 0xD0, 8) +/* not verified */ #define SOUND_OBJ_POUNDING_LOUD SOUND_ARG_LOAD(5, 0, 0x68, 0x40, 8) +/* not verified */ #define SOUND_OBJ_MIPS_RABBIT SOUND_ARG_LOAD(5, 0, 0x6A, 0x00, 8) +/* not verified */ #define SOUND_OBJ_MIPS_RABBIT_WATER SOUND_ARG_LOAD(5, 0, 0x6C, 0x00, 8) +#define SOUND_OBJ_EYEROK_EXPLODE SOUND_ARG_LOAD(5, 0, 0x6D, 0x00, 8) +#define SOUND_OBJ_CHUCKYA_DEATH SOUND_ARG_LOAD(5, 1, 0x6E, 0x00, 8) +#define SOUND_OBJ_WIGGLER_TALK SOUND_ARG_LOAD(5, 0, 0x6F, 0x00, 8) +#define SOUND_OBJ_WIGGLER_ATTACKED SOUND_ARG_LOAD(5, 0, 0x70, 0x60, 8) +#define SOUND_OBJ_WIGGLER_LOW_PITCH SOUND_ARG_LOAD(5, 0, 0x71, 0x20, 8) +#define SOUND_OBJ_SNUFIT_SKEETER_DEATH SOUND_ARG_LOAD(5, 0, 0x72, 0xC0, 8) +#define SOUND_OBJ_BUBBA_CHOMP SOUND_ARG_LOAD(5, 0, 0x73, 0x40, 8) +#define SOUND_OBJ_ENEMY_DEFEAT_SHRINK SOUND_ARG_LOAD(5, 0, 0x74, 0x40, 8) + +#define SOUND_AIR_BOWSER_SPIT_FIRE SOUND_ARG_LOAD(6, 0, 0x00, 0x00, 0) +#define SOUND_AIR_UNK01 0x6001 // ? +#define SOUND_AIR_LAKITU_FLY SOUND_ARG_LOAD(6, 0, 0x02, 0x80, 0) +#define SOUND_AIR_LAKITU_FLY_HIGHPRIO SOUND_ARG_LOAD(6, 0, 0x02, 0xFF, 0) +#define SOUND_AIR_AMP_BUZZ SOUND_ARG_LOAD(6, 0, 0x03, 0x40, 0) +#define SOUND_AIR_BLOW_FIRE SOUND_ARG_LOAD(6, 0, 0x04, 0x80, 0) +#define SOUND_AIR_BLOW_WIND SOUND_ARG_LOAD(6, 0, 0x04, 0x40, 0) +#define SOUND_AIR_ROUGH_SLIDE SOUND_ARG_LOAD(6, 0, 0x05, 0x00, 0) +#define SOUND_AIR_HEAVEHO_MOVE SOUND_ARG_LOAD(6, 0, 0x06, 0x40, 0) +#define SOUND_AIR_UNK07 0x6007 // ? +#define SOUND_AIR_BOBOMB_LIT_FUSE SOUND_ARG_LOAD(6, 0, 0x08, 0x60, 0) +#define SOUND_AIR_HOWLING_WIND SOUND_ARG_LOAD(6, 0, 0x09, 0x80, 0) +#define SOUND_AIR_CHUCKYA_MOVE SOUND_ARG_LOAD(6, 0, 0x0A, 0x40, 0) +#define SOUND_AIR_PEACH_TWINKLE SOUND_ARG_LOAD(6, 0, 0x0B, 0x40, 0) +#define SOUND_AIR_CASTLE_OUTDOORS_AMBIENT SOUND_ARG_LOAD(6, 0, 0x10, 0x40, 0) + +/* Menu Sound Effects */ +#define SOUND_MENU_CHANGE_SELECT SOUND_ARG_LOAD(7, 0, 0x00, 0xF8, 8) +/* not verified */ #define SOUND_MENU_REVERSE_PAUSE 0x7001 +#define SOUND_MENU_PAUSE SOUND_ARG_LOAD(7, 0, 0x02, 0xF0, 8) +#define SOUND_MENU_PAUSE_HIGHPRIO SOUND_ARG_LOAD(7, 0, 0x02, 0xFF, 8) +#define SOUND_MENU_PAUSE_2 SOUND_ARG_LOAD(7, 0, 0x03, 0xFF, 8) +#define SOUND_MENU_MESSAGE_APPEAR SOUND_ARG_LOAD(7, 0, 0x04, 0x00, 8) +#define SOUND_MENU_MESSAGE_DISAPPEAR SOUND_ARG_LOAD(7, 0, 0x05, 0x00, 8) +#define SOUND_MENU_CAMERA_ZOOM_IN SOUND_ARG_LOAD(7, 0, 0x06, 0x00, 8) +#define SOUND_MENU_CAMERA_ZOOM_OUT SOUND_ARG_LOAD(7, 0, 0x07, 0x00, 8) +#define SOUND_MENU_PINCH_MARIO_FACE SOUND_ARG_LOAD(7, 0, 0x08, 0x00, 8) +#define SOUND_MENU_LET_GO_MARIO_FACE SOUND_ARG_LOAD(7, 0, 0x09, 0x00, 8) +#define SOUND_MENU_HAND_APPEAR SOUND_ARG_LOAD(7, 0, 0x0A, 0x00, 8) +#define SOUND_MENU_HAND_DISAPPEAR SOUND_ARG_LOAD(7, 0, 0x0B, 0x00, 8) +/* not verified */ #define SOUND_MENU_UNK0C SOUND_ARG_LOAD(7, 0, 0x0C, 0x00, 8) +/* not verified */ #define SOUND_MENU_POWER_METER SOUND_ARG_LOAD(7, 0, 0x0D, 0x00, 8) +#define SOUND_MENU_CAMERA_BUZZ SOUND_ARG_LOAD(7, 0, 0x0E, 0x00, 8) +#define SOUND_MENU_CAMERA_TURN SOUND_ARG_LOAD(7, 0, 0x0F, 0x00, 8) +/* not verified */ #define SOUND_MENU_UNK10 0x7010 +#define SOUND_MENU_CLICK_FILE_SELECT SOUND_ARG_LOAD(7, 0, 0x11, 0x00, 8) +/* not verified */ #define SOUND_MENU_MESSAGE_NEXT_PAGE SOUND_ARG_LOAD(7, 0, 0x13, 0x00, 8) +#define SOUND_MENU_COIN_ITS_A_ME_MARIO SOUND_ARG_LOAD(7, 0, 0x14, 0x00, 8) +#define SOUND_MENU_YOSHI_GAIN_LIVES SOUND_ARG_LOAD(7, 0, 0x15, 0x00, 8) +#define SOUND_MENU_ENTER_PIPE SOUND_ARG_LOAD(7, 0, 0x16, 0xA0, 8) +#define SOUND_MENU_EXIT_PIPE SOUND_ARG_LOAD(7, 0, 0x17, 0xA0, 8) +#define SOUND_MENU_BOWSER_LAUGH SOUND_ARG_LOAD(7, 0, 0x18, 0x80, 8) +#define SOUND_MENU_ENTER_HOLE SOUND_ARG_LOAD(7, 1, 0x19, 0x80, 8) +/* not verified */ #define SOUND_MENU_CLICK_CHANGE_VIEW SOUND_ARG_LOAD(7, 0, 0x1A, 0x80, 8) +/* not verified */ #define SOUND_MENU_CAMERA_UNUSED1 0x701B +/* not verified */ #define SOUND_MENU_CAMERA_UNUSED2 0x701C +/* not verified */ #define SOUND_MENU_MARIO_CASTLE_WARP SOUND_ARG_LOAD(7, 0, 0x1D, 0xB0, 8) +#define SOUND_MENU_STAR_SOUND SOUND_ARG_LOAD(7, 0, 0x1E, 0xFF, 8) +#define SOUND_MENU_THANK_YOU_PLAYING_MY_GAME SOUND_ARG_LOAD(7, 0, 0x1F, 0xFF, 8) +/* not verified */ #define SOUND_MENU_READ_A_SIGN 0x7020 +/* not verified */ #define SOUND_MENU_EXIT_A_SIGN 0x7021 +/* not verified */ #define SOUND_MENU_MARIO_CASTLE_WARP2 SOUND_ARG_LOAD(7, 0, 0x22, 0x20, 8) +#define SOUND_MENU_STAR_SOUND_OKEY_DOKEY SOUND_ARG_LOAD(7, 0, 0x23, 0xFF, 8) +#define SOUND_MENU_STAR_SOUND_LETS_A_GO SOUND_ARG_LOAD(7, 0, 0x24, 0xFF, 8) + +// US/EU only; an index between 0-7 or 0-4 is added to the sound ID before +// playing, producing the same sound with different pitch. +#define SOUND_MENU_COLLECT_RED_COIN SOUND_ARG_LOAD(7, 8, 0x28, 0x90, 8) +#define SOUND_MENU_COLLECT_SECRET SOUND_ARG_LOAD(7, 0, 0x30, 0x20, 8) + +// Channel 8 loads sounds from the same place as channel 3, making it possible +// to play two channel 3 sounds at once (since just one sound from each channel +// can play at a given time). +#define SOUND_GENERAL2_BOBOMB_EXPLOSION SOUND_ARG_LOAD(8, 0, 0x2E, 0x20, 8) +#define SOUND_GENERAL2_PURPLE_SWITCH SOUND_ARG_LOAD(8, 0, 0x3E, 0xC0, 8) +#define SOUND_GENERAL2_ROTATING_BLOCK_CLICK SOUND_ARG_LOAD(8, 0, 0x40, 0x00, 8) +#define SOUND_GENERAL2_SPINDEL_ROLL SOUND_ARG_LOAD(8, 0, 0x48, 0x20, 8) +#define SOUND_GENERAL2_PYRAMID_TOP_SPIN SOUND_ARG_LOAD(8, 1, 0x4B, 0xE0, 8) +#define SOUND_GENERAL2_PYRAMID_TOP_EXPLOSION SOUND_ARG_LOAD(8, 1, 0x4C, 0xF0, 8) +#define SOUND_GENERAL2_BIRD_CHIRP2 SOUND_ARG_LOAD(8, 0, 0x50, 0x40, 0) +#define SOUND_GENERAL2_SWITCH_TICK_FAST SOUND_ARG_LOAD(8, 0, 0x54, 0xF0, 1) +#define SOUND_GENERAL2_SWITCH_TICK_SLOW SOUND_ARG_LOAD(8, 0, 0x55, 0xF0, 1) +#define SOUND_GENERAL2_STAR_APPEARS SOUND_ARG_LOAD(8, 0, 0x57, 0xFF, 9) +#define SOUND_GENERAL2_ROTATING_BLOCK_ALERT SOUND_ARG_LOAD(8, 0, 0x59, 0x00, 8) +#define SOUND_GENERAL2_BOWSER_EXPLODE SOUND_ARG_LOAD(8, 0, 0x60, 0x00, 8) +#define SOUND_GENERAL2_BOWSER_KEY SOUND_ARG_LOAD(8, 0, 0x61, 0x00, 8) +#define SOUND_GENERAL2_1UP_APPEAR SOUND_ARG_LOAD(8, 0, 0x63, 0xD0, 8) +#define SOUND_GENERAL2_RIGHT_ANSWER SOUND_ARG_LOAD(8, 0, 0x6A, 0xA0, 8) + +// Channel 9 loads sounds from the same place as channel 5. +#define SOUND_OBJ2_BOWSER_ROAR SOUND_ARG_LOAD(9, 0, 0x04, 0x00, 8) +#define SOUND_OBJ2_PIRANHA_PLANT_BITE SOUND_ARG_LOAD(9, 0, 0x10, 0x50, 8) +#define SOUND_OBJ2_PIRANHA_PLANT_DYING SOUND_ARG_LOAD(9, 0, 0x11, 0x60, 8) +#define SOUND_OBJ2_BOWSER_PUZZLE_PIECE_MOVE SOUND_ARG_LOAD(9, 0, 0x19, 0x20, 8) +#define SOUND_OBJ2_BULLY_ATTACKED SOUND_ARG_LOAD(9, 0, 0x1C, 0x00, 8) +#define SOUND_OBJ2_KING_BOBOMB_DAMAGE SOUND_ARG_LOAD(9, 1, 0x42, 0x40, 8) +#define SOUND_OBJ2_SCUTTLEBUG_WALK SOUND_ARG_LOAD(9, 0, 0x43, 0x40, 8) +#define SOUND_OBJ2_SCUTTLEBUG_ALERT SOUND_ARG_LOAD(9, 0, 0x44, 0x40, 8) +#define SOUND_OBJ2_BABY_PENGUIN_YELL SOUND_ARG_LOAD(9, 0, 0x45, 0x00, 8) +#define SOUND_OBJ2_SWOOP SOUND_ARG_LOAD(9, 0, 0x49, 0x00, 8) +#define SOUND_OBJ2_BIRD_CHIRP1 SOUND_ARG_LOAD(9, 0, 0x52, 0x40, 0) +#define SOUND_OBJ2_LARGE_BULLY_ATTACKED SOUND_ARG_LOAD(9, 0, 0x57, 0x00, 8) +#define SOUND_OBJ2_EYEROK_SOUND_SHORT SOUND_ARG_LOAD(9, 3, 0x5A, 0x00, 8) +#define SOUND_OBJ2_WHOMP_SOUND_SHORT SOUND_ARG_LOAD(9, 3, 0x5A, 0xC0, 8) +#define SOUND_OBJ2_EYEROK_SOUND_LONG SOUND_ARG_LOAD(9, 2, 0x5B, 0x00, 8) +#define SOUND_OBJ2_BOWSER_TELEPORT SOUND_ARG_LOAD(9, 0, 0x66, 0x80, 8) +#define SOUND_OBJ2_MONTY_MOLE_APPEAR SOUND_ARG_LOAD(9, 0, 0x67, 0x80, 8) +#define SOUND_OBJ2_BOSS_DIALOG_GRUNT SOUND_ARG_LOAD(9, 0, 0x69, 0x40, 8) +#define SOUND_OBJ2_MRI_SPINNING SOUND_ARG_LOAD(9, 0, 0x6B, 0x00, 8) + +#endif // AUDIO_DEFINES_H diff --git a/src/include/command_macros_base.h b/src/include/command_macros_base.h new file mode 100644 index 0000000..5c24ad1 --- /dev/null +++ b/src/include/command_macros_base.h @@ -0,0 +1,28 @@ +#ifndef COMMAND_MACROS_BASE_H +#define COMMAND_MACROS_BASE_H + +#include "PR/gbi.h" + +#if IS_BIG_ENDIAN +#if IS_64_BIT +#define CMD_BBBB(a, b, c, d) ((uintptr_t)(_SHIFTL(a, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 8, 8) | _SHIFTL(d, 0, 8)) << 32) +#define CMD_BBH(a, b, c) ((uintptr_t)(_SHIFTL(a, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 0, 16)) << 32) +#define CMD_HH(a, b) ((uintptr_t)(_SHIFTL(a, 16, 16) | _SHIFTL(b, 0, 16)) << 32) +#define CMD_W(a) ((uintptr_t)(a) << 32) +#else +#define CMD_BBBB(a, b, c, d) (_SHIFTL(a, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 8, 8) | _SHIFTL(d, 0, 8)) +#define CMD_BBH(a, b, c) (_SHIFTL(a, 24, 8) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 0, 16)) +#define CMD_HH(a, b) (_SHIFTL(a, 16, 16) | _SHIFTL(b, 0, 16)) +#define CMD_W(a) (a) +#endif +#else +#define CMD_BBBB(a, b, c, d) (_SHIFTL(a, 0, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(c, 16, 8) | _SHIFTL(d, 24, 8)) +#define CMD_BBH(a, b, c) (_SHIFTL(a, 0, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(c, 16, 16)) +#define CMD_HH(a, b) (_SHIFTL(a, 0, 16) | _SHIFTL(b, 16, 16)) +#define CMD_W(a) (a) +#endif +#define CMD_PTR(a) ((uintptr_t)(a)) + +#define CMD_HHHHHH(a, b, c, d, e, f) CMD_HH(a, b), CMD_HH(c, d), CMD_HH(e, f) + +#endif // COMMAND_MACROS_BASE_H diff --git a/src/include/geo_commands.h b/src/include/geo_commands.h new file mode 100644 index 0000000..14850e4 --- /dev/null +++ b/src/include/geo_commands.h @@ -0,0 +1,432 @@ +#ifndef GEO_COMMANDS_H +#define GEO_COMMANDS_H + +#include "command_macros_base.h" + +//#include "game/shadow.h" +//#include "game/object_helpers.h" +//#include "game/behavior_actions.h" +//#include "game/segment2.h" +#include "../game/mario_misc.h" +#include "../game/mario_actions_cutscene.h" + +// sky background params +#define BACKGROUND_OCEAN_SKY 0 +#define BACKGROUND_FLAMING_SKY 1 +#define BACKGROUND_UNDERWATER_CITY 2 +#define BACKGROUND_BELOW_CLOUDS 3 +#define BACKGROUND_SNOW_MOUNTAINS 4 +#define BACKGROUND_DESERT 5 +#define BACKGROUND_HAUNTED 6 +#define BACKGROUND_GREEN_SKY 7 +#define BACKGROUND_ABOVE_CLOUDS 8 +#define BACKGROUND_PURPLE_SKY 9 + +// geo layout macros + +/** + * 0x00: Branch and store return address + * 0x04: scriptTarget, segment address of geo layout + */ +#define GEO_BRANCH_AND_LINK(scriptTarget) \ + CMD_BBH(0x00, 0x00, 0x0000), \ + CMD_PTR(scriptTarget) + +/** + * 0x01: Terminate geo layout + * 0x01-0x03: unused + */ +#define GEO_END() \ + CMD_BBH(0x01, 0x00, 0x0000) + +/** + * 0x02: Branch + * 0x01: if 1, store next geo layout address on stack + * 0x02-0x03: unused + * 0x04: scriptTarget, segment address of geo layout + */ +#define GEO_BRANCH(type, scriptTarget) \ + CMD_BBH(0x02, type, 0x0000), \ + CMD_PTR(scriptTarget) + +/** + * 0x03: Return from branch + * 0x01-0x03: unused + */ +#define GEO_RETURN() \ + CMD_BBH(0x03, 0x00, 0x0000) + +/** + * 0x04: Open node + * 0x01-0x03: unused + */ +#define GEO_OPEN_NODE() \ + CMD_BBH(0x04, 0x00, 0x0000) + +/** + * 0x05: Close node + * 0x01-0x03: unused + */ +#define GEO_CLOSE_NODE() \ + CMD_BBH(0x05, 0x00, 0x0000) + +/** + * 0x06: Register the current node at the given index in the gGeoViews array + * 0x01: unused + * 0x02: s16 index + */ +#define GEO_ASSIGN_AS_VIEW(index) \ + CMD_BBH(0x06, 0x00, index) + +/** + * 0x07: Update current scene graph node flags + * 0x01: u8 operation (0 = reset, 1 = set, 2 = clear) + * 0x02: s16 bits + */ +#define GEO_UPDATE_NODE_FLAGS(operation, flagBits) \ + CMD_BBH(0x07, operation, flagBits) + +/** + * 0x08: Create screen area scene graph node + * 0x01: unused + * 0x02: s16 num entries (+2) to allocate + * 0x04: s16 x + * 0x06: s16 y + * 0x08: s16 width + * 0x0A: s16 height + */ +#define GEO_NODE_SCREEN_AREA(numEntries, x, y, width, height) \ + CMD_BBH(0x08, 0x00, numEntries), \ + CMD_HH(x, y), \ + CMD_HH(width, height) + +/** + * 0x09: Create orthographic projection scene graph node + * 0x02: s16 scale as percentage + */ +#define GEO_NODE_ORTHO(scale) \ + CMD_BBH(0x09, 0x00, scale) + +/** + * 0x0A: Create camera frustum scene graph node + * 0x01: u8 if nonzero, enable function field + * 0x02: s16 field of view + * 0x04: s16 near + * 0x06: s16 far + * 0x08: [GraphNodeFunc function] +*/ +#define GEO_CAMERA_FRUSTUM(fov, near, far) \ + CMD_BBH(0x0A, 0x00, fov), \ + CMD_HH(near, far) +#define GEO_CAMERA_FRUSTUM_WITH_FUNC(fov, near, far, func) \ + CMD_BBH(0x0A, 0x01, fov), \ + CMD_HH(near, far), \ + CMD_PTR(func) + +/** + * 0x0B: Create a root scene graph node + * 0x01-0x03: unused + */ +#define GEO_NODE_START() \ + CMD_BBH(0x0B, 0x00, 0x0000) + +/** + * 0x0C: Create zbuffer-toggling scene graph node + * 0x01: u8 enableZBuffer (1 = on, 0 = off) + * 0x02-0x03: unused + */ +#define GEO_ZBUFFER(enable) \ + CMD_BBH(0x0C, enable, 0x0000) + +/** + * 0x0D: Create render range scene graph node + * 0x01-0x03: unused + * 0x04: s16 minDistance + * 0x06: s16 maxDistance + */ +#define GEO_RENDER_RANGE(minDistance, maxDistance) \ + CMD_BBH(0x0D, 0x00, 0x0000), \ + CMD_HH(minDistance, maxDistance) + +/** + * 0x0E: Create switch-case scene graph node + * 0x01: unused + * 0x02: s16 numCases + * 0x04: GraphNodeFunc caseSelectorFunc + */ +#define GEO_SWITCH_CASE(count, function) \ + CMD_BBH(0x0E, 0x00, count), \ + CMD_PTR(function) + +/** + * 0x0F: Create a camera scene graph node. + * 0x01: unused + * 0x02: s16 camera type + * 0x04: s16 posX + * 0x06: s16 posY + * 0x08: s16 posZ + * 0x0A: s16 focusX + * 0x0C: s16 focusY + * 0x0E: s16 focusZ + * 0x10: GraphNodeFunc function + */ +#define GEO_CAMERA(type, x1, y1, z1, x2, y2, z2, function) \ + CMD_BBH(0x0F, 0x00, type), \ + CMD_HHHHHH(x1, y1, z1, x2, y2, z2), \ + CMD_PTR(function) + +/** + * 0x10: Create translation & rotation scene graph node with optional display list + * Four different versions of 0x10 + * cmd+0x01: u8 params + * 0b1000_0000: if set, enable displayList field and drawingLayer + * 0b0111_0000: fieldLayout (determines how rest of data is formatted + * 0b0000_1111: drawingLayer + * + * fieldLayout = 0: Translate & Rotate + * 0x04: s16 xTranslation + * 0x06: s16 yTranslation + * 0x08: s16 zTranslation + * 0x0A: s16 xRotation + * 0x0C: s16 yRotation + * 0x0E: s16 zRotation + * 0x10: [u32 displayList: if MSbit of params set, display list segmented address] + */ +#define GEO_TRANSLATE_ROTATE(layer, tx, ty, tz, rx, ry, rz) \ + CMD_BBH(0x10, (0x00 | layer), 0x0000), \ + CMD_HHHHHH(tx, ty, tz, rx, ry, rz) +#define GEO_TRANSLATE_ROTATE_WITH_DL(layer, tx, ty, tz, rx, ry, rz, displayList) \ + CMD_BBH(0x10, (0x00 | layer | 0x80), 0x0000), \ + CMD_HHHHHH(tx, ty, tz, rx, ry, rz), \ + CMD_PTR(displayList) + +/** + * fieldLayout = 1: Translate + * 0x02: s16 xTranslation + * 0x04: s16 yTranslation + * 0x06: s16 zTranslation + * 0x08: [u32 displayList: if MSbit of params set, display list segmented address] + */ +#define GEO_TRANSLATE(layer, tx, ty, tz) \ + CMD_BBH(0x10, (0x10 | layer), tx), \ + CMD_HH(ty, tz) +#define GEO_TRANSLATE_WITH_DL(layer, tx, ty, tz, displayList) \ + CMD_BBH(0x10, (0x10 | layer | 0x80), tx), \ + CMD_HH(ty, tz), \ + CMD_PTR(displayList) + +/** + * fieldLayout = 2: Rotate + * 0x02: s16 xRotation + * 0x04: s16 yRotation + * 0x06: s16 zRotation + * 0x08: [u32 displayList: if MSbit of params set, display list segmented address] + */ +#define GEO_ROTATE(layer, rx, ry, rz) \ + CMD_BBH(0x10, (0x20 | layer), rx), \ + CMD_HH(ry, rz) +#define GEO_ROTATE_WITH_DL(layer, rx, ry, rz, displayList) \ + CMD_BBH(0x10, (0x20 | layer | 0x80), rx), \ + CMD_HH(ry, rz), \ + CMD_PTR(displayList) + +/** + * fieldLayout = 3: Rotate Y + * 0x02: s16 yRotation + * 0x04: [u32 displayList: if MSbit of params set, display list segmented address] + */ +#define GEO_ROTATE_Y(layer, ry) \ + CMD_BBH(0x10, (0x30 | layer), ry) +#define GEO_ROTATE_Y_WITH_DL(layer, ry, displayList) \ + CMD_BBH(0x10, (0x30 | layer | 0x80), ry), \ + CMD_PTR(displayList) + +/** + * 0x11: Create translation scene graph node with optional display list + * 0x01: u8 params + * 0b1000_0000: if set, enable displayList field and drawingLayer + * 0b0000_1111: drawingLayer + * 0x02: s16 translationX + * 0x04: s16 translationY + * 0x06: s16 translationZ + * 0x08: [u32 displayList: if MSbit of params set, display list segmented address] + */ +#define GEO_TRANSLATE_NODE(layer, ux, uy, uz) \ + CMD_BBH(0x11, layer, ux), \ + CMD_HH(uy, uz) +#define GEO_TRANSLATE_NODE_WITH_DL(layer, ux, uy, uz, displayList) \ + CMD_BBH(0x11, (layer | 0x80), ux), \ + CMD_HH(uy, uz), \ + CMD_PTR(displayList) + +/** + * 0x12: Create rotation scene graph node with optional display list + * 0x01: u8 params + * 0b1000_0000: if set, enable displayList field and drawingLayer + * 0b0000_1111: drawingLayer + * 0x02: s16 rotationX + * 0x04: s16 rotationY + * 0x06: s16 rotationZ + * 0x08: [u32 displayList: if MSbit of params set, display list segmented address] + */ +#define GEO_ROTATION_NODE(layer, ux, uy, uz) \ + CMD_BBH(0x12, layer, ux), \ + CMD_HH(uy, uz) +#define GEO_ROTATION_NODE_WITH_DL(layer, ux, uy, uz, displayList) \ + CMD_BBH(0x12, (layer | 0x80), ux), \ + CMD_HH(uy, uz), \ + CMD_PTR(displayList) + +/** + * 0x13: Create a scene graph node that is rotated by the object's animation. + * 0x01: u8 drawingLayer + * 0x02: s16 xTranslation + * 0x04: s16 yTranslation + * 0x06: s16 zTranslation + * 0x08: u32 displayList: dislay list segmented address + */ +#define GEO_ANIMATED_PART(layer, x, y, z, displayList) \ + CMD_BBH(0x13, layer, x), \ + CMD_HH(y, z), \ + CMD_PTR(displayList) + +/** + * 0x14: Create billboarding node with optional display list + * 0x01: u8 params + * 0b1000_0000: if set, enable displayList field and drawingLayer + * 0b0000_1111: drawingLayer + * 0x02: s16 xTranslation + * 0x04: s16 yTranslation + * 0x06: s16 zTranslation + * 0x08: [u32 displayList: if MSbit of params is set, display list segmented address] + */ +#define GEO_BILLBOARD_WITH_PARAMS(layer, tx, ty, tz) \ + CMD_BBH(0x14, layer, tx), \ + CMD_HH(ty, tz) +#define GEO_BILLBOARD_WITH_PARAMS_AND_DL(layer, tx, ty, tz, displayList) \ + CMD_BBH(0x14, (layer | 0x80), tx), \ + CMD_HH(ty, tz), \ + CMD_PTR(displayList) +#define GEO_BILLBOARD() \ + GEO_BILLBOARD_WITH_PARAMS(0, 0, 0, 0) + +/** + * 0x15: Create plain display list scene graph node + * 0x01: u8 drawingLayer + * 0x02-0x03: unused + * 0x04: u32 displayList: display list segmented address + */ +#define GEO_DISPLAY_LIST(layer, displayList) \ + CMD_BBH(0x15, layer, 0x0000), \ + CMD_PTR(displayList) + +/** + * 0x16: Create shadow scene graph node + * 0x01: unused + * 0x02: s16 shadowType (cast to u8) + * 0x04: s16 shadowSolidity (cast to u8) + * 0x06: s16 shadowScale + */ +#define GEO_SHADOW(type, solidity, scale) \ + CMD_BBH(0x16, 0x00, type), \ + CMD_HH(solidity, scale) + +/** + * 0x17: Create render object scene graph node + * 0x01-0x03: unused + */ +#define GEO_RENDER_OBJ() \ + CMD_BBH(0x17, 0x00, 0x0000) + +/** + * 0x18: Create dynamically generated displaylist scene graph node + * 0x01: unused + * 0x02: s16 parameter + * 0x04: GraphNodeFunc function + */ +#define GEO_ASM(param, function) \ + CMD_BBH(0x18, 0x00, param), \ + CMD_PTR(function) + +/** + * 0x19: Create background scene graph node + * 0x02: s16 background: background ID, or RGBA5551 color if backgroundFunc is null + * 0x04: GraphNodeFunc backgroundFunc + */ +#define GEO_BACKGROUND(background, function) \ + CMD_BBH(0x19, 0x00, background), \ + CMD_PTR(function) +#define GEO_BACKGROUND_COLOR(background) \ + GEO_BACKGROUND(background, NULL) + +/** + * 0x1A: No operation + */ +#define GEO_NOP_1A() \ + CMD_BBH(0x1A, 0x00, 0x0000), \ + CMD_HH(0x0000, 0x0000) + +/** + * 0x1B: Copy the shared children from an object parent node from a specific view + * to a newly created object parent. + * 0x02: s16 index of array + */ +#define GEO_COPY_VIEW(index) \ + CMD_BBH(0x1B, 0x00, index) + +/** + * 0x1C: Create a held object scene graph node + * cmd+0x01: u8 unused + * cmd+0x02: s16 offsetX + * cmd+0x04: s16 offsetY + * cmd+0x06: s16 offsetZ + * cmd+0x08: GraphNodeFunc nodeFunc + */ +#define GEO_HELD_OBJECT(param, ux, uy, uz, nodeFunc) \ + CMD_BBH(0x1C, param, ux), \ + CMD_HH(uy, uz), \ + CMD_PTR(nodeFunc) + +/** + * 0x1D: Create scale scene graph node with optional display list + * 0x01: u8 params + * 0b1000_0000: if set, enable displayList field and drawingLayer + * 0b0000_1111: drawingLayer + * 0x02-0x03: unused + * 0x04: u32 scale (0x10000 = 1.0) + * 0x08: [u32 displayList: if MSbit of params is set, display list segment address] + */ +#define GEO_SCALE(layer, scale) \ + CMD_BBH(0x1D, layer, 0x0000), \ + CMD_W(scale) +#define GEO_SCALE_WITH_DL(layer, scale, displayList) \ + CMD_BBH(0x1D, (layer | 0x80), 0x0000), \ + CMD_W(scale), \ + CMD_PTR(displayList) + +/** + * 0x1E: No operation + */ +#define GEO_NOP_1E() \ + CMD_BBH(0x1E, 0x00, 0x0000), \ + CMD_HH(0x0000, 0x0000) + +/** + * 0x1F: No operation + */ +#define GEO_NOP_1F() \ + CMD_BBH(0x1F, 0x00, 0x0000), \ + CMD_HH(0x0000, 0x0000), \ + CMD_HH(0x0000, 0x0000), \ + CMD_HH(0x0000, 0x0000) + +/** + * 0x20: Create a scene graph node that specifies for an object the radius that + * is used for frustum culling. + * 0x01: unused + * 0x02: s16 cullingRadius + */ +#define GEO_CULLING_RADIUS(cullingRadius) \ + CMD_BBH(0x20, 0x00, cullingRadius) + +#endif // GEO_COMMANDS_H diff --git a/src/include/level_misc_macros.h b/src/include/level_misc_macros.h new file mode 100644 index 0000000..91ccd78 --- /dev/null +++ b/src/include/level_misc_macros.h @@ -0,0 +1,28 @@ +#ifndef LEVEL_MISC_MACROS_H +#define LEVEL_MISC_MACROS_H + +#define MACRO_OBJECT_WITH_BEH_PARAM(preset, yaw, posX, posY, posZ, behParam) \ + ((s16)((yaw * 0x10 / 45) << 9) | (preset + 0x1F)), posX, posY, posZ, behParam + +#define MACRO_OBJECT(preset, yaw, posX, posY, posZ) \ + MACRO_OBJECT_WITH_BEH_PARAM(preset, yaw, posX, posY, posZ, 0) + +#define MACRO_OBJECT_END() \ + 0x001E + +#define SPECIAL_OBJECT(preset, posX, posY, posZ) \ + preset, posX, posY, posZ + +#define SPECIAL_OBJECT_WITH_YAW(preset, posX, posY, posZ, yaw) \ + preset, posX, posY, posZ, yaw + +#define SPECIAL_OBJECT_WITH_YAW_AND_PARAM(preset, posX, posY, posZ, yaw, param) \ + preset, posX, posY, posZ, yaw, param + +#define TRAJECTORY_POS(trajId, x, y, z) \ + trajId, x, y, z + +#define TRAJECTORY_END() \ + -1 + +#endif // LEVEL_MISC_MACROS_H diff --git a/src/include/macros.h b/src/include/macros.h new file mode 100644 index 0000000..6f2ffc4 --- /dev/null +++ b/src/include/macros.h @@ -0,0 +1,77 @@ +#ifndef MACROS_H +#define MACROS_H + +#include "platform_info.h" + +#ifndef __sgi +#define GLOBAL_ASM(...) +#endif + +// ===== PATCH ===== +#define NON_MATCHING 1 +#define AVOID_UB 1 +// ================= + +#if !defined(__sgi) && (!defined(NON_MATCHING) || !defined(AVOID_UB)) +// asm-process isn't supported outside of IDO, and undefined behavior causes +// crashes. +#error Matching build is only possible on IDO; please build with NON_MATCHING=1. +#endif + +#define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0])) + +#define GLUE(a, b) a ## b +#define GLUE2(a, b) GLUE(a, b) + +// Avoid compiler warnings for unused variables +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif + +// Avoid undefined behaviour for non-returning functions +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +// Static assertions +#ifdef __GNUC__ +#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) +#else +#define STATIC_ASSERT(cond, msg) typedef char GLUE2(static_assertion_failed, __LINE__)[(cond) ? 1 : -1] +#endif + +// Align to 8-byte boundary for DMA requirements +#ifdef __GNUC__ +#define ALIGNED8 __attribute__((aligned(8))) +#else +#define ALIGNED8 +#endif + +// Align to 16-byte boundary for audio lib requirements +#ifdef __GNUC__ +#define ALIGNED16 __attribute__((aligned(16))) +#else +#define ALIGNED16 +#endif + +#ifndef NO_SEGMENTED_MEMORY +// convert a virtual address to physical. +#define VIRTUAL_TO_PHYSICAL(addr) ((uintptr_t)(addr) & 0x1FFFFFFF) + +// convert a physical address to virtual. +#define PHYSICAL_TO_VIRTUAL(addr) ((uintptr_t)(addr) | 0x80000000) + +// another way of converting virtual to physical +#define VIRTUAL_TO_PHYSICAL2(addr) ((u8 *)(addr) - 0x80000000U) +#else +// no conversion needed other than cast +#define VIRTUAL_TO_PHYSICAL(addr) ((addr)) +#define PHYSICAL_TO_VIRTUAL(addr) ((addr)) +#define VIRTUAL_TO_PHYSICAL2(addr) ((void *)(addr)) +#endif + +#endif // MACROS_H diff --git a/src/include/mario_animation_ids.h b/src/include/mario_animation_ids.h new file mode 100644 index 0000000..1d22d3d --- /dev/null +++ b/src/include/mario_animation_ids.h @@ -0,0 +1,219 @@ +#ifndef MARIO_ANIMATION_IDS_H +#define MARIO_ANIMATION_IDS_H + +/* Mario Animation IDs */ + +enum MarioAnimID +{ + /* 0x00 */ MARIO_ANIM_SLOW_LEDGE_GRAB, + /* 0x01 */ MARIO_ANIM_FALL_OVER_BACKWARDS, + /* 0x02 */ MARIO_ANIM_BACKWARD_AIR_KB, + /* 0x03 */ MARIO_ANIM_DYING_ON_BACK, + /* 0x04 */ MARIO_ANIM_BACKFLIP, + /* 0x05 */ MARIO_ANIM_CLIMB_UP_POLE, + /* 0x06 */ MARIO_ANIM_GRAB_POLE_SHORT, + /* 0x07 */ MARIO_ANIM_GRAB_POLE_SWING_PART1, + /* 0x08 */ MARIO_ANIM_GRAB_POLE_SWING_PART2, + /* 0x09 */ MARIO_ANIM_HANDSTAND_IDLE, + /* 0x0A */ MARIO_ANIM_HANDSTAND_JUMP, + /* 0x0B */ MARIO_ANIM_START_HANDSTAND, + /* 0x0C */ MARIO_ANIM_RETURN_FROM_HANDSTAND, + /* 0x0D */ MARIO_ANIM_IDLE_ON_POLE, + /* 0x0E */ MARIO_ANIM_A_POSE, + /* 0x0F */ MARIO_ANIM_SKID_ON_GROUND, + /* 0x10 */ MARIO_ANIM_STOP_SKID, + /* 0x11 */ MARIO_ANIM_CROUCH_FROM_FAST_LONGJUMP, + /* 0x12 */ MARIO_ANIM_CROUCH_FROM_SLOW_LONGJUMP, + /* 0x13 */ MARIO_ANIM_FAST_LONGJUMP, + /* 0x14 */ MARIO_ANIM_SLOW_LONGJUMP, + /* 0x15 */ MARIO_ANIM_AIRBORNE_ON_STOMACH, + /* 0x16 */ MARIO_ANIM_WALK_WITH_LIGHT_OBJ, + /* 0x17 */ MARIO_ANIM_RUN_WITH_LIGHT_OBJ, + /* 0x18 */ MARIO_ANIM_SLOW_WALK_WITH_LIGHT_OBJ, + /* 0x19 */ MARIO_ANIM_SHIVERING_WARMING_HAND, + /* 0x1A */ MARIO_ANIM_SHIVERING_RETURN_TO_IDLE, + /* 0x1B */ MARIO_ANIM_SHIVERING, + /* 0x1C */ MARIO_ANIM_CLIMB_DOWN_LEDGE, + /* 0x1D */ MARIO_ANIM_CREDITS_WAVING, + /* 0x1E */ MARIO_ANIM_CREDITS_LOOK_UP, + /* 0x1F */ MARIO_ANIM_CREDITS_RETURN_FROM_LOOK_UP, + /* 0x20 */ MARIO_ANIM_CREDITS_RAISE_HAND, + /* 0x21 */ MARIO_ANIM_CREDITS_LOWER_HAND, + /* 0x22 */ MARIO_ANIM_CREDITS_TAKE_OFF_CAP, + /* 0x23 */ MARIO_ANIM_CREDITS_START_WALK_LOOK_UP, + /* 0x24 */ MARIO_ANIM_CREDITS_LOOK_BACK_THEN_RUN, + /* 0x25 */ MARIO_ANIM_FINAL_BOWSER_RAISE_HAND_SPIN, + /* 0x26 */ MARIO_ANIM_FINAL_BOWSER_WING_CAP_TAKE_OFF, + /* 0x27 */ MARIO_ANIM_CREDITS_PEACE_SIGN, + /* 0x28 */ MARIO_ANIM_STAND_UP_FROM_LAVA_BOOST, + /* 0x29 */ MARIO_ANIM_FIRE_LAVA_BURN, + /* 0x2A */ MARIO_ANIM_WING_CAP_FLY, + /* 0x2B */ MARIO_ANIM_HANG_ON_OWL, + /* 0x2C */ MARIO_ANIM_LAND_ON_STOMACH, + /* 0x2D */ MARIO_ANIM_AIR_FORWARD_KB, + /* 0x2E */ MARIO_ANIM_DYING_ON_STOMACH, + /* 0x2F */ MARIO_ANIM_SUFFOCATING, + /* 0x30 */ MARIO_ANIM_COUGHING, + /* 0x31 */ MARIO_ANIM_THROW_CATCH_KEY, + /* 0x32 */ MARIO_ANIM_DYING_FALL_OVER, + /* 0x33 */ MARIO_ANIM_IDLE_ON_LEDGE, + /* 0x34 */ MARIO_ANIM_FAST_LEDGE_GRAB, + /* 0x35 */ MARIO_ANIM_HANG_ON_CEILING, + /* 0x36 */ MARIO_ANIM_PUT_CAP_ON, + /* 0x37 */ MARIO_ANIM_TAKE_CAP_OFF_THEN_ON, + /* 0x38 */ MARIO_ANIM_QUICKLY_PUT_CAP_ON, // unused + /* 0x39 */ MARIO_ANIM_HEAD_STUCK_IN_GROUND, + /* 0x3A */ MARIO_ANIM_GROUND_POUND_LANDING, + /* 0x3B */ MARIO_ANIM_TRIPLE_JUMP_GROUND_POUND, + /* 0x3C */ MARIO_ANIM_START_GROUND_POUND, + /* 0x3D */ MARIO_ANIM_GROUND_POUND, + /* 0x3E */ MARIO_ANIM_BOTTOM_STUCK_IN_GROUND, + /* 0x3F */ MARIO_ANIM_IDLE_WITH_LIGHT_OBJ, + /* 0x40 */ MARIO_ANIM_JUMP_LAND_WITH_LIGHT_OBJ, + /* 0x41 */ MARIO_ANIM_JUMP_WITH_LIGHT_OBJ, + /* 0x42 */ MARIO_ANIM_FALL_LAND_WITH_LIGHT_OBJ, + /* 0x43 */ MARIO_ANIM_FALL_WITH_LIGHT_OBJ, + /* 0x44 */ MARIO_ANIM_FALL_FROM_SLIDING_WITH_LIGHT_OBJ, + /* 0x45 */ MARIO_ANIM_SLIDING_ON_BOTTOM_WITH_LIGHT_OBJ, + /* 0x46 */ MARIO_ANIM_STAND_UP_FROM_SLIDING_WITH_LIGHT_OBJ, + /* 0x47 */ MARIO_ANIM_RIDING_SHELL, + /* 0x48 */ MARIO_ANIM_WALKING, + /* 0x49 */ MARIO_ANIM_FORWARD_FLIP, // unused + /* 0x4A */ MARIO_ANIM_JUMP_RIDING_SHELL, + /* 0x4B */ MARIO_ANIM_LAND_FROM_DOUBLE_JUMP, + /* 0x4C */ MARIO_ANIM_DOUBLE_JUMP_FALL, + /* 0x4D */ MARIO_ANIM_SINGLE_JUMP, + /* 0x4E */ MARIO_ANIM_LAND_FROM_SINGLE_JUMP, + /* 0x4F */ MARIO_ANIM_AIR_KICK, + /* 0x50 */ MARIO_ANIM_DOUBLE_JUMP_RISE, + /* 0x51 */ MARIO_ANIM_START_FORWARD_SPINNING, // unused + /* 0x52 */ MARIO_ANIM_THROW_LIGHT_OBJECT, + /* 0x53 */ MARIO_ANIM_FALL_FROM_SLIDE_KICK, + /* 0x54 */ MARIO_ANIM_BEND_KNESS_RIDING_SHELL, // unused + /* 0x55 */ MARIO_ANIM_LEGS_STUCK_IN_GROUND, + /* 0x56 */ MARIO_ANIM_GENERAL_FALL, + /* 0x57 */ MARIO_ANIM_GENERAL_LAND, + /* 0x58 */ MARIO_ANIM_BEING_GRABBED, + /* 0x59 */ MARIO_ANIM_GRAB_HEAVY_OBJECT, + /* 0x5A */ MARIO_ANIM_SLOW_LAND_FROM_DIVE, + /* 0x5B */ MARIO_ANIM_FLY_FROM_CANNON, + /* 0x5C */ MARIO_ANIM_MOVE_ON_WIRE_NET_RIGHT, + /* 0x5D */ MARIO_ANIM_MOVE_ON_WIRE_NET_LEFT, + /* 0x5E */ MARIO_ANIM_MISSING_CAP, + /* 0x5F */ MARIO_ANIM_PULL_DOOR_WALK_IN, + /* 0x60 */ MARIO_ANIM_PUSH_DOOR_WALK_IN, + /* 0x61 */ MARIO_ANIM_UNLOCK_DOOR, + /* 0x62 */ MARIO_ANIM_START_REACH_POCKET, // unused, reaching keys maybe? + /* 0x63 */ MARIO_ANIM_REACH_POCKET, // unused + /* 0x64 */ MARIO_ANIM_STOP_REACH_POCKET, // unused + /* 0x65 */ MARIO_ANIM_GROUND_THROW, + /* 0x66 */ MARIO_ANIM_GROUND_KICK, + /* 0x67 */ MARIO_ANIM_FIRST_PUNCH, + /* 0x68 */ MARIO_ANIM_SECOND_PUNCH, + /* 0x69 */ MARIO_ANIM_FIRST_PUNCH_FAST, + /* 0x6A */ MARIO_ANIM_SECOND_PUNCH_FAST, + /* 0x6B */ MARIO_ANIM_PICK_UP_LIGHT_OBJ, + /* 0x6C */ MARIO_ANIM_PUSHING, + /* 0x6D */ MARIO_ANIM_START_RIDING_SHELL, + /* 0x6E */ MARIO_ANIM_PLACE_LIGHT_OBJ, + /* 0x6F */ MARIO_ANIM_FORWARD_SPINNING, + /* 0x70 */ MARIO_ANIM_BACKWARD_SPINNING, + /* 0x71 */ MARIO_ANIM_BREAKDANCE, + /* 0x72 */ MARIO_ANIM_RUNNING, + /* 0x73 */ MARIO_ANIM_RUNNING_UNUSED, // unused duplicate, originally part 2? + /* 0x74 */ MARIO_ANIM_SOFT_BACK_KB, + /* 0x75 */ MARIO_ANIM_SOFT_FRONT_KB, + /* 0x76 */ MARIO_ANIM_DYING_IN_QUICKSAND, + /* 0x77 */ MARIO_ANIM_IDLE_IN_QUICKSAND, + /* 0x78 */ MARIO_ANIM_MOVE_IN_QUICKSAND, + /* 0x79 */ MARIO_ANIM_ELECTROCUTION, + /* 0x7A */ MARIO_ANIM_SHOCKED, + /* 0x7B */ MARIO_ANIM_BACKWARD_KB, + /* 0x7C */ MARIO_ANIM_FORWARD_KB, + /* 0x7D */ MARIO_ANIM_IDLE_HEAVY_OBJ, + /* 0x7E */ MARIO_ANIM_STAND_AGAINST_WALL, + /* 0x7F */ MARIO_ANIM_SIDESTEP_LEFT, + /* 0x80 */ MARIO_ANIM_SIDESTEP_RIGHT, + /* 0x81 */ MARIO_ANIM_START_SLEEP_IDLE, + /* 0x82 */ MARIO_ANIM_START_SLEEP_SCRATCH, + /* 0x83 */ MARIO_ANIM_START_SLEEP_YAWN, + /* 0x84 */ MARIO_ANIM_START_SLEEP_SITTING, + /* 0x85 */ MARIO_ANIM_SLEEP_IDLE, + /* 0x86 */ MARIO_ANIM_SLEEP_START_LYING, + /* 0x87 */ MARIO_ANIM_SLEEP_LYING, + /* 0x88 */ MARIO_ANIM_DIVE, + /* 0x89 */ MARIO_ANIM_SLIDE_DIVE, + /* 0x8A */ MARIO_ANIM_GROUND_BONK, + /* 0x8B */ MARIO_ANIM_STOP_SLIDE_LIGHT_OBJ, + /* 0x8C */ MARIO_ANIM_SLIDE_KICK, + /* 0x8D */ MARIO_ANIM_CROUCH_FROM_SLIDE_KICK, + /* 0x8E */ MARIO_ANIM_SLIDE_MOTIONLESS, // unused + /* 0x8F */ MARIO_ANIM_STOP_SLIDE, + /* 0x90 */ MARIO_ANIM_FALL_FROM_SLIDE, + /* 0x91 */ MARIO_ANIM_SLIDE, + /* 0x92 */ MARIO_ANIM_TIPTOE, + /* 0x93 */ MARIO_ANIM_TWIRL_LAND, + /* 0x94 */ MARIO_ANIM_TWIRL, + /* 0x95 */ MARIO_ANIM_START_TWIRL, + /* 0x96 */ MARIO_ANIM_STOP_CROUCHING, + /* 0x97 */ MARIO_ANIM_START_CROUCHING, + /* 0x98 */ MARIO_ANIM_CROUCHING, + /* 0x99 */ MARIO_ANIM_CRAWLING, + /* 0x9A */ MARIO_ANIM_STOP_CRAWLING, + /* 0x9B */ MARIO_ANIM_START_CRAWLING, + /* 0x9C */ MARIO_ANIM_SUMMON_STAR, + /* 0x9D */ MARIO_ANIM_RETURN_STAR_APPROACH_DOOR, + /* 0x9E */ MARIO_ANIM_BACKWARDS_WATER_KB, + /* 0x9F */ MARIO_ANIM_SWIM_WITH_OBJ_PART1, + /* 0xA0 */ MARIO_ANIM_SWIM_WITH_OBJ_PART2, + /* 0xA1 */ MARIO_ANIM_FLUTTERKICK_WITH_OBJ, + /* 0xA2 */ MARIO_ANIM_WATER_ACTION_END_WITH_OBJ, // either swimming or flutterkicking + /* 0xA3 */ MARIO_ANIM_STOP_GRAB_OBJ_WATER, + /* 0xA4 */ MARIO_ANIM_WATER_IDLE_WITH_OBJ, + /* 0xA5 */ MARIO_ANIM_DROWNING_PART1, + /* 0xA6 */ MARIO_ANIM_DROWNING_PART2, + /* 0xA7 */ MARIO_ANIM_WATER_DYING, + /* 0xA8 */ MARIO_ANIM_WATER_FORWARD_KB, + /* 0xA9 */ MARIO_ANIM_FALL_FROM_WATER, + /* 0xAA */ MARIO_ANIM_SWIM_PART1, + /* 0xAB */ MARIO_ANIM_SWIM_PART2, + /* 0xAC */ MARIO_ANIM_FLUTTERKICK, + /* 0xAD */ MARIO_ANIM_WATER_ACTION_END, // either swimming or flutterkicking + /* 0xAE */ MARIO_ANIM_WATER_PICK_UP_OBJ, + /* 0xAF */ MARIO_ANIM_WATER_GRAB_OBJ_PART2, + /* 0xB0 */ MARIO_ANIM_WATER_GRAB_OBJ_PART1, + /* 0xB1 */ MARIO_ANIM_WATER_THROW_OBJ, + /* 0xB2 */ MARIO_ANIM_WATER_IDLE, + /* 0xB3 */ MARIO_ANIM_WATER_STAR_DANCE, + /* 0xB4 */ MARIO_ANIM_RETURN_FROM_WATER_STAR_DANCE, + /* 0xB5 */ MARIO_ANIM_GRAB_BOWSER, + /* 0xB6 */ MARIO_ANIM_SWINGING_BOWSER, + /* 0xB7 */ MARIO_ANIM_RELEASE_BOWSER, + /* 0xB8 */ MARIO_ANIM_HOLDING_BOWSER, + /* 0xB9 */ MARIO_ANIM_HEAVY_THROW, + /* 0xBA */ MARIO_ANIM_WALK_PANTING, + /* 0xBB */ MARIO_ANIM_WALK_WITH_HEAVY_OBJ, + /* 0xBC */ MARIO_ANIM_TURNING_PART1, + /* 0xBD */ MARIO_ANIM_TURNING_PART2, + /* 0xBE */ MARIO_ANIM_SLIDEFLIP_LAND, + /* 0XBF */ MARIO_ANIM_SLIDEFLIP, + /* 0xC0 */ MARIO_ANIM_TRIPLE_JUMP_LAND, + /* 0xC1 */ MARIO_ANIM_TRIPLE_JUMP, + /* 0xC2 */ MARIO_ANIM_FIRST_PERSON, + /* 0xC3 */ MARIO_ANIM_IDLE_HEAD_LEFT, + /* 0xC4 */ MARIO_ANIM_IDLE_HEAD_RIGHT, + /* 0xC5 */ MARIO_ANIM_IDLE_HEAD_CENTER, + /* 0xC6 */ MARIO_ANIM_HANDSTAND_LEFT, + /* 0xC7 */ MARIO_ANIM_HANDSTAND_RIGHT, + /* 0xC8 */ MARIO_ANIM_WAKE_FROM_SLEEP, + /* 0xC9 */ MARIO_ANIM_WAKE_FROM_LYING, + /* 0xCA */ MARIO_ANIM_START_TIPTOE, + /* 0xCB */ MARIO_ANIM_SLIDEJUMP, // pole jump and wall kick + /* 0xCC */ MARIO_ANIM_START_WALLKICK, + /* 0xCD */ MARIO_ANIM_STAR_DANCE, + /* 0xCE */ MARIO_ANIM_RETURN_FROM_STAR_DANCE, + /* 0xCF */ MARIO_ANIM_FORWARD_SPINNING_FLIP, + /* 0xD0 */ MARIO_ANIM_TRIPLE_JUMP_FLY +}; + +#endif // MARIO_ANIMATION_IDS_H diff --git a/src/include/mario_geo_switch_case_ids.h b/src/include/mario_geo_switch_case_ids.h new file mode 100644 index 0000000..007d23b --- /dev/null +++ b/src/include/mario_geo_switch_case_ids.h @@ -0,0 +1,45 @@ +#ifndef MARIO_GEO_SWITCH_CASE_IDS_H +#define MARIO_GEO_SWITCH_CASE_IDS_H + +/* Mario Geo-Switch-Case IDs */ + +enum MarioEyesGSCId +{ + /*0x00*/ MARIO_EYES_BLINK, + /*0x01*/ MARIO_EYES_OPEN, + /*0x02*/ MARIO_EYES_HALF_CLOSED, + /*0x03*/ MARIO_EYES_CLOSED, + /*0x04*/ MARIO_EYES_LOOK_LEFT, // unused + /*0x05*/ MARIO_EYES_LOOK_RIGHT, // unused + /*0x06*/ MARIO_EYES_LOOK_UP, // unused + /*0x07*/ MARIO_EYES_LOOK_DOWN, // unused + /*0x08*/ MARIO_EYES_DEAD +}; + +enum MarioHandGSCId +{ + /*0x00*/ MARIO_HAND_FISTS, + /*0x01*/ MARIO_HAND_OPEN, + /*0x02*/ MARIO_HAND_PEACE_SIGN, + /*0x03*/ MARIO_HAND_HOLDING_CAP, + /*0x04*/ MARIO_HAND_HOLDING_WING_CAP, + /*0x05*/ MARIO_HAND_RIGHT_OPEN +}; + +enum MarioCapGSCId +{ + /*0x00*/ MARIO_HAS_DEFAULT_CAP_ON, + /*0x01*/ MARIO_HAS_DEFAULT_CAP_OFF, + /*0x02*/ MARIO_HAS_WING_CAP_ON, + /*0x03*/ MARIO_HAS_WING_CAP_OFF // unused +}; + +enum MarioGrabPosGSCId +{ + /*0x00*/ GRAB_POS_NULL, + /*0x01*/ GRAB_POS_LIGHT_OBJ, + /*0x02*/ GRAB_POS_HEAVY_OBJ, + /*0x03*/ GRAB_POS_BOWSER +}; + +#endif // MARIO_GEO_SWITCH_CASE_IDS_H diff --git a/src/include/object_fields.h b/src/include/object_fields.h new file mode 100644 index 0000000..5914a5a --- /dev/null +++ b/src/include/object_fields.h @@ -0,0 +1,1161 @@ +#ifndef OBJECT_FIELDS_H +#define OBJECT_FIELDS_H + +/** + * The array [0x88, 0x1C8) in struct Object consists of fields that can vary by + * object type. These macros provide access to these fields. + */ + +#ifdef OBJECT_FIELDS_INDEX_DIRECTLY +#define OBJECT_FIELD_U32(index) index +#define OBJECT_FIELD_S32(index) index +#define OBJECT_FIELD_S16(index, subIndex) index +#define OBJECT_FIELD_F32(index) index +#define OBJECT_FIELD_S16P(index) index +#define OBJECT_FIELD_S32P(index) index +#define OBJECT_FIELD_ANIMS(index) index +#define OBJECT_FIELD_WAYPOINT(index) index +#define OBJECT_FIELD_CHAIN_SEGMENT(index) index +#define OBJECT_FIELD_OBJ(index) index +#define OBJECT_FIELD_SURFACE(index) index +#define OBJECT_FIELD_VPTR(index) index +#define OBJECT_FIELD_CVPTR(index) index +#else +#define OBJECT_FIELD_U32(index) rawData.asU32[index] +#define OBJECT_FIELD_S32(index) rawData.asS32[index] +#define OBJECT_FIELD_S16(index, subIndex) rawData.asS16[index][subIndex] +#define OBJECT_FIELD_F32(index) rawData.asF32[index] +#if !IS_64_BIT +#define OBJECT_FIELD_S16P(index) rawData.asS16P[index] +#define OBJECT_FIELD_S32P(index) rawData.asS32P[index] +#define OBJECT_FIELD_ANIMS(index) rawData.asAnims[index] +#define OBJECT_FIELD_WAYPOINT(index) rawData.asWaypoint[index] +#define OBJECT_FIELD_CHAIN_SEGMENT(index) rawData.asChainSegment[index] +#define OBJECT_FIELD_OBJ(index) rawData.asObject[index] +#define OBJECT_FIELD_SURFACE(index) rawData.asSurface[index] +#define OBJECT_FIELD_VPTR(index) rawData.asVoidPtr[index] +#define OBJECT_FIELD_CVPTR(index) rawData.asConstVoidPtr[index] +#else +#define OBJECT_FIELD_S16P(index) ptrData.asS16P[index] +#define OBJECT_FIELD_S32P(index) ptrData.asS32P[index] +#define OBJECT_FIELD_ANIMS(index) ptrData.asAnims[index] +#define OBJECT_FIELD_WAYPOINT(index) ptrData.asWaypoint[index] +#define OBJECT_FIELD_CHAIN_SEGMENT(index) ptrData.asChainSegment[index] +#define OBJECT_FIELD_OBJ(index) ptrData.asObject[index] +#define OBJECT_FIELD_SURFACE(index) ptrData.asSurface[index] +#define OBJECT_FIELD_VPTR(index) ptrData.asVoidPtr[index] +#define OBJECT_FIELD_CVPTR(index) ptrData.asConstVoidPtr[index] +#endif +#endif + +// 0x088 (0x00), the first field, is object-specific and defined below the common fields. +/* Common fields */ +#define /*0x08C*/ oFlags OBJECT_FIELD_U32(0x01) +#define /*0x090*/ oDialogResponse OBJECT_FIELD_S16(0x02, 0) +#define /*0x092*/ oDialogState OBJECT_FIELD_S16(0x02, 1) +#define /*0x094*/ oUnk94 OBJECT_FIELD_U32(0x03) +// 0x98 unused/removed. +#define /*0x09C*/ oIntangibleTimer OBJECT_FIELD_S32(0x05) +#define /*0x0A0*/ O_POS_INDEX 0x06 +#define /*0x0A0*/ oPosX OBJECT_FIELD_F32(O_POS_INDEX + 0) +#define /*0x0A4*/ oPosY OBJECT_FIELD_F32(O_POS_INDEX + 1) +#define /*0x0A8*/ oPosZ OBJECT_FIELD_F32(O_POS_INDEX + 2) +#define /*0x0AC*/ oVelX OBJECT_FIELD_F32(0x09) +#define /*0x0B0*/ oVelY OBJECT_FIELD_F32(0x0A) +#define /*0x0B4*/ oVelZ OBJECT_FIELD_F32(0x0B) +#define /*0x0B8*/ oForwardVel OBJECT_FIELD_F32(0x0C) +#define /*0x0B8*/ oForwardVelS32 OBJECT_FIELD_S32(0x0C) +#define /*0x0BC*/ oUnkBC OBJECT_FIELD_F32(0x0D) +#define /*0x0C0*/ oUnkC0 OBJECT_FIELD_F32(0x0E) +#define /*0x0C4*/ O_MOVE_ANGLE_INDEX 0x0F +#define /*0x0C4*/ O_MOVE_ANGLE_PITCH_INDEX (O_MOVE_ANGLE_INDEX + 0) +#define /*0x0C4*/ O_MOVE_ANGLE_YAW_INDEX (O_MOVE_ANGLE_INDEX + 1) +#define /*0x0C4*/ O_MOVE_ANGLE_ROLL_INDEX (O_MOVE_ANGLE_INDEX + 2) +#define /*0x0C4*/ oMoveAnglePitch OBJECT_FIELD_S32(O_MOVE_ANGLE_PITCH_INDEX) +#define /*0x0C8*/ oMoveAngleYaw OBJECT_FIELD_S32(O_MOVE_ANGLE_YAW_INDEX) +#define /*0x0CC*/ oMoveAngleRoll OBJECT_FIELD_S32(O_MOVE_ANGLE_ROLL_INDEX) +#define /*0x0D0*/ O_FACE_ANGLE_INDEX 0x12 +#define /*0x0D0*/ O_FACE_ANGLE_PITCH_INDEX (O_FACE_ANGLE_INDEX + 0) +#define /*0x0D0*/ O_FACE_ANGLE_YAW_INDEX (O_FACE_ANGLE_INDEX + 1) +#define /*0x0D0*/ O_FACE_ANGLE_ROLL_INDEX (O_FACE_ANGLE_INDEX + 2) +#define /*0x0D0*/ oFaceAnglePitch OBJECT_FIELD_S32(O_FACE_ANGLE_PITCH_INDEX) +#define /*0x0D4*/ oFaceAngleYaw OBJECT_FIELD_S32(O_FACE_ANGLE_YAW_INDEX) +#define /*0x0D8*/ oFaceAngleRoll OBJECT_FIELD_S32(O_FACE_ANGLE_ROLL_INDEX) +#define /*0x0DC*/ oGraphYOffset OBJECT_FIELD_F32(0x15) +#define /*0x0E0*/ oActiveParticleFlags OBJECT_FIELD_U32(0x16) +#define /*0x0E4*/ oGravity OBJECT_FIELD_F32(0x17) +#define /*0x0E8*/ oFloorHeight OBJECT_FIELD_F32(0x18) +#define /*0x0EC*/ oMoveFlags OBJECT_FIELD_U32(0x19) +#define /*0x0F0*/ oAnimState OBJECT_FIELD_S32(0x1A) +// 0x0F4-0x110 (0x1B-0x22) are object specific and defined below the common fields. +#define /*0x114*/ oAngleVelPitch OBJECT_FIELD_S32(0x23) +#define /*0x118*/ oAngleVelYaw OBJECT_FIELD_S32(0x24) +#define /*0x11C*/ oAngleVelRoll OBJECT_FIELD_S32(0x25) +#define /*0x120*/ oAnimations OBJECT_FIELD_ANIMS(0x26) +#define /*0x124*/ oHeldState OBJECT_FIELD_U32(0x27) +#define /*0x128*/ oWallHitboxRadius OBJECT_FIELD_F32(0x28) +#define /*0x12C*/ oDragStrength OBJECT_FIELD_F32(0x29) +#define /*0x130*/ oInteractType OBJECT_FIELD_U32(0x2A) +#define /*0x134*/ oInteractStatus OBJECT_FIELD_S32(0x2B) +#define /*0x138*/ O_PARENT_RELATIVE_POS_INDEX 0x2C +#define /*0x138*/ oParentRelativePosX OBJECT_FIELD_F32(O_PARENT_RELATIVE_POS_INDEX + 0) +#define /*0x13C*/ oParentRelativePosY OBJECT_FIELD_F32(O_PARENT_RELATIVE_POS_INDEX + 1) +#define /*0x140*/ oParentRelativePosZ OBJECT_FIELD_F32(O_PARENT_RELATIVE_POS_INDEX + 2) +#define /*0x144*/ oBehParams2ndByte OBJECT_FIELD_S32(0x2F) +// 0x148 unused, possibly a third param byte. +#define /*0x14C*/ oAction OBJECT_FIELD_S32(0x31) +#define /*0x150*/ oSubAction OBJECT_FIELD_S32(0x32) +#define /*0x154*/ oTimer OBJECT_FIELD_S32(0x33) +#define /*0x158*/ oBounciness OBJECT_FIELD_F32(0x34) +#define /*0x15C*/ oDistanceToMario OBJECT_FIELD_F32(0x35) +#define /*0x160*/ oAngleToMario OBJECT_FIELD_S32(0x36) +#define /*0x164*/ oHomeX OBJECT_FIELD_F32(0x37) +#define /*0x168*/ oHomeY OBJECT_FIELD_F32(0x38) +#define /*0x16C*/ oHomeZ OBJECT_FIELD_F32(0x39) +#define /*0x170*/ oFriction OBJECT_FIELD_F32(0x3A) +#define /*0x174*/ oBuoyancy OBJECT_FIELD_F32(0x3B) +#define /*0x178*/ oSoundStateID OBJECT_FIELD_S32(0x3C) +#define /*0x17C*/ oOpacity OBJECT_FIELD_S32(0x3D) +#define /*0x180*/ oDamageOrCoinValue OBJECT_FIELD_S32(0x3E) +#define /*0x184*/ oHealth OBJECT_FIELD_S32(0x3F) +#define /*0x188*/ oBehParams OBJECT_FIELD_S32(0x40) +#define /*0x18C*/ oPrevAction OBJECT_FIELD_S32(0x41) +#define /*0x190*/ oInteractionSubtype OBJECT_FIELD_U32(0x42) +#define /*0x194*/ oCollisionDistance OBJECT_FIELD_F32(0x43) +#define /*0x198*/ oNumLootCoins OBJECT_FIELD_S32(0x44) +#define /*0x19C*/ oDrawingDistance OBJECT_FIELD_F32(0x45) +#define /*0x1A0*/ oRoom OBJECT_FIELD_S32(0x46) +// 0x1A4 is unused, possibly related to 0x1A8 in removed macro purposes. +#define /*0x1A8*/ oUnk1A8 OBJECT_FIELD_U32(0x48) +// 0x1AC-0x1B2 (0x48-0x4A) are object specific and defined below the common fields. +#define /*0x1B4*/ oWallAngle OBJECT_FIELD_S32(0x4B) +#define /*0x1B8*/ oFloorType OBJECT_FIELD_S16(0x4C, 0) +#define /*0x1BA*/ oFloorRoom OBJECT_FIELD_S16(0x4C, 1) +#define /*0x1BC*/ oAngleToHome OBJECT_FIELD_S32(0x4D) +#define /*0x1C0*/ oFloor OBJECT_FIELD_SURFACE(0x4E) +#define /*0x1C4*/ oDeathSound OBJECT_FIELD_S32(0x4F) + +/* Pathed (see obj_follow_path) */ +#define /*0x0FC*/ oPathedStartWaypoint OBJECT_FIELD_WAYPOINT(0x1D) +#define /*0x100*/ oPathedPrevWaypoint OBJECT_FIELD_WAYPOINT(0x1E) +#define /*0x104*/ oPathedPrevWaypointFlags OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oPathedTargetPitch OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oPathedTargetYaw OBJECT_FIELD_S32(0x21) + +/* Special Object Macro */ +#define /*0x108*/ oMacroUnk108 OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oMacroUnk10C OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oMacroUnk110 OBJECT_FIELD_F32(0x22) + +/* Mario */ +#define /*0x0F4*/ oMarioParticleFlags OBJECT_FIELD_S32(0x1B) +#define /*0x108*/ oMarioPoleUnk108 OBJECT_FIELD_S32(0x20) +#define /*0x108*/ oMarioReadingSignDYaw OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oMarioPoleYawVel OBJECT_FIELD_S32(0x21) +#define /*0x10C*/ oMarioCannonObjectYaw OBJECT_FIELD_S32(0x21) +#define /*0x10C*/ oMarioTornadoYawVel OBJECT_FIELD_S32(0x21) +#define /*0x10C*/ oMarioReadingSignDPosX OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oMarioPolePos OBJECT_FIELD_F32(0x22) +#define /*0x110*/ oMarioCannonInputYaw OBJECT_FIELD_S32(0x22) +#define /*0x110*/ oMarioTornadoPosY OBJECT_FIELD_F32(0x22) +#define /*0x110*/ oMarioReadingSignDPosZ OBJECT_FIELD_F32(0x22) +#define /*0x110*/ oMarioWhirlpoolPosY OBJECT_FIELD_F32(0x22) +#define /*0x110*/ oMarioBurnTimer OBJECT_FIELD_S32(0x22) +#define /*0x110*/ oMarioLongJumpIsSlow OBJECT_FIELD_S32(0x22) +#define /*0x110*/ oMarioSteepJumpYaw OBJECT_FIELD_S32(0x22) +#define /*0x110*/ oMarioWalkingPitch OBJECT_FIELD_S32(0x22) + +/* 1-Up Hidden */ +#define /*0x0F4*/ o1UpHiddenUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Activated Back and Forth Platform */ +#define /*0x0F4*/ oActivatedBackAndForthPlatformMaxOffset OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oActivatedBackAndForthPlatformOffset OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oActivatedBackAndForthPlatformVel OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oActivatedBackAndForthPlatformCountdown OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oActivatedBackAndForthPlatformStartYaw OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oActivatedBackAndForthPlatformVertical OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oActivatedBackAndForthPlatformFlipRotation OBJECT_FIELD_S32(0x21) + +/* Amp */ +#define /*0x0F4*/ oAmpRadiusOfRotation OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oAmpYPhase OBJECT_FIELD_S32(0x1C) + +/* Homing Amp */ +#define /*0x0F4*/ oHomingAmpLockedOn OBJECT_FIELD_S32(0x1B) +#define /*0x0FC*/ oHomingAmpAvgY OBJECT_FIELD_F32(0x1D) + +/* Arrow Lift */ +#define /*0x0F4*/ oArrowLiftDisplacement OBJECT_FIELD_F32(0x1B) +#define /*0x100*/ oArrowLiftUnk100 OBJECT_FIELD_S32(0x1E) + +/* Back-and-Forth Platform */ +#define /*0x0F4*/ oBackAndForthPlatformUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oBackAndForthPlatformUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oBackAndForthPlatformUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oBackAndForthPlatformUnk100 OBJECT_FIELD_F32(0x1E) + +/* Bird */ +#define /*0x0F4*/ oBirdSpeed OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oBirdTargetPitch OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oBirdTargetYaw OBJECT_FIELD_S32(0x1D) + +/* Bird Chirp Chirp */ +#define /*0x0F4*/ oBirdChirpChirpUnkF4 OBJECT_FIELD_S32(0x1B) + +/* End Birds */ +#define /*0x104*/ oEndBirdUnk104 OBJECT_FIELD_F32(0x1F) + +/* Hidden Blue Coin */ +#define /*0x0F8*/ oHiddenBlueCoinSwitch OBJECT_FIELD_OBJ(0x1C) + +/* Bob-omb */ +#define /*0x0F4*/ oBobombBlinkTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBobombFuseLit OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oBobombFuseTimer OBJECT_FIELD_S32(0x1D) + +/* Bob-omb Buddy */ +#define /*0x0F4*/ oBobombBuddyBlinkTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBobombBuddyHasTalkedToMario OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oBobombBuddyRole OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oBobombBuddyCannonStatus OBJECT_FIELD_S32(0x1E) +#define /*0x108*/ oBobombBuddyPosXCopy OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oBobombBuddyPosYCopy OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oBobombBuddyPosZCopy OBJECT_FIELD_F32(0x22) + +/* Bob-omb Explosion Bubble */ +#define /*0x0FC*/ oBobombExpBubGfxScaleFacX OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oBobombExpBubGfxScaleFacY OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oBobombExpBubGfxExpRateX OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oBobombExpBubGfxExpRateY OBJECT_FIELD_S32(0x20) + +/* Bomp (Small) */ +#define /*0x100*/ oSmallBompInitX OBJECT_FIELD_F32(0x1E) + +/* Boo */ +#define /*0x088*/ oBooDeathStatus OBJECT_FIELD_S32(0x00) +#define /*0x0F4*/ oBooTargetOpacity OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBooBaseScale OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oBooOscillationTimer OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oBooMoveYawDuringHit OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oBooMoveYawBeforeHit OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oBooParentBigBoo OBJECT_FIELD_OBJ(0x20) +#define /*0x10C*/ oBooNegatedAggressiveness OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oBooInitialMoveYaw OBJECT_FIELD_S32(0x22) +#define /*0x1B0*/ oBooTurningSpeed OBJECT_FIELD_S16(0x4A, 0) + +/* Big Boo */ +#define /*0x1AC*/ oBigBooNumMinionBoosKilled OBJECT_FIELD_S32(0x49) + +/* Bookend */ +#define /*0x0F4*/ oBookendUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBookendUnkF8 OBJECT_FIELD_S32(0x1C) + +/* Book Switch */ +#define /*0x0F4*/ oBookSwitchUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Book Switch Manager */ +#define /*0x0F4*/ oBookSwitchManagerUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBookSwitchManagerUnkF8 OBJECT_FIELD_S32(0x1C) + +/* Haunted Bookshelf */ +#define /*0x088*/ oHauntedBookshelfShouldOpen OBJECT_FIELD_S32(0x00) + +/* Bouncing FireBall */ +#define /*0x0F4*/ oBouncingFireBallUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Bowling Ball */ +#define /*0x0F4*/ oBowlingBallTargetYaw OBJECT_FIELD_S32(0x1B) +// 0x1D-0x21 reserved for pathing + +/* Bowling Ball Spawner (Generic) */ +#define /*0x0F4*/ oBBallSpawnerMaxSpawnDist OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oBBallSpawnerSpawnOdds OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oBBallSpawnerPeriodMinus1 OBJECT_FIELD_S32(0x1D) + +/* Bowser */ +#define /*0x088*/ oBowserUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F4*/ oBowserUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBowserUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oBowserDistToCentre OBJECT_FIELD_F32(0x1D) +#define /*0x106*/ oBowserUnk106 OBJECT_FIELD_S16(0x1F, 1) +#define /*0x108*/ oBowserUnk108 OBJECT_FIELD_S16(0x20, 0) +#define /*0x10A*/ oBowserHeldAnglePitch OBJECT_FIELD_S16(0x20, 1) +#define /*0x10D*/ oBowserHeldAngleVelYaw OBJECT_FIELD_S16(0x21, 0) +#define /*0x10E*/ oBowserUnk10E OBJECT_FIELD_S16(0x21, 1) +#define /*0x110*/ oBowserUnk110 OBJECT_FIELD_S16(0x22, 0) +#define /*0x112*/ oBowserAngleToCentre OBJECT_FIELD_S16(0x22, 1) +#define /*0x1AC*/ oBowserUnk1AC OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oBowserUnk1AE OBJECT_FIELD_S16(0x49, 1) +#define /*0x1B0*/ oBowserEyesShut OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oBowserUnk1B2 OBJECT_FIELD_S16(0x4A, 1) + +/* Bowser Shockwave */ +#define /*0x0F4*/ oBowserShockWaveUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Black Smoke Bowser */ +#define /*0x0F4*/ oBlackSmokeBowserUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Bowser Key Cutscene */ +#define /*0x0F4*/ oBowserKeyScale OBJECT_FIELD_F32(0x1B) + +/* Bowser Puzzle */ +#define /*0x0F4*/ oBowserPuzzleCompletionFlags OBJECT_FIELD_S32(0x1B) + +/* Bowser Puzzle Piece */ +#define /*0x0FC*/ oBowserPuzzlePieceOffsetX OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oBowserPuzzlePieceOffsetY OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oBowserPuzzlePieceOffsetZ OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oBowserPuzzlePieceContinuePerformingAction OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oBowserPuzzlePieceActionList OBJECT_FIELD_VPTR(0x21) +#define /*0x110*/ oBowserPuzzlePieceNextAction OBJECT_FIELD_VPTR(0x22) + +/* Bubba */ +#define /*0x0F4*/ oBubbaUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oBubbaUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oBubbaUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oBubbaUnk100 OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oBubbaUnk104 OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oBubbaUnk108 OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oBubbaUnk10C OBJECT_FIELD_F32(0x21) +#define /*0x1AC*/ oBubbaUnk1AC OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oBubbaUnk1AE OBJECT_FIELD_S16(0x49, + 1) +#define /*0x1B0*/ oBubbaUnk1B0 OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oBubbaUnk1B2 OBJECT_FIELD_S16(0x4A, + 1) + +/* Bullet Bill */ +#define /*0x0F8*/ oBulletBillInitialMoveYaw OBJECT_FIELD_S32(0x1C) + +/* Bully (all variants) */ +#define /*0x0F4*/ oBullySubtype OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oBullyPrevX OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oBullyPrevY OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oBullyPrevZ OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oBullyKBTimerAndMinionKOCounter OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oBullyMarioCollisionAngle OBJECT_FIELD_S32(0x20) + +/* Butterfly */ +#define /*0x0F4*/ oButterflyYPhase OBJECT_FIELD_S32(0x1B) + +/* Triplet Butterfly */ +#define /*0x0F4*/ oTripletButterflyScale OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oTripletButterflySpeed OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oTripletButterflyBaseYaw OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oTripletButterflyTargetPitch OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oTripletButterflyTargetYaw OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oTripletButterflyType OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oTripletButterflyModel OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oTripletButterflySelectedButterfly OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oTripletButterflyScalePhase OBJECT_FIELD_S32(0x49) + +/* Cannon */ +#define /*0x0F4*/ oCannonUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oCannonUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x10C*/ oCannonUnk10C OBJECT_FIELD_S32(0x21) + +/* Cap */ +#define /*0x0F4*/ oCapUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oCapUnkF8 OBJECT_FIELD_S32(0x1C) + +/* Chain Chomp */ +#define /*0x0F4*/ oChainChompSegments OBJECT_FIELD_CHAIN_SEGMENT(0x1B) +#define /*0x0F8*/ oChainChompMaxDistFromPivotPerChainPart OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oChainChompMaxDistBetweenChainParts OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oChainChompDistToPivot OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oChainChompUnk104 OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oChainChompRestrictedByChain OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oChainChompTargetPitch OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oChainChompNumLunges OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oChainChompReleaseStatus OBJECT_FIELD_S32(0x49) +#define /*0x1B0*/ oChainChompHitGate OBJECT_FIELD_S32(0x4A) + +/* Checkerboard Platform */ +#define /*0x0F8*/ oCheckerBoardPlatformUnkF8 OBJECT_FIELD_S32(0x1C) // oAction like +#define /*0x0FC*/ oCheckerBoardPlatformUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x1AC*/ oCheckerBoardPlatformUnk1AC OBJECT_FIELD_F32(0x49) + +/* Cheep Cheep */ +#define /*0x0F4*/ oCheepCheepUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oCheepCheepUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oCheepCheepUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x104*/ oCheepCheepUnk104 OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oCheepCheepUnk108 OBJECT_FIELD_F32(0x20) + +/* Chuckya */ +#define /*0x088*/ oChuckyaUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F8*/ oChuckyaUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oChuckyaUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oChuckyaUnk100 OBJECT_FIELD_S32(0x1E) + +/* Clam */ +#define /*0x0F4*/ oClamUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Cloud */ +#define /*0x0F4*/ oCloudCenterX OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oCloudCenterY OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oCloudBlowing OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oCloudGrowSpeed OBJECT_FIELD_F32(0x1E) +#define /*0x1AC*/ oCloudFwooshMovementRadius OBJECT_FIELD_S16(0x49, 0) + +/* Coin */ +#define /*0x0F4*/ oCoinUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oCoinUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x110*/ oCoinUnk110 OBJECT_FIELD_F32(0x22) +#ifndef VERSION_JP +#define /*0x1B0*/ oCoinUnk1B0 OBJECT_FIELD_S32(0x4A) +#endif + +/* Collision Particle */ +#define /*0x0F4*/ oCollisionParticleUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Controllable Platform */ +#define /*0x0F8*/ oControllablePlatformUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oControllablePlatformUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oControllablePlatformUnk100 OBJECT_FIELD_S32(0x1E) + +/* Breakable Box Small (Small Cork Box) */ +#define /*0x0F4*/ oBreakableBoxSmallReleased OBJECT_FIELD_S32(0x1B) +#define /*0x0FC*/ oBreakableBoxSmallFramesSinceReleased OBJECT_FIELD_S32(0x1D) + +/* Jumping Box (Crazy Box) */ +#define /*0x0F4*/ oJumpingBoxUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oJumpingBoxUnkF8 OBJECT_FIELD_S32(0x1C) + +/* RR Cruiser Wing */ +#define /*0x0F4*/ oRRCruiserWingUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oRRCruiserWingUnkF8 OBJECT_FIELD_S32(0x1C) + +/* Donut Platform Spawner */ +#define /*0x0F4*/ oDonutPlatformSpawnerSpawnedPlatforms OBJECT_FIELD_S32(0x1B) + +/* Door */ +#define /*0x088*/ oDoorUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F8*/ oDoorUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oDoorUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oDoorUnk100 OBJECT_FIELD_S32(0x1E) + +/* Dorrie */ +#define /*0x0F4*/ oDorrieDistToHome OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oDorrieOffsetY OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oDorrieVelY OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oDorrieForwardDistToMario OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oDorrieYawVel OBJECT_FIELD_S32(0x1F) +#define /*0x10C*/ oDorrieLiftingMario OBJECT_FIELD_S32(0x21) +#define /*0x1AC*/ oDorrieGroundPounded OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oDorrieAngleToHome OBJECT_FIELD_S16(0x49, + 1) +#define /*0x1B0*/ oDorrieNeckAngle OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oDorrieHeadRaiseSpeed OBJECT_FIELD_S16(0x4A, + 1) + +/* Elevator */ +#define /*0x0F4*/ oElevatorUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oElevatorUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oElevatorUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oElevatorUnk100 OBJECT_FIELD_S32(0x1E) + +/* Exclamation Box */ +#define /*0x0F4*/ oExclamationBoxUnkF4 OBJECT_FIELD_F32(0x1B) // scale? +#define /*0x0F8*/ oExclamationBoxUnkF8 OBJECT_FIELD_F32(0x1C) // scale? +#define /*0x0FC*/ oExclamationBoxUnkFC OBJECT_FIELD_S32(0x1D) // angle? + +/* Eyerok Boss */ +#define /*0x0F8*/ oEyerokBossNumHands OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oEyerokBossUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oEyerokBossActiveHand OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oEyerokBossUnk104 OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oEyerokBossUnk108 OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oEyerokBossUnk10C OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oEyerokBossUnk110 OBJECT_FIELD_F32(0x22) +#define /*0x1AC*/ oEyerokBossUnk1AC OBJECT_FIELD_S32(0x49) + +/* Eyerok Hand */ +#define /*0x0F4*/ oEyerokHandWakeUpTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oEyerokReceivedAttack OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oEyerokHandUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oEyerokHandUnk100 OBJECT_FIELD_S32(0x1E) + +/* Falling Pillar */ +#define /*0x0F4*/ oFallingPillarPitchAcceleration OBJECT_FIELD_F32(0x1B) + +/* Fire Spitter */ +#define /*0x0F4*/ oFireSpitterScaleVel OBJECT_FIELD_F32(0x1B) + +/* Blue Fish */ +#define /*0x0F4*/ oBlueFishRandomVel OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oBlueFishRandomTime OBJECT_FIELD_S32(0x1C) +#define /*0x100*/ oBlueFishRandomAngle OBJECT_FIELD_F32(0x1E) + +/* Fish Group */ +#define /*0x0F4*/ oFishWaterLevel OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oFishPosY OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oFishRandomOffset OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oFishRandomSpeed OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oFishRespawnDistance OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oFishRandomVel OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oFishDepthDistance OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oFishActiveDistance OBJECT_FIELD_F32(0x22) + +/* Flame */ +#define /*0x0F4*/ oFlameUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oFlameUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oFlameUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oFlameUnk100 OBJECT_FIELD_OBJ(0x1E) + +/* Blue Flame */ +#define /*0x0F8*/ oBlueFlameUnkF8 OBJECT_FIELD_F32(0x1C) + +/* Small Piranha Flame */ +#define /*0x0F4*/ oSmallPiranhaFlameStartSpeed OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oSmallPiranhaFlameEndSpeed OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oSmallPiranhaFlameModel OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oSmallPiranhaFlameUnk100 OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oSmallPiranhaFlameUnk104 OBJECT_FIELD_F32(0x1F) + +/* Moving Flame */ +#define /*0x0F4*/ oMovingFlameTimer OBJECT_FIELD_S32(0x1B) + +/* Flamethrower Flame */ +#define /*0x110*/ oFlameThowerFlameUnk110 OBJECT_FIELD_S32(0x22) + +/* Flamethrower */ +#define /*0x110*/ oFlameThowerUnk110 OBJECT_FIELD_S32(0x22) + +/* Floating Platform */ +#define /*0x0F4*/ oFloatingPlatformUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oFloatingPlatformUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oFloatingPlatformUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oFloatingPlatformUnk100 OBJECT_FIELD_S32(0x1E) + +/* Floor Switch Press Animation */ +#define /*0x0F4*/ oFloorSwitchPressAnimationUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oFloorSwitchPressAnimationUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oFloorSwitchPressAnimationUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oFloorSwitchPressAnimationUnk100 OBJECT_FIELD_S32(0x1E) + +/* Fly Guy */ +#define /*0x0F4*/ oFlyGuyIdleTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oFlyGuyOscTimer OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oFlyGuyUnusedJitter OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oFlyGuyLungeYDecel OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oFlyGuyLungeTargetPitch OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oFlyGuyTargetRoll OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oFlyGuyScaleVel OBJECT_FIELD_F32(0x21) + +/* Grand Star */ +#define /*0x108*/ oGrandStarUnk108 OBJECT_FIELD_S32(0x20) + +/* Horizontal Grindel */ +#define /*0x0F4*/ oHorizontalGrindelTargetYaw OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oHorizontalGrindelDistToHome OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oHorizontalGrindelOnGround OBJECT_FIELD_S32(0x1D) + +/* Goomba */ +#define /*0x0F4*/ oGoombaSize OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oGoombaScale OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oGoombaWalkTimer OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oGoombaTargetYaw OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oGoombaBlinkTimer OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oGoombaTurningAwayFromWall OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oGoombaRelativeSpeed OBJECT_FIELD_F32(0x21) + +/* Haunted Chair */ +#define /*0x0F4*/ oHauntedChairUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oHauntedChairUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oHauntedChairUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oHauntedChairUnk100 OBJECT_FIELD_S32P(0x1E) +#define /*0x104*/ oHauntedChairUnk104 OBJECT_FIELD_S32(0x1F) + +/* Heave-Ho */ +#define /*0x088*/ oHeaveHoUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F4*/ oHeaveHoUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Hidden Object */ +#define /*0x0F4*/ oHiddenObjectUnkF4 OBJECT_FIELD_OBJ(0x1B) + +/* Hoot */ +#define /*0x0F4*/ oHootAvailability OBJECT_FIELD_S32(0x1B) +#define /*0x110*/ oHootMarioReleaseTime OBJECT_FIELD_S32(0x22) + +/* Horizontal Movement */ +#define /*0x0F4*/ oHorizontalMovementUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oHorizontalMovementUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x100*/ oHorizontalMovementUnk100 OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oHorizontalMovementUnk104 OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oHorizontalMovementUnk108 OBJECT_FIELD_F32(0x20) + +/* Kickable Board */ +#define /*0x0F4*/ oKickableBoardF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oKickableBoardF8 OBJECT_FIELD_S32(0x1C) + + /* King Bob-omb */ +#define /*0x088*/ oKingBobombUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F8*/ oKingBobombUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oKingBobombUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oKingBobombUnk100 OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oKingBobombUnk104 OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oKingBobombUnk108 OBJECT_FIELD_S32(0x20) + +/* Klepto */ +#define /*0x0F4*/ oKleptoDistanceToTarget OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oKleptoUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oKleptoUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oKleptoSpeed OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oKleptoStartPosX OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oKleptoStartPosY OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oKleptoStartPosZ OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oKleptoTimeUntilTargetChange OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oKleptoTargetNumber OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oKleptoUnk1AE OBJECT_FIELD_S16(0x49, + 1) +#define /*0x1B0*/ oKleptoUnk1B0 OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oKleptoYawToTarget OBJECT_FIELD_S16(0x4A, + 1) + +/* Koopa */ +#define /*0x0F4*/ oKoopaAgility OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oKoopaMovementType OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oKoopaTargetYaw OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oKoopaUnshelledTimeUntilTurn OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oKoopaTurningAwayFromWall OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oKoopaDistanceToMario OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oKoopaAngleToMario OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oKoopaBlinkTimer OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oKoopaCountdown OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oKoopaTheQuickRaceIndex OBJECT_FIELD_S16(0x49, 1) +#define /*0x1B0*/ oKoopaTheQuickInitTextboxCooldown OBJECT_FIELD_S16(0x4A, 0) +// 0x1D-0x21 for koopa the quick reserved for pathing + +/* Koopa Race Endpoint */ +#define /*0x0F4*/ oKoopaRaceEndpointRaceBegun OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oKoopaRaceEndpointKoopaFinished OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oKoopaRaceEndpointRaceStatus OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oKoopaRaceEndpointUnk100 OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oKoopaRaceEndpointRaceEnded OBJECT_FIELD_S32(0x1F) + +/* Koopa Shell Flame */ +#define /*0x0F4*/ oKoopaShellFlameUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oKoopaShellFlameUnkF8 OBJECT_FIELD_F32(0x1C) + +/* Camera Lakitu */ +#define /*0x0F4*/ oCameraLakituBlinkTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oCameraLakituSpeed OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oCameraLakituCircleRadius OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oCameraLakituFinishedDialog OBJECT_FIELD_S32(0x1E) +#ifndef VERSION_JP +#define /*0x104*/ oCameraLakituUnk104 OBJECT_FIELD_S32(0x1F) +#endif +#define /*0x1AC*/ oCameraLakituPitchVel OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oCameraLakituYawVel OBJECT_FIELD_S16(0x49, + 1) + +/* Evil Lakitu */ +#define /*0x0F4*/ oEnemyLakituNumSpinies OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oEnemyLakituBlinkTimer OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oEnemyLakituSpinyCooldown OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oEnemyLakituFaceForwardCountdown OBJECT_FIELD_S32(0x1E) + +/* Intro Cutscene Lakitu */ +#define /*0x0F8*/ oIntroLakituSplineSegmentProgress OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oIntroLakituSplineSegment OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oIntroLakituUnk100 OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oIntroLakituUnk104 OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oIntroLakituUnk108 OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oIntroLakituUnk10C OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oIntroLakituUnk110 OBJECT_FIELD_F32(0x22) +#define /*0x1AC*/ oIntroLakituCloud OBJECT_FIELD_OBJ(0x49) + +/* Main Menu Button */ +#define /*0x0F4*/ oMenuButtonState OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oMenuButtonTimer OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oMenuButtonOrigPosX OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oMenuButtonOrigPosY OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oMenuButtonOrigPosZ OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oMenuButtonScale OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oMenuButtonActionPhase OBJECT_FIELD_S32(0x21) + +/* Manta Ray */ +#define /*0x0F4*/ oMantaUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oMantaUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x1AC*/ oMantaUnk1AC OBJECT_FIELD_S32(0x49) + +/* Merry-Go-Round */ +#define /*0x088*/ oMerryGoRoundStopped OBJECT_FIELD_S32(0x00) +#define /*0x0F8*/ oMerryGoRoundMusicShouldPlay OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oMerryGoRoundMarioIsOutside OBJECT_FIELD_S32(0x1D) + +/* Merry-Go-Round Boo Manager */ +#define /*0x088*/ oMerryGoRoundBooManagerNumBoosKilled OBJECT_FIELD_S32(0x00) +#define /*0x0FC*/ oMerryGoRoundBooManagerNumBoosSpawned OBJECT_FIELD_S32(0x1D) + +/* Mips */ +#define /*0x0F4*/ oMipsStarStatus OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oMipsStartWaypointIndex OBJECT_FIELD_S32(0x1C) + // 0x1D-0x21 reserved for pathing +#define /*0x1AC*/ oMipsForwardVelocity OBJECT_FIELD_F32(0x49) + +/* Moneybag */ +#define /*0x0F4*/ oMoneybagJumpState OBJECT_FIELD_S32(0x1B) + +/* Monty Mole */ +#define /*0x0F4*/ oMontyMoleCurrentHole OBJECT_FIELD_OBJ(0x1B) +#define /*0x0F8*/ oMontyMoleHeightRelativeToFloor OBJECT_FIELD_F32(0x1C) + +/* Monty Mole Hole */ +#define /*0x0F4*/ oMontyMoleHoleCooldown OBJECT_FIELD_S32(0x1B) + +/* Mr. Blizzard */ +#define /*0x0F4*/ oMrBlizzardScale OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oMrBlizzardHeldObj OBJECT_FIELD_OBJ(0x1C) +#define /*0x0FC*/ oMrBlizzardGraphYVel OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oMrBlizzardTimer OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oMrBlizzardDizziness OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oMrBlizzardChangeInDizziness OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oMrBlizzardGraphYOffset OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oMrBlizzardDistFromHome OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oMrBlizzardTargetMoveYaw OBJECT_FIELD_S32(0x49) + +/* Mr. I */ +#define /*0x0F4*/ oMrIUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0FC*/ oMrIUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oMrIUnk100 OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oMrIUnk104 OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oMrIUnk108 OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oMrISize OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oMrIUnk110 OBJECT_FIELD_S32(0x22) + +/* Object Respawner */ +#define /*0x0F4*/ oRespawnerModelToRespawn OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oRespawnerMinSpawnDist OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oRespawnerBehaviorToRespawn OBJECT_FIELD_CVPTR(0x1D) + +/* Openable Grill */ +#define /*0x088*/ oOpenableGrillUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F4*/ oOpenableGrillUnkF4 OBJECT_FIELD_OBJ(0x1B) + +/* Intro Cutscene Peach */ +#define /*0x108*/ oIntroPeachYawFromFocus OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oIntroPeachPitchFromFocus OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oIntroPeachDistToCamera OBJECT_FIELD_F32(0x22) + +/* Racing Penguin */ +#define /*0x0F4*/ oRacingPenguinInitTextCooldown OBJECT_FIELD_S32(0x1B) +// 0x1D-0x21 reserved for pathing +#define /*0x110*/ oRacingPenguinWeightedNewTargetSpeed OBJECT_FIELD_F32(0x22) +#define /*0x1AC*/ oRacingPenguinFinalTextbox OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oRacingPenguinMarioWon OBJECT_FIELD_S16(0x49, + 1) +#define /*0x1B0*/ oRacingPenguinReachedBottom OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oRacingPenguinMarioCheated OBJECT_FIELD_S16(0x4A, + 1) + +/* Small Penguin */ +#define /*0x088*/ oSmallPenguinUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x100*/ oSmallPenguinUnk100 OBJECT_FIELD_S32(0x1E) // angle? +#define /*0x104*/ oSmallPenguinUnk104 OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oSmallPenguinUnk108 OBJECT_FIELD_F32(0x20) +#define /*0x110*/ oSmallPenguinUnk110 OBJECT_FIELD_S32(0x22) + +/* SL Walking Penguin */ +#define /*0x100*/ oSLWalkingPenguinWindCollisionXPos OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oSLWalkingPenguinWindCollisionZPos OBJECT_FIELD_F32(0x1F) +#define /*0x10C*/ oSLWalkingPenguinCurStep OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oSLWalkingPenguinCurStepTimer OBJECT_FIELD_S32(0x22) + +/* Piranha Plant */ +#define /*0x0F4*/ oPiranhaPlantSleepMusicState OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oPiranhaPlantScale OBJECT_FIELD_F32(0x1C) + +/* Fire Piranha Plant */ +#define /*0x0F4*/ oFirePiranhaPlantNeutralScale OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oFirePiranhaPlantScale OBJECT_FIELD_F32(0x1C) //Shared with above obj? Coincidence? +#define /*0x0FC*/ oFirePiranhaPlantActive OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oFirePiranhaPlantDeathSpinTimer OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oFirePiranhaPlantDeathSpinVel OBJECT_FIELD_F32(0x1F) + +/* Pitoune */ +#define /*0x0F4*/ oPitouneUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oPitouneUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oPitouneUnkFC OBJECT_FIELD_F32(0x1D) + +/* Platform */ +#define /*0x0F4*/ oPlatformTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oPlatformUnkF8 OBJECT_FIELD_OBJ(0x1C) +#define /*0x0FC*/ oPlatformUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x10C*/ oPlatformUnk10C OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oPlatformUnk110 OBJECT_FIELD_F32(0x22) + +/* Platform on Tracks */ +#define /*0x088*/ oPlatformOnTrackBaseBallIndex OBJECT_FIELD_S32(0x00) +#define /*0x0F4*/ oPlatformOnTrackDistMovedSinceLastBall OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oPlatformOnTrackSkiLiftRollVel OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oPlatformOnTrackStartWaypoint OBJECT_FIELD_WAYPOINT(0x1D) +#define /*0x100*/ oPlatformOnTrackPrevWaypoint OBJECT_FIELD_WAYPOINT(0x1E) +#define /*0x104*/ oPlatformOnTrackPrevWaypointFlags OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oPlatformOnTrackPitch OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oPlatformOnTrackYaw OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oPlatformOnTrackOffsetY OBJECT_FIELD_F32(0x22) +#define /*0x1AC*/ oPlatformOnTrackIsNotSkiLift OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oPlatformOnTrackIsNotHMC OBJECT_FIELD_S16(0x49, + 1) +#define /*0x1B0*/ oPlatformOnTrackType OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oPlatformOnTrackWasStoodOn OBJECT_FIELD_S16(0x4A, + 1) + +/* Platform Spawner */ +#define /*0x0F4*/ oPlatformSpawnerUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oPlatformSpawnerUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oPlatformSpawnerUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oPlatformSpawnerUnk100 OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oPlatformSpawnerUnk104 OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oPlatformSpawnerUnk108 OBJECT_FIELD_F32(0x20) + +/* Pokey */ +#define /*0x0F4*/ oPokeyAliveBodyPartFlags OBJECT_FIELD_U32(0x1B) +#define /*0x0F8*/ oPokeyNumAliveBodyParts OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oPokeyBottomBodyPartSize OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oPokeyHeadWasKilled OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oPokeyTargetYaw OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oPokeyChangeTargetTimer OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oPokeyTurningAwayFromWall OBJECT_FIELD_S32(0x21) + +/* Pokey Body Part */ +#define /*0x0F8*/ oPokeyBodyPartDeathDelayAfterHeadKilled OBJECT_FIELD_S32(0x1C) +#define /*0x110*/ oPokeyBodyPartBlinkTimer OBJECT_FIELD_S32(0x22) + +/* DDD Pole */ +#define /*0x0F4*/ oDDDPoleVel OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oDDDPoleMaxOffset OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oDDDPoleOffset OBJECT_FIELD_F32(0x1D) + +/* Pyramid Top */ +#define /*0x0F4*/ oPyramidTopPillarsTouched OBJECT_FIELD_S32(0x1B) + +/* Pyramid Top Explosion */ +#define /*0x0F4*/ oPyramidTopFragmentsScale OBJECT_FIELD_F32(0x1B) + +/* Rolling Log */ +#define /*0x0F4*/ oRollingLogUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Lll Rotating Hex Flame */ +#define /*0x0F4*/ oLllRotatingHexFlameUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oLllRotatingHexFlameUnkF8 OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oLllRotatingHexFlameUnkFC OBJECT_FIELD_F32(0x1D) + +/* Scuttlebug */ +#define /*0x0F4*/ oScuttlebugUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oScuttlebugUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oScuttlebugUnkFC OBJECT_FIELD_S32(0x1D) + +/* Scuttlebug Spawner */ +#define /*0x088*/ oScuttlebugSpawnerUnk88 OBJECT_FIELD_S32(0x00) +#define /*0x0F4*/ oScuttlebugSpawnerUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Seesaw Platform */ +#define /*0x0F4*/ oSeesawPlatformPitchVel OBJECT_FIELD_F32(0x1B) + +/* Ship Part 3 */ +#define /*0x0F4*/ oShipPart3UnkF4 OBJECT_FIELD_S32(0x1B) // angle? +#define /*0x0F8*/ oShipPart3UnkF8 OBJECT_FIELD_S32(0x1C) // angle? + +/* Sink When Stepped On */ +#define /*0x104*/ oSinkWhenSteppedOnUnk104 OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oSinkWhenSteppedOnUnk108 OBJECT_FIELD_F32(0x20) + +/* Skeeter */ +#define /*0x0F4*/ oSkeeterTargetAngle OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oSkeeterUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oSkeeterUnkFC OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oSkeeterWaitTime OBJECT_FIELD_S32(0x1E) +#define /*0x1AC*/ oSkeeterUnk1AC OBJECT_FIELD_S16(0x49, 0) + +/* Jrb Sliding Box */ +#define /*0x0F4*/ oJrbSlidingBoxUnkF4 OBJECT_FIELD_OBJ(0x1B) +#define /*0x0F8*/ oJrbSlidingBoxUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oJrbSlidingBoxUnkFC OBJECT_FIELD_F32(0x1D) + +/* WF Sliding Brick Platform */ +#define /*0x0F4*/ oWFSlidBrickPtfmMovVel OBJECT_FIELD_F32(0x1B) + +/* Smoke */ +#define /*0x0F4*/ oSmokeTimer OBJECT_FIELD_S32(0x1B) + +/* Snowman's Bottom */ +#define /*0x0F4*/ oSnowmansBottomUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oSnowmansBottomUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x1AC*/ oSnowmansBottomUnk1AC OBJECT_FIELD_S32(0x49) +// 0x1D-0x21 reserved for pathing + +/* Snowman's Head */ +#define /*0x0F4*/ oSnowmansHeadUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Snowman Wind Blowing */ +#define /*0x0F4*/ oSLSnowmanWindOriginalYaw OBJECT_FIELD_S32(0x1B) + +/* Snufit */ +#define /*0x0F4*/ oSnufitRecoil OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oSnufitScale OBJECT_FIELD_F32(0x1C) +#define /*0x100*/ oSnufitCircularPeriod OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oSnufitBodyScalePeriod OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oSnufitBodyBaseScale OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oSnufitBullets OBJECT_FIELD_S32(0x21) +#define /*0x1AC*/ oSnufitXOffset OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oSnufitYOffset OBJECT_FIELD_S16(0x49, + 1) +#define /*0x1B0*/ oSnufitZOffset OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oSnufitBodyScale OBJECT_FIELD_S16(0x4A, + 1) + +/* Spindel */ +#define /*0x0F4*/ oSpindelUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oSpindelUnkF8 OBJECT_FIELD_S32(0x1C) + +/* Spinning Heart */ +#define /*0x0F4*/ oSpinningHeartTotalSpin OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oSpinningHeartPlayedSound OBJECT_FIELD_S32(0x1C) + +/* Spiny */ +#define /*0x0F4*/ oSpinyTimeUntilTurn OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oSpinyTargetYaw OBJECT_FIELD_S32(0x1C) +#define /*0x100*/ oSpinyTurningAwayFromWall OBJECT_FIELD_S32(0x1E) + +/* Sound Effect */ +#define /*0x0F4*/ oSoundEffectUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Star Spawn */ +#define /*0x0F4*/ oStarSpawnDisFromHome OBJECT_FIELD_F32(0x1B) +#define /*0x0FC*/ oStarSpawnUnkFC OBJECT_FIELD_F32(0x1D) + +/* Hidden Star */ +// Secrets/Red Coins +#define /*0x0F4*/ oHiddenStarTriggerCounter OBJECT_FIELD_S32(0x1B) + +// Overall very difficult to determine usage, mostly stubbed code. +/* Sparkle Spawn Star */ +#define /*0x1B0*/ oSparkleSpawnUnk1B0 OBJECT_FIELD_S32(0x4A) + +/* Sealed Door Star */ +#define /*0x108*/ oUnlockDoorStarState OBJECT_FIELD_U32(0x20) +#define /*0x10C*/ oUnlockDoorStarTimer OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oUnlockDoorStarYawVel OBJECT_FIELD_S32(0x22) + +/* Celebration Star */ +#define /*0x0F4*/ oCelebStarUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x108*/ oCelebStarDiameterOfRotation OBJECT_FIELD_S32(0x20) + +/* Star Selector */ +#define /*0x0F4*/ oStarSelectorType OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oStarSelectorTimer OBJECT_FIELD_S32(0x1C) +#define /*0x108*/ oStarSelectorSize OBJECT_FIELD_F32(0x20) + +/* Sushi Shark */ +#define /*0x0F4*/ oSushiSharkUnkF4 OBJECT_FIELD_S32(0x1B) // angle? + +/* Swing Platform */ +#define /*0x0F4*/ oSwingPlatformAngle OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oSwingPlatformSpeed OBJECT_FIELD_F32(0x1C) + +/* Swoop */ +#define /*0x0F4*/ oSwoopBonkCountdown OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oSwoopTargetPitch OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oSwoopTargetYaw OBJECT_FIELD_S32(0x1D) + +/* Thwomp */ +#define /*0x0F4*/ oThwompRandomTimer OBJECT_FIELD_S32(0x1B) + +/* Tilting Platform */ +#define /*0x0F4*/ oTiltingPyramidNormalX OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oTiltingPyramidNormalY OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oTiltingPyramidNormalZ OBJECT_FIELD_F32(0x1D) +#define /*0x10C*/ oTiltingPyramidMarioOnPlatform OBJECT_FIELD_S32(0x21) + +/* Toad Message */ +#define /*0x108*/ oToadMessageDialogId OBJECT_FIELD_U32(0x20) +#define /*0x10C*/ oToadMessageRecentlyTalked OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oToadMessageState OBJECT_FIELD_S32(0x22) + +/* Tox Box */ +#define /*0x1AC*/ oToxBoxMovementPattern OBJECT_FIELD_VPTR(0x49) +#define /*0x1B0*/ oToxBoxMovementStep OBJECT_FIELD_S32(0x4A) + +/* TTC Rotating Solid */ +#define /*0x0F4*/ oTTCRotatingSolidNumTurns OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTTCRotatingSolidNumSides OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTTCRotatingSolidRotationDelay OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oTTCRotatingSolidVelY OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oTTCRotatingSolidSoundTimer OBJECT_FIELD_S32(0x1F) + +/* TTC Pendulum */ +#define /*0x0F4*/ oTTCPendulumAccelDir OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oTTCPendulumAngle OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oTTCPendulumAngleVel OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oTTCPendulumAngleAccel OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oTTCPendulumDelay OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oTTCPendulumSoundTimer OBJECT_FIELD_S32(0x20) + +/* TTC Treadmill */ +#define /*0x0F4*/ oTTCTreadmillBigSurface OBJECT_FIELD_S16P(0x1B) +#define /*0x0F8*/ oTTCTreadmillSmallSurface OBJECT_FIELD_S16P(0x1C) +#define /*0x0FC*/ oTTCTreadmillSpeed OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oTTCTreadmillTargetSpeed OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oTTCTreadmillTimeUntilSwitch OBJECT_FIELD_S32(0x1F) + +/* TTC Moving Bar */ +#define /*0x0F4*/ oTTCMovingBarDelay OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTTCMovingBarStoppedTimer OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTTCMovingBarOffset OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oTTCMovingBarSpeed OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oTTCMovingBarStartOffset OBJECT_FIELD_F32(0x1F) + +/* TTC Cog */ +#define /*0x0F4*/ oTTCCogDir OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oTTCCogSpeed OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oTTCCogTargetVel OBJECT_FIELD_F32(0x1D) + +/* TTC Pit Block */ +#define /*0x0F4*/ oTTCPitBlockPeakY OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oTTCPitBlockDir OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTTCPitBlockWaitTime OBJECT_FIELD_S32(0x1D) + +/* TTC Elevator */ +#define /*0x0F4*/ oTTCElevatorDir OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oTTCElevatorPeakY OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oTTCElevatorMoveTime OBJECT_FIELD_S32(0x1D) + +/* TTC 2D Rotator */ +#define /*0x0F4*/ oTTC2DRotatorMinTimeUntilNextTurn OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTTC2DRotatorTargetYaw OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTTC2DRotatorIncrement OBJECT_FIELD_S32(0x1D) +#define /*0x104*/ oTTC2DRotatorRandomDirTimer OBJECT_FIELD_S32(0x1F) +#define /*0x108*/ oTTC2DRotatorSpeed OBJECT_FIELD_S32(0x20) + +/* TTC Spinner */ +#define /*0x0F4*/ oTTCSpinnerDir OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTTCChangeDirTimer OBJECT_FIELD_S32(0x1C) + +/* Beta Trampoline */ +#define /*0x110*/ oBetaTrampolineMarioOnTrampoline OBJECT_FIELD_S32(0x22) + +/* Treasure Chest */ +#define /*0x0F4*/ oTreasureChestUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTreasureChestUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTreasureChestUnkFC OBJECT_FIELD_S32(0x1D) + +/* Tree Snow Or Leaf */ +#define /*0x0F4*/ oTreeSnowOrLeafUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTreeSnowOrLeafUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTreeSnowOrLeafUnkFC OBJECT_FIELD_S32(0x1D) + +/* Tumbling Bridge */ +#define /*0x0F4*/ oTumblingBridgeUnkF4 OBJECT_FIELD_S32(0x1B) + +/* Tweester */ +#define /*0x0F4*/ oTweesterScaleTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTweesterUnused OBJECT_FIELD_S32(0x1C) + +/* Ukiki */ +#define /*0x0F4*/ oUkikiTauntCounter OBJECT_FIELD_S16(0x1B, 0) +#define /*0x0F6*/ oUkikiTauntsToBeDone OBJECT_FIELD_S16(0x1B, 1) +// 0x1D-0x21 reserved for pathing +#define /*0x110*/ oUkikiChaseFleeRange OBJECT_FIELD_F32(0x22) +#define /*0x1AC*/ oUkikiTextState OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oUkikiTextboxTimer OBJECT_FIELD_S16(0x49, 1) +#define /*0x1B0*/ oUkikiCageSpinTimer OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oUkikiHasCap OBJECT_FIELD_S16(0x4A, 1) + +/* Ukiki Cage*/ +#define /*0x088*/ oUkikiCageNextAction OBJECT_FIELD_S32(0x00) + +/* Unagi */ +#define /*0x0F4*/ oUnagiUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oUnagiUnkF8 OBJECT_FIELD_F32(0x1C) +// 0x1D-0x21 reserved for pathing +#define /*0x110*/ oUnagiUnk110 OBJECT_FIELD_F32(0x22) +#define /*0x1AC*/ oUnagiUnk1AC OBJECT_FIELD_F32(0x49) +#define /*0x1B0*/ oUnagiUnk1B0 OBJECT_FIELD_S16(0x4A, 0) +#define /*0x1B2*/ oUnagiUnk1B2 OBJECT_FIELD_S16(0x4A, + 1) + +/* Water Bomb */ +#define /*0x0F8*/ oWaterBombVerticalStretch OBJECT_FIELD_F32(0x1C) +#define /*0x0FC*/ oWaterBombStretchSpeed OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oWaterBombOnGround OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oWaterBombNumBounces OBJECT_FIELD_F32(0x1F) + +/* Water Bomb Spawner */ +#define /*0x0F4*/ oWaterBombSpawnerBombActive OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWaterBombSpawnerTimeToSpawn OBJECT_FIELD_S32(0x1C) + +/* Water Bomb Cannon */ +#define /*0x0F4*/ oWaterCannonUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWaterCannonUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oWaterCannonUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oWaterCannonUnk100 OBJECT_FIELD_S32(0x1E) + +/* Cannon Barrel Bubbles */ +#define /*0x0F4*/ oCannonBarrelBubblesUnkF4 OBJECT_FIELD_F32(0x1B) + +/* Water Level Pillar */ +#define /*0x0F8*/ oWaterLevelPillarDrained OBJECT_FIELD_S32(0x1C) + +/* Water Level Trigger */ +#define /*0x0F4*/ oWaterLevelTriggerUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWaterLevelTriggerTargetWaterLevel OBJECT_FIELD_S32(0x1C) + +/* Water Objects */ +#define /*0x0F4*/ oWaterObjUnkF4 OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWaterObjUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oWaterObjUnkFC OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oWaterObjUnk100 OBJECT_FIELD_S32(0x1E) + +/* Water Ring (both variants) */ +#define /*0x0F4*/ oWaterRingScalePhaseX OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWaterRingScalePhaseY OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oWaterRingScalePhaseZ OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oWaterRingNormalX OBJECT_FIELD_F32(0x1E) +#define /*0x104*/ oWaterRingNormalY OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oWaterRingNormalZ OBJECT_FIELD_F32(0x20) +#define /*0x10C*/ oWaterRingMarioDistInFront OBJECT_FIELD_F32(0x21) +#define /*0x110*/ oWaterRingIndex OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oWaterRingAvgScale OBJECT_FIELD_F32(0x49) + +/* Water Ring Spawner (Jet Stream Ring Spawner and Manta Ray) */ +#define /*0x1AC*/ oWaterRingSpawnerRingsCollected OBJECT_FIELD_S32(0x49) + +/* Water Ring Manager (Jet Stream Ring Spawner and Manta Ray Ring Manager) */ +#define /*0x0F4*/ oWaterRingMgrNextRingIndex OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWaterRingMgrLastRingCollected OBJECT_FIELD_S32(0x1C) + +/* Wave Trail */ +#define /*0x0F8*/ oWaveTrailSize OBJECT_FIELD_F32(0x1C) + +/* Whirlpool */ +#define /*0x0F4*/ oWhirlpoolInitFacePitch OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWhirlpoolInitFaceRoll OBJECT_FIELD_S32(0x1C) + +/* White Puff Explode */ +#define /*0x0F4*/ oWhitePuffUnkF4 OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oWhitePuffUnkF8 OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oWhitePuffUnkFC OBJECT_FIELD_S32(0x1D) + +/* White Wind Particle */ +#define /*0x0F4*/ oStrongWindParticlePenguinObj OBJECT_FIELD_OBJ(0x1B) + +/* Whomp */ +#define /*0x0F8*/ oWhompShakeVal OBJECT_FIELD_S32(0x1C) + +/* Wiggler */ +#define /*0x0F4*/ oWigglerFallThroughFloorsHeight OBJECT_FIELD_F32(0x1B) +#define /*0x0F8*/ oWigglerSegments OBJECT_FIELD_CHAIN_SEGMENT(0x1C) +#define /*0x0FC*/ oWigglerWalkAnimSpeed OBJECT_FIELD_F32(0x1D) +#define /*0x104*/ oWigglerSquishSpeed OBJECT_FIELD_F32(0x1F) +#define /*0x108*/ oWigglerTimeUntilRandomTurn OBJECT_FIELD_S32(0x20) +#define /*0x10C*/ oWigglerTargetYaw OBJECT_FIELD_S32(0x21) +#define /*0x110*/ oWigglerWalkAwayFromWallTimer OBJECT_FIELD_S32(0x22) +#define /*0x1AC*/ oWigglerUnused OBJECT_FIELD_S16(0x49, 0) +#define /*0x1AE*/ oWigglerTextStatus OBJECT_FIELD_S16(0x49, + 1) + +/* Lll Wood Piece */ +#define /*0x0F4*/ oLllWoodPieceOscillationTimer OBJECT_FIELD_S32(0x1B) + +/* Wooden Post */ +#define /*0x0F4*/ oWoodenPostTotalMarioAngle OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oWoodenPostPrevAngleToMario OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oWoodenPostSpeedY OBJECT_FIELD_F32(0x1D) +#define /*0x100*/ oWoodenPostMarioPounding OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oWoodenPostOffsetY OBJECT_FIELD_F32(0x1F) + +/* Yoshi */ +#define /*0x0F4*/ oYoshiBlinkTimer OBJECT_FIELD_S32(0x1B) +#define /*0x0FC*/ oYoshiChosenHome OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oYoshiTargetYaw OBJECT_FIELD_S32(0x1E) + +#endif // OBJECT_FIELDS_H diff --git a/src/include/platform_info.h b/src/include/platform_info.h new file mode 100644 index 0000000..310aa4d --- /dev/null +++ b/src/include/platform_info.h @@ -0,0 +1,15 @@ +#ifndef PLATFORM_INFO_H +#define PLATFORM_INFO_H + +#ifdef TARGET_N64 +#define IS_64_BIT 0 +#define IS_BIG_ENDIAN 1 +#else +#include +#define IS_64_BIT (UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFU) +#define IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#endif + +#define DOUBLE_SIZE_ON_64_BIT(size) ((size) * (sizeof(void *) / 4)) + +#endif // PLATFORM_INFO_H diff --git a/src/include/seq_ids.h b/src/include/seq_ids.h new file mode 100644 index 0000000..4e52979 --- /dev/null +++ b/src/include/seq_ids.h @@ -0,0 +1,45 @@ +#ifndef SEQ_IDS_H +#define SEQ_IDS_H + +#define SEQ_VARIATION 0x80 + +enum SeqId { + SEQ_SOUND_PLAYER, // 0x00 + SEQ_EVENT_CUTSCENE_COLLECT_STAR, // 0x01 + SEQ_MENU_TITLE_SCREEN, // 0x02 + SEQ_LEVEL_GRASS, // 0x03 + SEQ_LEVEL_INSIDE_CASTLE, // 0x04 + SEQ_LEVEL_WATER, // 0x05 + SEQ_LEVEL_HOT, // 0x06 + SEQ_LEVEL_BOSS_KOOPA, // 0x07 + SEQ_LEVEL_SNOW, // 0x08 + SEQ_LEVEL_SLIDE, // 0x09 + SEQ_LEVEL_SPOOKY, // 0x0A + SEQ_EVENT_PIRANHA_PLANT, // 0x0B + SEQ_LEVEL_UNDERGROUND, // 0x0C + SEQ_MENU_STAR_SELECT, // 0x0D + SEQ_EVENT_POWERUP, // 0x0E + SEQ_EVENT_METAL_CAP, // 0x0F + SEQ_EVENT_KOOPA_MESSAGE, // 0x10 + SEQ_LEVEL_KOOPA_ROAD, // 0x11 + SEQ_EVENT_HIGH_SCORE, // 0x12 + SEQ_EVENT_MERRY_GO_ROUND, // 0x13 + SEQ_EVENT_RACE, // 0x14 + SEQ_EVENT_CUTSCENE_STAR_SPAWN, // 0x15 + SEQ_EVENT_BOSS, // 0x16 + SEQ_EVENT_CUTSCENE_COLLECT_KEY, // 0x17 + SEQ_EVENT_ENDLESS_STAIRS, // 0x18 + SEQ_LEVEL_BOSS_KOOPA_FINAL, // 0x19 + SEQ_EVENT_CUTSCENE_CREDITS, // 0x1A + SEQ_EVENT_SOLVE_PUZZLE, // 0x1B + SEQ_EVENT_TOAD_MESSAGE, // 0x1C + SEQ_EVENT_PEACH_MESSAGE, // 0x1D + SEQ_EVENT_CUTSCENE_INTRO, // 0x1E + SEQ_EVENT_CUTSCENE_VICTORY, // 0x1F + SEQ_EVENT_CUTSCENE_ENDING, // 0x20 + SEQ_MENU_FILE_SELECT, // 0x21 + SEQ_EVENT_CUTSCENE_LAKITU, // 0x22 (not in JP) + SEQ_COUNT +}; + +#endif // SEQ_IDS_H diff --git a/src/include/sm64.h b/src/include/sm64.h new file mode 100644 index 0000000..1e4856d --- /dev/null +++ b/src/include/sm64.h @@ -0,0 +1,435 @@ +#ifndef SM64_H +#define SM64_H + +// Global header for Super Mario 64 + +#include "types.h" +// #include "config.h" +// #include "object_fields.h" +// #include "object_constants.h" +#include "audio_defines.h" +// #include "model_ids.h" +// #include "mario_animation_ids.h" +// #include "mario_geo_switch_case_ids.h" +#include "surface_terrains.h" +#include "macros.h" + +// Crash handler enhancement +#ifdef CRASH_SCREEN_INCLUDED +#define DEBUG_ASSERT(exp) do { if (!(exp)) _n64_assert(__FILE__, __LINE__, #exp, 1); } while (0) +#else +#define DEBUG_ASSERT(exp) +#endif + +// Pointer casting is technically UB, and avoiding it gets rid of endian issues +// as well as a nice side effect. +#ifdef AVOID_UB +#define GET_HIGH_U16_OF_32(var) ((u16)((var) >> 16)) +#define GET_HIGH_S16_OF_32(var) ((s16)((var) >> 16)) +#define GET_LOW_U16_OF_32(var) ((u16)((var) & 0xFFFF)) +#define GET_LOW_S16_OF_32(var) ((s16)((var) & 0xFFFF)) +#define SET_HIGH_U16_OF_32(var, x) ((var) = ((var) & 0xFFFF) | ((x) << 16)) +#define SET_HIGH_S16_OF_32(var, x) ((var) = ((var) & 0xFFFF) | ((x) << 16)) +#else +#define GET_HIGH_U16_OF_32(var) (((u16 *)&(var))[0]) +#define GET_HIGH_S16_OF_32(var) (((s16 *)&(var))[0]) +#define GET_LOW_U16_OF_32(var) (((u16 *)&(var))[1]) +#define GET_LOW_S16_OF_32(var) (((s16 *)&(var))[1]) +#define SET_HIGH_U16_OF_32(var, x) ((((u16 *)&(var))[0]) = (x)) +#define SET_HIGH_S16_OF_32(var, x) ((((s16 *)&(var))[0]) = (x)) +#endif + +// Layers +#define LAYER_FORCE 0 +#define LAYER_OPAQUE 1 +#define LAYER_OPAQUE_DECAL 2 +#define LAYER_OPAQUE_INTER 3 +#define LAYER_ALPHA 4 +#define LAYER_TRANSPARENT 5 +#define LAYER_TRANSPARENT_DECAL 6 +#define LAYER_TRANSPARENT_INTER 7 + +#define INPUT_NONZERO_ANALOG 0x0001 +#define INPUT_A_PRESSED 0x0002 +#define INPUT_OFF_FLOOR 0x0004 +#define INPUT_ABOVE_SLIDE 0x0008 +#define INPUT_FIRST_PERSON 0x0010 +#define INPUT_UNKNOWN_5 0x0020 +#define INPUT_SQUISHED 0x0040 +#define INPUT_A_DOWN 0x0080 +#define INPUT_IN_POISON_GAS 0x0100 +#define INPUT_IN_WATER 0x0200 +#define INPUT_UNKNOWN_10 0x0400 +#define INPUT_INTERACT_OBJ_GRABBABLE 0x0800 +#define INPUT_UNKNOWN_12 0x1000 +#define INPUT_B_PRESSED 0x2000 +#define INPUT_Z_DOWN 0x4000 +#define INPUT_Z_PRESSED 0x8000 + +#define GROUND_STEP_LEFT_GROUND 0 +#define GROUND_STEP_NONE 1 +#define GROUND_STEP_HIT_WALL 2 +#define GROUND_STEP_HIT_WALL_STOP_QSTEPS 2 +#define GROUND_STEP_HIT_WALL_CONTINUE_QSTEPS 3 + +#define AIR_STEP_CHECK_LEDGE_GRAB 0x00000001 +#define AIR_STEP_CHECK_HANG 0x00000002 + +#define AIR_STEP_NONE 0 +#define AIR_STEP_LANDED 1 +#define AIR_STEP_HIT_WALL 2 +#define AIR_STEP_GRABBED_LEDGE 3 +#define AIR_STEP_GRABBED_CEILING 4 +#define AIR_STEP_HIT_LAVA_WALL 6 + +#define WATER_STEP_NONE 0 +#define WATER_STEP_HIT_FLOOR 1 +#define WATER_STEP_HIT_CEILING 2 +#define WATER_STEP_CANCELLED 3 +#define WATER_STEP_HIT_WALL 4 + +#define PARTICLE_DUST /* 0x00000001 */ (1 << 0) +#define PARTICLE_VERTICAL_STAR /* 0x00000002 */ (1 << 1) +#define PARTICLE_2 /* 0x00000004 */ (1 << 2) +#define PARTICLE_SPARKLES /* 0x00000008 */ (1 << 3) +#define PARTICLE_HORIZONTAL_STAR /* 0x00000010 */ (1 << 4) +#define PARTICLE_BUBBLE /* 0x00000020 */ (1 << 5) +#define PARTICLE_WATER_SPLASH /* 0x00000040 */ (1 << 6) +#define PARTICLE_IDLE_WATER_WAVE /* 0x00000080 */ (1 << 7) +#define PARTICLE_SHALLOW_WATER_WAVE /* 0x00000100 */ (1 << 8) +#define PARTICLE_PLUNGE_BUBBLE /* 0x00000200 */ (1 << 9) +#define PARTICLE_WAVE_TRAIL /* 0x00000400 */ (1 << 10) +#define PARTICLE_FIRE /* 0x00000800 */ (1 << 11) +#define PARTICLE_SHALLOW_WATER_SPLASH /* 0x00001000 */ (1 << 12) +#define PARTICLE_LEAF /* 0x00002000 */ (1 << 13) +#define PARTICLE_SNOW /* 0x00004000 */ (1 << 14) +#define PARTICLE_DIRT /* 0x00008000 */ (1 << 15) +#define PARTICLE_MIST_CIRCLE /* 0x00010000 */ (1 << 16) +#define PARTICLE_BREATH /* 0x00020000 */ (1 << 17) +#define PARTICLE_TRIANGLE /* 0x00040000 */ (1 << 18) +#define PARTICLE_19 /* 0x00080000 */ (1 << 19) + +#define MODEL_STATE_NOISE_ALPHA 0x180 +#define MODEL_STATE_METAL 0x200 + +#define MARIO_NORMAL_CAP 0x00000001 +#define MARIO_VANISH_CAP 0x00000002 +#define MARIO_METAL_CAP 0x00000004 +#define MARIO_WING_CAP 0x00000008 +#define MARIO_CAP_ON_HEAD 0x00000010 +#define MARIO_CAP_IN_HAND 0x00000020 +#define MARIO_METAL_SHOCK 0x00000040 +#define MARIO_TELEPORTING 0x00000080 +#define MARIO_UNKNOWN_08 0x00000100 +#define MARIO_UNKNOWN_13 0x00002000 +#define MARIO_ACTION_SOUND_PLAYED 0x00010000 +#define MARIO_MARIO_SOUND_PLAYED 0x00020000 +#define MARIO_UNKNOWN_18 0x00040000 +#define MARIO_PUNCHING 0x00100000 +#define MARIO_KICKING 0x00200000 +#define MARIO_TRIPPING 0x00400000 +#define MARIO_UNKNOWN_25 0x02000000 +#define MARIO_UNKNOWN_30 0x40000000 +#define MARIO_UNKNOWN_31 0x80000000 + +#define MARIO_SPECIAL_CAPS (MARIO_VANISH_CAP | MARIO_METAL_CAP | MARIO_WING_CAP) +#define MARIO_CAPS (MARIO_NORMAL_CAP | MARIO_SPECIAL_CAPS) + +#define ACT_ID_MASK 0x000001FF + +#define ACT_GROUP_MASK 0x000001C0 +#define ACT_GROUP_STATIONARY /* 0x00000000 */ (0 << 6) +#define ACT_GROUP_MOVING /* 0x00000040 */ (1 << 6) +#define ACT_GROUP_AIRBORNE /* 0x00000080 */ (2 << 6) +#define ACT_GROUP_SUBMERGED /* 0x000000C0 */ (3 << 6) +#define ACT_GROUP_CUTSCENE /* 0x00000100 */ (4 << 6) +#define ACT_GROUP_AUTOMATIC /* 0x00000140 */ (5 << 6) +#define ACT_GROUP_OBJECT /* 0x00000180 */ (6 << 6) + +#define ACT_FLAG_STATIONARY /* 0x00000200 */ (1 << 9) +#define ACT_FLAG_MOVING /* 0x00000400 */ (1 << 10) +#define ACT_FLAG_AIR /* 0x00000800 */ (1 << 11) +#define ACT_FLAG_INTANGIBLE /* 0x00001000 */ (1 << 12) +#define ACT_FLAG_SWIMMING /* 0x00002000 */ (1 << 13) +#define ACT_FLAG_METAL_WATER /* 0x00004000 */ (1 << 14) +#define ACT_FLAG_SHORT_HITBOX /* 0x00008000 */ (1 << 15) +#define ACT_FLAG_RIDING_SHELL /* 0x00010000 */ (1 << 16) +#define ACT_FLAG_INVULNERABLE /* 0x00020000 */ (1 << 17) +#define ACT_FLAG_BUTT_OR_STOMACH_SLIDE /* 0x00040000 */ (1 << 18) +#define ACT_FLAG_DIVING /* 0x00080000 */ (1 << 19) +#define ACT_FLAG_ON_POLE /* 0x00100000 */ (1 << 20) +#define ACT_FLAG_HANGING /* 0x00200000 */ (1 << 21) +#define ACT_FLAG_IDLE /* 0x00400000 */ (1 << 22) +#define ACT_FLAG_ATTACKING /* 0x00800000 */ (1 << 23) +#define ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION /* 0x01000000 */ (1 << 24) +#define ACT_FLAG_CONTROL_JUMP_HEIGHT /* 0x02000000 */ (1 << 25) +#define ACT_FLAG_ALLOW_FIRST_PERSON /* 0x04000000 */ (1 << 26) +#define ACT_FLAG_PAUSE_EXIT /* 0x08000000 */ (1 << 27) +#define ACT_FLAG_SWIMMING_OR_FLYING /* 0x10000000 */ (1 << 28) +#define ACT_FLAG_WATER_OR_TEXT /* 0x20000000 */ (1 << 29) +#define ACT_FLAG_THROWING /* 0x80000000 */ (1 << 31) + +#define ACT_UNINITIALIZED 0x00000000 // (0x000) + +// group 0x000: stationary actions +#define ACT_IDLE 0x0C400201 // (0x001 | ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_START_SLEEPING 0x0C400202 // (0x002 | ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_SLEEPING 0x0C000203 // (0x003 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_WAKING_UP 0x0C000204 // (0x004 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_PANTING 0x0C400205 // (0x005 | ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_PANTING_UNUSED 0x08000206 // (0x006 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_IDLE 0x08000207 // (0x007 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_HEAVY_IDLE 0x08000208 // (0x008 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_STANDING_AGAINST_WALL 0x0C400209 // (0x009 | ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_COUGHING 0x0C40020A // (0x00A | ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_SHIVERING 0x0C40020B // (0x00B | ACT_FLAG_STATIONARY | ACT_FLAG_IDLE | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_IN_QUICKSAND 0x0002020D // (0x00D | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_UNKNOWN_0002020E 0x0002020E // (0x00E | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_CROUCHING 0x0C008220 // (0x020 | ACT_FLAG_STATIONARY | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_START_CROUCHING 0x0C008221 // (0x021 | ACT_FLAG_STATIONARY | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_STOP_CROUCHING 0x0C008222 // (0x022 | ACT_FLAG_STATIONARY | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_START_CRAWLING 0x0C008223 // (0x023 | ACT_FLAG_STATIONARY | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_STOP_CRAWLING 0x0C008224 // (0x024 | ACT_FLAG_STATIONARY | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_SLIDE_KICK_SLIDE_STOP 0x08000225 // (0x025 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_SHOCKWAVE_BOUNCE 0x00020226 // (0x026 | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_FIRST_PERSON 0x0C000227 // (0x027 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_BACKFLIP_LAND_STOP 0x0800022F // (0x02F | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_JUMP_LAND_STOP 0x0C000230 // (0x030 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_DOUBLE_JUMP_LAND_STOP 0x0C000231 // (0x031 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_FREEFALL_LAND_STOP 0x0C000232 // (0x032 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_SIDE_FLIP_LAND_STOP 0x0C000233 // (0x033 | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_JUMP_LAND_STOP 0x08000234 // (0x034 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_FREEFALL_LAND_STOP 0x08000235 // (0x035 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_AIR_THROW_LAND 0x80000A36 // (0x036 | ACT_FLAG_STATIONARY | ACT_FLAG_AIR | ACT_FLAG_THROWING) +#define ACT_TWIRL_LAND 0x18800238 // (0x038 | ACT_FLAG_STATIONARY | ACT_FLAG_ATTACKING | ACT_FLAG_PAUSE_EXIT | ACT_FLAG_SWIMMING_OR_FLYING) +#define ACT_LAVA_BOOST_LAND 0x08000239 // (0x039 | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_TRIPLE_JUMP_LAND_STOP 0x0800023A // (0x03A | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_LONG_JUMP_LAND_STOP 0x0800023B // (0x03B | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_GROUND_POUND_LAND 0x0080023C // (0x03C | ACT_FLAG_STATIONARY | ACT_FLAG_ATTACKING) +#define ACT_BRAKING_STOP 0x0C00023D // (0x03D | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_BUTT_SLIDE_STOP 0x0C00023E // (0x03E | ACT_FLAG_STATIONARY | ACT_FLAG_ALLOW_FIRST_PERSON | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_BUTT_SLIDE_STOP 0x0800043F // (0x03F | ACT_FLAG_MOVING | ACT_FLAG_PAUSE_EXIT) + +// group 0x040: moving (ground) actions +#define ACT_WALKING 0x04000440 // (0x040 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_HOLD_WALKING 0x00000442 // (0x042 | ACT_FLAG_MOVING) +#define ACT_TURNING_AROUND 0x00000443 // (0x043 | ACT_FLAG_MOVING) +#define ACT_FINISH_TURNING_AROUND 0x00000444 // (0x044 | ACT_FLAG_MOVING) +#define ACT_BRAKING 0x04000445 // (0x045 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_RIDING_SHELL_GROUND 0x20810446 // (0x046 | ACT_FLAG_MOVING | ACT_FLAG_RIDING_SHELL | ACT_FLAG_ATTACKING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_HOLD_HEAVY_WALKING 0x00000447 // (0x047 | ACT_FLAG_MOVING) +#define ACT_CRAWLING 0x04008448 // (0x048 | ACT_FLAG_MOVING | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_BURNING_GROUND 0x00020449 // (0x049 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_DECELERATING 0x0400044A // (0x04A | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_HOLD_DECELERATING 0x0000044B // (0x04B | ACT_FLAG_MOVING) +#define ACT_BEGIN_SLIDING 0x00000050 // (0x050) +#define ACT_HOLD_BEGIN_SLIDING 0x00000051 // (0x051) +#define ACT_BUTT_SLIDE 0x00840452 // (0x052 | ACT_FLAG_MOVING | ACT_FLAG_BUTT_OR_STOMACH_SLIDE | ACT_FLAG_ATTACKING) +#define ACT_STOMACH_SLIDE 0x008C0453 // (0x053 | ACT_FLAG_MOVING | ACT_FLAG_BUTT_OR_STOMACH_SLIDE | ACT_FLAG_DIVING | ACT_FLAG_ATTACKING) +#define ACT_HOLD_BUTT_SLIDE 0x00840454 // (0x054 | ACT_FLAG_MOVING | ACT_FLAG_BUTT_OR_STOMACH_SLIDE | ACT_FLAG_ATTACKING) +#define ACT_HOLD_STOMACH_SLIDE 0x008C0455 // (0x055 | ACT_FLAG_MOVING | ACT_FLAG_BUTT_OR_STOMACH_SLIDE | ACT_FLAG_DIVING | ACT_FLAG_ATTACKING) +#define ACT_DIVE_SLIDE 0x00880456 // (0x056 | ACT_FLAG_MOVING | ACT_FLAG_DIVING | ACT_FLAG_ATTACKING) +#define ACT_MOVE_PUNCHING 0x00800457 // (0x057 | ACT_FLAG_MOVING | ACT_FLAG_ATTACKING) +#define ACT_CROUCH_SLIDE 0x04808459 // (0x059 | ACT_FLAG_MOVING | ACT_FLAG_SHORT_HITBOX | ACT_FLAG_ATTACKING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_SLIDE_KICK_SLIDE 0x0080045A // (0x05A | ACT_FLAG_MOVING | ACT_FLAG_ATTACKING) +#define ACT_HARD_BACKWARD_GROUND_KB 0x00020460 // (0x060 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_HARD_FORWARD_GROUND_KB 0x00020461 // (0x061 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_BACKWARD_GROUND_KB 0x00020462 // (0x062 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_FORWARD_GROUND_KB 0x00020463 // (0x063 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_SOFT_BACKWARD_GROUND_KB 0x00020464 // (0x064 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_SOFT_FORWARD_GROUND_KB 0x00020465 // (0x065 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_GROUND_BONK 0x00020466 // (0x066 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_DEATH_EXIT_LAND 0x00020467 // (0x067 | ACT_FLAG_MOVING | ACT_FLAG_INVULNERABLE) +#define ACT_JUMP_LAND 0x04000470 // (0x070 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_FREEFALL_LAND 0x04000471 // (0x071 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_DOUBLE_JUMP_LAND 0x04000472 // (0x072 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_SIDE_FLIP_LAND 0x04000473 // (0x073 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_HOLD_JUMP_LAND 0x00000474 // (0x074 | ACT_FLAG_MOVING) +#define ACT_HOLD_FREEFALL_LAND 0x00000475 // (0x075 | ACT_FLAG_MOVING) +#define ACT_QUICKSAND_JUMP_LAND 0x00000476 // (0x076 | ACT_FLAG_MOVING) +#define ACT_HOLD_QUICKSAND_JUMP_LAND 0x00000477 // (0x077 | ACT_FLAG_MOVING) +#define ACT_TRIPLE_JUMP_LAND 0x04000478 // (0x078 | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_LONG_JUMP_LAND 0x00000479 // (0x079 | ACT_FLAG_MOVING) +#define ACT_BACKFLIP_LAND 0x0400047A // (0x07A | ACT_FLAG_MOVING | ACT_FLAG_ALLOW_FIRST_PERSON) + +// group 0x080: airborne actions +#define ACT_JUMP 0x03000880 // (0x080 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_DOUBLE_JUMP 0x03000881 // (0x081 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_TRIPLE_JUMP 0x01000882 // (0x082 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_BACKFLIP 0x01000883 // (0x083 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_STEEP_JUMP 0x03000885 // (0x085 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_WALL_KICK_AIR 0x03000886 // (0x086 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_SIDE_FLIP 0x01000887 // (0x087 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_LONG_JUMP 0x03000888 // (0x088 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_WATER_JUMP 0x01000889 // (0x089 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_DIVE 0x0188088A // (0x08A | ACT_FLAG_AIR | ACT_FLAG_DIVING | ACT_FLAG_ATTACKING | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_FREEFALL 0x0100088C // (0x08C | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_TOP_OF_POLE_JUMP 0x0300088D // (0x08D | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_BUTT_SLIDE_AIR 0x0300088E // (0x08E | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_FLYING_TRIPLE_JUMP 0x03000894 // (0x094 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_SHOT_FROM_CANNON 0x00880898 // (0x098 | ACT_FLAG_AIR | ACT_FLAG_DIVING | ACT_FLAG_ATTACKING) +#define ACT_FLYING 0x10880899 // (0x099 | ACT_FLAG_AIR | ACT_FLAG_DIVING | ACT_FLAG_ATTACKING | ACT_FLAG_SWIMMING_OR_FLYING) +#define ACT_RIDING_SHELL_JUMP 0x0281089A // (0x09A | ACT_FLAG_AIR | ACT_FLAG_RIDING_SHELL | ACT_FLAG_ATTACKING | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_RIDING_SHELL_FALL 0x0081089B // (0x09B | ACT_FLAG_AIR | ACT_FLAG_RIDING_SHELL | ACT_FLAG_ATTACKING) +#define ACT_VERTICAL_WIND 0x1008089C // (0x09C | ACT_FLAG_AIR | ACT_FLAG_DIVING | ACT_FLAG_SWIMMING_OR_FLYING) +#define ACT_HOLD_JUMP 0x030008A0 // (0x0A0 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_HOLD_FREEFALL 0x010008A1 // (0x0A1 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_HOLD_BUTT_SLIDE_AIR 0x010008A2 // (0x0A2 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_HOLD_WATER_JUMP 0x010008A3 // (0x0A3 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_TWIRLING 0x108008A4 // (0x0A4 | ACT_FLAG_AIR | ACT_FLAG_ATTACKING | ACT_FLAG_SWIMMING_OR_FLYING) +#define ACT_FORWARD_ROLLOUT 0x010008A6 // (0x0A6 | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_AIR_HIT_WALL 0x000008A7 // (0x0A7 | ACT_FLAG_AIR) +#define ACT_RIDING_HOOT 0x000004A8 // (0x0A8 | ACT_FLAG_MOVING) +#define ACT_GROUND_POUND 0x008008A9 // (0x0A9 | ACT_FLAG_AIR | ACT_FLAG_ATTACKING) +#define ACT_SLIDE_KICK 0x018008AA // (0x0AA | ACT_FLAG_AIR | ACT_FLAG_ATTACKING | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_AIR_THROW 0x830008AB // (0x0AB | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT | ACT_FLAG_THROWING) +#define ACT_JUMP_KICK 0x018008AC // (0x0AC | ACT_FLAG_AIR | ACT_FLAG_ATTACKING | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_BACKWARD_ROLLOUT 0x010008AD // (0x0AD | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_CRAZY_BOX_BOUNCE 0x000008AE // (0x0AE | ACT_FLAG_AIR) +#define ACT_SPECIAL_TRIPLE_JUMP 0x030008AF // (0x0AF | ACT_FLAG_AIR | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION | ACT_FLAG_CONTROL_JUMP_HEIGHT) +#define ACT_BACKWARD_AIR_KB 0x010208B0 // (0x0B0 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_FORWARD_AIR_KB 0x010208B1 // (0x0B1 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_HARD_FORWARD_AIR_KB 0x010208B2 // (0x0B2 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_HARD_BACKWARD_AIR_KB 0x010208B3 // (0x0B3 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_BURNING_JUMP 0x010208B4 // (0x0B4 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_BURNING_FALL 0x010208B5 // (0x0B5 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_SOFT_BONK 0x010208B6 // (0x0B6 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_LAVA_BOOST 0x010208B7 // (0x0B7 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_GETTING_BLOWN 0x010208B8 // (0x0B8 | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_THROWN_FORWARD 0x010208BD // (0x0BD | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) +#define ACT_THROWN_BACKWARD 0x010208BE // (0x0BE | ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ALLOW_VERTICAL_WIND_ACTION) + +// group 0x0C0: submerged actions +#define ACT_WATER_IDLE 0x380022C0 // (0x0C0 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_PAUSE_EXIT | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_HOLD_WATER_IDLE 0x380022C1 // (0x0C1 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_PAUSE_EXIT | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_ACTION_END 0x300022C2 // (0x0C2 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_HOLD_WATER_ACTION_END 0x300022C3 // (0x0C3 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_DROWNING 0x300032C4 // (0x0C4 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_BACKWARD_WATER_KB 0x300222C5 // (0x0C5 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_INVULNERABLE | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_FORWARD_WATER_KB 0x300222C6 // (0x0C6 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_INVULNERABLE | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_DEATH 0x300032C7 // (0x0C7 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_SHOCKED 0x300222C8 // (0x0C8 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_INVULNERABLE | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_BREASTSTROKE 0x300024D0 // (0x0D0 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_SWIMMING_END 0x300024D1 // (0x0D1 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_FLUTTER_KICK 0x300024D2 // (0x0D2 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_HOLD_BREASTSTROKE 0x300024D3 // (0x0D3 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_HOLD_SWIMMING_END 0x300024D4 // (0x0D4 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_HOLD_FLUTTER_KICK 0x300024D5 // (0x0D5 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_SHELL_SWIMMING 0x300024D6 // (0x0D6 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_THROW 0x300024E0 // (0x0E0 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_PUNCH 0x300024E1 // (0x0E1 | ACT_FLAG_MOVING | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_WATER_PLUNGE 0x300022E2 // (0x0E2 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_CAUGHT_IN_WHIRLPOOL 0x300222E3 // (0x0E3 | ACT_FLAG_STATIONARY | ACT_FLAG_SWIMMING | ACT_FLAG_INVULNERABLE | ACT_FLAG_SWIMMING_OR_FLYING | ACT_FLAG_WATER_OR_TEXT) +#define ACT_METAL_WATER_STANDING 0x080042F0 // (0x0F0 | ACT_FLAG_STATIONARY | ACT_FLAG_METAL_WATER | ACT_FLAG_PAUSE_EXIT) +#define ACT_HOLD_METAL_WATER_STANDING 0x080042F1 // (0x0F1 | ACT_FLAG_STATIONARY | ACT_FLAG_METAL_WATER | ACT_FLAG_PAUSE_EXIT) +#define ACT_METAL_WATER_WALKING 0x000044F2 // (0x0F2 | ACT_FLAG_MOVING | ACT_FLAG_METAL_WATER) +#define ACT_HOLD_METAL_WATER_WALKING 0x000044F3 // (0x0F3 | ACT_FLAG_MOVING | ACT_FLAG_METAL_WATER) +#define ACT_METAL_WATER_FALLING 0x000042F4 // (0x0F4 | ACT_FLAG_STATIONARY | ACT_FLAG_METAL_WATER) +#define ACT_HOLD_METAL_WATER_FALLING 0x000042F5 // (0x0F5 | ACT_FLAG_STATIONARY | ACT_FLAG_METAL_WATER) +#define ACT_METAL_WATER_FALL_LAND 0x000042F6 // (0x0F6 | ACT_FLAG_STATIONARY | ACT_FLAG_METAL_WATER) +#define ACT_HOLD_METAL_WATER_FALL_LAND 0x000042F7 // (0x0F7 | ACT_FLAG_STATIONARY | ACT_FLAG_METAL_WATER) +#define ACT_METAL_WATER_JUMP 0x000044F8 // (0x0F8 | ACT_FLAG_MOVING | ACT_FLAG_METAL_WATER) +#define ACT_HOLD_METAL_WATER_JUMP 0x000044F9 // (0x0F9 | ACT_FLAG_MOVING | ACT_FLAG_METAL_WATER) +#define ACT_METAL_WATER_JUMP_LAND 0x000044FA // (0x0FA | ACT_FLAG_MOVING | ACT_FLAG_METAL_WATER) +#define ACT_HOLD_METAL_WATER_JUMP_LAND 0x000044FB // (0x0FB | ACT_FLAG_MOVING | ACT_FLAG_METAL_WATER) + +// group 0x100: cutscene actions +#define ACT_DISAPPEARED 0x00001300 // (0x100 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_INTRO_CUTSCENE 0x04001301 // (0x101 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_ALLOW_FIRST_PERSON) +#define ACT_STAR_DANCE_EXIT 0x00001302 // (0x102 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_STAR_DANCE_WATER 0x00001303 // (0x103 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_FALL_AFTER_STAR_GRAB 0x00001904 // (0x104 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_READING_AUTOMATIC_DIALOG 0x20001305 // (0x105 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_WATER_OR_TEXT) +#define ACT_READING_NPC_DIALOG 0x20001306 // (0x106 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_WATER_OR_TEXT) +#define ACT_STAR_DANCE_NO_EXIT 0x00001307 // (0x107 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_READING_SIGN 0x00001308 // (0x108 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_JUMBO_STAR_CUTSCENE 0x00001909 // (0x109 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_WAITING_FOR_DIALOG 0x0000130A // (0x10A | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_DEBUG_FREE_MOVE 0x0000130F // (0x10F | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_STANDING_DEATH 0x00021311 // (0x111 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_QUICKSAND_DEATH 0x00021312 // (0x112 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_ELECTROCUTION 0x00021313 // (0x113 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_SUFFOCATION 0x00021314 // (0x114 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_DEATH_ON_STOMACH 0x00021315 // (0x115 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_DEATH_ON_BACK 0x00021316 // (0x116 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_EATEN_BY_BUBBA 0x00021317 // (0x117 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE | ACT_FLAG_INVULNERABLE) +#define ACT_END_PEACH_CUTSCENE 0x00001918 // (0x118 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_CREDITS_CUTSCENE 0x00001319 // (0x119 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_END_WAVING_CUTSCENE 0x0000131A // (0x11A | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_PULLING_DOOR 0x00001320 // (0x120 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_PUSHING_DOOR 0x00001321 // (0x121 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_WARP_DOOR_SPAWN 0x00001322 // (0x122 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_EMERGE_FROM_PIPE 0x00001923 // (0x123 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_SPAWN_SPIN_AIRBORNE 0x00001924 // (0x124 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_SPAWN_SPIN_LANDING 0x00001325 // (0x125 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_EXIT_AIRBORNE 0x00001926 // (0x126 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_EXIT_LAND_SAVE_DIALOG 0x00001327 // (0x127 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_DEATH_EXIT 0x00001928 // (0x128 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_UNUSED_DEATH_EXIT 0x00001929 // (0x129 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_FALLING_DEATH_EXIT 0x0000192A // (0x12A | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_SPECIAL_EXIT_AIRBORNE 0x0000192B // (0x12B | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_SPECIAL_DEATH_EXIT 0x0000192C // (0x12C | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_FALLING_EXIT_AIRBORNE 0x0000192D // (0x12D | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_UNLOCKING_KEY_DOOR 0x0000132E // (0x12E | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_UNLOCKING_STAR_DOOR 0x0000132F // (0x12F | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_ENTERING_STAR_DOOR 0x00001331 // (0x131 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_SPAWN_NO_SPIN_AIRBORNE 0x00001932 // (0x132 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_SPAWN_NO_SPIN_LANDING 0x00001333 // (0x133 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_BBH_ENTER_JUMP 0x00001934 // (0x134 | ACT_FLAG_AIR | ACT_FLAG_INTANGIBLE) +#define ACT_BBH_ENTER_SPIN 0x00001535 // (0x135 | ACT_FLAG_MOVING | ACT_FLAG_INTANGIBLE) +#define ACT_TELEPORT_FADE_OUT 0x00001336 // (0x136 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_TELEPORT_FADE_IN 0x00001337 // (0x137 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_SHOCKED 0x00020338 // (0x138 | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_SQUISHED 0x00020339 // (0x139 | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_HEAD_STUCK_IN_GROUND 0x0002033A // (0x13A | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_BUTT_STUCK_IN_GROUND 0x0002033B // (0x13B | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_FEET_STUCK_IN_GROUND 0x0002033C // (0x13C | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_PUTTING_ON_CAP 0x0000133D // (0x13D | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) + +// group 0x140: "automatic" actions +#define ACT_HOLDING_POLE 0x08100340 // (0x140 | ACT_FLAG_STATIONARY | ACT_FLAG_ON_POLE | ACT_FLAG_PAUSE_EXIT) +#define ACT_GRAB_POLE_SLOW 0x00100341 // (0x141 | ACT_FLAG_STATIONARY | ACT_FLAG_ON_POLE) +#define ACT_GRAB_POLE_FAST 0x00100342 // (0x142 | ACT_FLAG_STATIONARY | ACT_FLAG_ON_POLE) +#define ACT_CLIMBING_POLE 0x00100343 // (0x143 | ACT_FLAG_STATIONARY | ACT_FLAG_ON_POLE) +#define ACT_TOP_OF_POLE_TRANSITION 0x00100344 // (0x144 | ACT_FLAG_STATIONARY | ACT_FLAG_ON_POLE) +#define ACT_TOP_OF_POLE 0x00100345 // (0x145 | ACT_FLAG_STATIONARY | ACT_FLAG_ON_POLE) +#define ACT_START_HANGING 0x08200348 // (0x148 | ACT_FLAG_STATIONARY | ACT_FLAG_HANGING | ACT_FLAG_PAUSE_EXIT) +#define ACT_HANGING 0x00200349 // (0x149 | ACT_FLAG_STATIONARY | ACT_FLAG_HANGING) +#define ACT_HANG_MOVING 0x0020054A // (0x14A | ACT_FLAG_MOVING | ACT_FLAG_HANGING) +#define ACT_LEDGE_GRAB 0x0800034B // (0x14B | ACT_FLAG_STATIONARY | ACT_FLAG_PAUSE_EXIT) +#define ACT_LEDGE_CLIMB_SLOW_1 0x0000054C // (0x14C | ACT_FLAG_MOVING) +#define ACT_LEDGE_CLIMB_SLOW_2 0x0000054D // (0x14D | ACT_FLAG_MOVING) +#define ACT_LEDGE_CLIMB_DOWN 0x0000054E // (0x14E | ACT_FLAG_MOVING) +#define ACT_LEDGE_CLIMB_FAST 0x0000054F // (0x14F | ACT_FLAG_MOVING) +#define ACT_GRABBED 0x00020370 // (0x170 | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE) +#define ACT_IN_CANNON 0x00001371 // (0x171 | ACT_FLAG_STATIONARY | ACT_FLAG_INTANGIBLE) +#define ACT_TORNADO_TWIRLING 0x10020372 // (0x172 | ACT_FLAG_STATIONARY | ACT_FLAG_INVULNERABLE | ACT_FLAG_SWIMMING_OR_FLYING) + +// group 0x180: object actions +#define ACT_PUNCHING 0x00800380 // (0x180 | ACT_FLAG_STATIONARY | ACT_FLAG_ATTACKING) +#define ACT_PICKING_UP 0x00000383 // (0x183 | ACT_FLAG_STATIONARY) +#define ACT_DIVE_PICKING_UP 0x00000385 // (0x185 | ACT_FLAG_STATIONARY) +#define ACT_STOMACH_SLIDE_STOP 0x00000386 // (0x186 | ACT_FLAG_STATIONARY) +#define ACT_PLACING_DOWN 0x00000387 // (0x187 | ACT_FLAG_STATIONARY) +#define ACT_THROWING 0x80000588 // (0x188 | ACT_FLAG_MOVING | ACT_FLAG_THROWING) +#define ACT_HEAVY_THROW 0x80000589 // (0x189 | ACT_FLAG_MOVING | ACT_FLAG_THROWING) +#define ACT_PICKING_UP_BOWSER 0x00000390 // (0x190 | ACT_FLAG_STATIONARY) +#define ACT_HOLDING_BOWSER 0x00000391 // (0x191 | ACT_FLAG_STATIONARY) +#define ACT_RELEASING_BOWSER 0x00000392 // (0x192 | ACT_FLAG_STATIONARY) + +/* + this input mask is unused by the controller, + but END_DEMO is used internally to signal + the demo to end. This button cannot + be pressed normally by a controller. +*/ +#define END_DEMO (1 << 7) + +#define VALID_BUTTONS (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | \ + U_JPAD | D_JPAD | L_JPAD | R_JPAD | \ + L_TRIG | R_TRIG | \ + U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS ) + +#define C_BUTTONS (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS ) + +#endif // SM64_H diff --git a/src/include/special_preset_names.h b/src/include/special_preset_names.h new file mode 100644 index 0000000..229d287 --- /dev/null +++ b/src/include/special_preset_names.h @@ -0,0 +1,93 @@ +#ifndef SPECIAL_PRESET_NAMES_H +#define SPECIAL_PRESET_NAMES_H + +enum SpecialPresets { + special_null_start, + special_yellow_coin, + special_yellow_coin_2, + special_unknown_3, + special_boo, + special_unknown_5, + special_lll_moving_octagonal_mesh_platform, + special_snow_ball, + special_lll_drawbridge_spawner, + special_empty_9, + special_lll_rotating_block_with_fire_bars, + special_lll_floating_wood_bridge, + special_tumbling_platform, + special_lll_rotating_hexagonal_ring, + special_lll_sinking_rectangular_platform, + special_lll_sinking_square_platforms, + special_lll_tilting_square_platform, + special_lll_bowser_puzzle, + special_mr_i, + special_small_bully, + special_big_bully, + special_empty_21, + special_empty_22, + special_empty_23, + special_empty_24, + special_empty_25, + special_moving_blue_coin, + special_jrb_chest, + special_water_ring, + special_mine, + special_empty_30, + special_empty_31, + special_butterfly, + special_bowser, + special_wf_rotating_wooden_platform, + special_small_bomp, + special_wf_sliding_platform, + special_tower_platform_group, + special_rotating_counter_clockwise, + special_wf_tumbling_bridge, + special_large_bomp, + + special_level_geo_03 = 0x65, + special_level_geo_04, + special_level_geo_05, + special_level_geo_06, + special_level_geo_07, + special_level_geo_08, + special_level_geo_09, + special_level_geo_0A, + special_level_geo_0B, + special_level_geo_0C, + special_level_geo_0D, + special_level_geo_0E, + special_level_geo_0F, + special_level_geo_10, + special_level_geo_11, + special_level_geo_12, + special_level_geo_13, + special_level_geo_14, + special_level_geo_15, + special_level_geo_16, + special_bubble_tree, + special_spiky_tree, + special_snow_tree, + special_unknown_tree, + special_palm_tree, + special_wooden_door, + special_haunted_door = special_wooden_door, + special_unknown_door, + special_metal_door, + special_hmc_door, + special_unknown2_door, + special_wooden_door_warp, + special_unknown1_door_warp, + special_metal_door_warp, + special_unknown2_door_warp, + special_unknown3_door_warp, + special_castle_door_warp, + special_castle_door, + special_0stars_door, + special_1star_door, + special_3star_door, + special_key_door, + + special_null_end = 0xFF +}; + +#endif // SPECIAL_PRESET_NAMES_H diff --git a/src/include/surface_terrains.h b/src/include/surface_terrains.h new file mode 100644 index 0000000..669a0ac --- /dev/null +++ b/src/include/surface_terrains.h @@ -0,0 +1,223 @@ +#ifndef SURFACE_TERRAINS_H +#define SURFACE_TERRAINS_H + +// Surface Types +#define SURFACE_DEFAULT 0x0000 // Environment default +#define SURFACE_BURNING 0x0001 // Lava / Frostbite (in SL), but is used mostly for Lava +#define SURFACE_0004 0x0004 // Unused, has no function and has parameters +#define SURFACE_HANGABLE 0x0005 // Ceiling that Mario can climb on +#define SURFACE_SLOW 0x0009 // Slow down Mario, unused +#define SURFACE_DEATH_PLANE 0x000A // Death floor +#define SURFACE_CLOSE_CAMERA 0x000B // Close camera +#define SURFACE_WATER 0x000D // Water, has no action, used on some waterboxes below +#define SURFACE_FLOWING_WATER 0x000E // Water (flowing), has parameters +#define SURFACE_INTANGIBLE 0x0012 // Intangible (Separates BBH mansion from merry-go-round, for room usage) +#define SURFACE_VERY_SLIPPERY 0x0013 // Very slippery, mostly used for slides +#define SURFACE_SLIPPERY 0x0014 // Slippery +#define SURFACE_NOT_SLIPPERY 0x0015 // Non-slippery, climbable +#define SURFACE_TTM_VINES 0x0016 // TTM vines, has no action defined +#define SURFACE_MGR_MUSIC 0x001A // Plays the Merry go round music, see handle_merry_go_round_music in bbh_merry_go_round.inc.c for more details +#define SURFACE_INSTANT_WARP_1B 0x001B // Instant warp to another area, used to warp between areas in WDW and the endless stairs to warp back +#define SURFACE_INSTANT_WARP_1C 0x001C // Instant warp to another area, used to warp between areas in WDW +#define SURFACE_INSTANT_WARP_1D 0x001D // Instant warp to another area, used to warp between areas in DDD, SSL and TTM +#define SURFACE_INSTANT_WARP_1E 0x001E // Instant warp to another area, used to warp between areas in DDD, SSL and TTM +#define SURFACE_SHALLOW_QUICKSAND 0x0021 // Shallow Quicksand (depth of 10 units) +#define SURFACE_DEEP_QUICKSAND 0x0022 // Quicksand (lethal, slow, depth of 160 units) +#define SURFACE_INSTANT_QUICKSAND 0x0023 // Quicksand (lethal, instant) +#define SURFACE_DEEP_MOVING_QUICKSAND 0x0024 // Moving quicksand (flowing, depth of 160 units) +#define SURFACE_SHALLOW_MOVING_QUICKSAND 0x0025 // Moving quicksand (flowing, depth of 25 units) +#define SURFACE_QUICKSAND 0x0026 // Moving quicksand (60 units) +#define SURFACE_MOVING_QUICKSAND 0x0027 // Moving quicksand (flowing, depth of 60 units) +#define SURFACE_WALL_MISC 0x0028 // Used for some walls, Cannon to adjust the camera, and some objects like Warp Pipe +#define SURFACE_NOISE_DEFAULT 0x0029 // Default floor with noise +#define SURFACE_NOISE_SLIPPERY 0x002A // Slippery floor with noise +#define SURFACE_HORIZONTAL_WIND 0x002C // Horizontal wind, has parameters +#define SURFACE_INSTANT_MOVING_QUICKSAND 0x002D // Quicksand (lethal, flowing) +#define SURFACE_ICE 0x002E // Slippery Ice, in snow levels and THI's water floor +#define SURFACE_LOOK_UP_WARP 0x002F // Look up and warp (Wing cap entrance) +#define SURFACE_HARD 0x0030 // Hard floor (Always has fall damage) +#define SURFACE_WARP 0x0032 // Surface warp +#define SURFACE_TIMER_START 0x0033 // Timer start (Peach's secret slide) +#define SURFACE_TIMER_END 0x0034 // Timer stop (Peach's secret slide) +#define SURFACE_HARD_SLIPPERY 0x0035 // Hard and slippery (Always has fall damage) +#define SURFACE_HARD_VERY_SLIPPERY 0x0036 // Hard and very slippery (Always has fall damage) +#define SURFACE_HARD_NOT_SLIPPERY 0x0037 // Hard and Non-slippery (Always has fall damage) +#define SURFACE_VERTICAL_WIND 0x0038 // Death at bottom with vertical wind +#define SURFACE_BOSS_FIGHT_CAMERA 0x0065 // Wide camera for BOB and WF bosses +#define SURFACE_CAMERA_FREE_ROAM 0x0066 // Free roam camera for THI and TTC +#define SURFACE_THI3_WALLKICK 0x0068 // Surface where there's a wall kick section in THI 3rd area, has no action defined +#define SURFACE_CAMERA_8_DIR 0x0069 // Surface that enables far camera for platforms, used in THI +#define SURFACE_CAMERA_MIDDLE 0x006E // Surface camera that returns to the middle, used on the 4 pillars of SSL +#define SURFACE_CAMERA_ROTATE_RIGHT 0x006F // Surface camera that rotates to the right (Bowser 1 & THI) +#define SURFACE_CAMERA_ROTATE_LEFT 0x0070 // Surface camera that rotates to the left (BOB & TTM) +#define SURFACE_CAMERA_BOUNDARY 0x0072 // Intangible Area, only used to restrict camera movement +#define SURFACE_NOISE_VERY_SLIPPERY_73 0x0073 // Very slippery floor with noise, unused +#define SURFACE_NOISE_VERY_SLIPPERY_74 0x0074 // Very slippery floor with noise, unused +#define SURFACE_NOISE_VERY_SLIPPERY 0x0075 // Very slippery floor with noise, used in CCM +#define SURFACE_NO_CAM_COLLISION 0x0076 // Surface with no cam collision flag +#define SURFACE_NO_CAM_COLLISION_77 0x0077 // Surface with no cam collision flag, unused +#define SURFACE_NO_CAM_COL_VERY_SLIPPERY 0x0078 // Surface with no cam collision flag, very slippery with noise (THI) +#define SURFACE_NO_CAM_COL_SLIPPERY 0x0079 // Surface with no cam collision flag, slippery with noise (CCM, PSS and TTM slides) +#define SURFACE_SWITCH 0x007A // Surface with no cam collision flag, non-slippery with noise, used by switches and Dorrie +#define SURFACE_VANISH_CAP_WALLS 0x007B // Vanish cap walls, pass through them with Vanish Cap +#define SURFACE_PAINTING_WOBBLE_A6 0x00A6 // Painting wobble (BOB Left) +#define SURFACE_PAINTING_WOBBLE_A7 0x00A7 // Painting wobble (BOB Middle) +#define SURFACE_PAINTING_WOBBLE_A8 0x00A8 // Painting wobble (BOB Right) +#define SURFACE_PAINTING_WOBBLE_A9 0x00A9 // Painting wobble (CCM Left) +#define SURFACE_PAINTING_WOBBLE_AA 0x00AA // Painting wobble (CCM Middle) +#define SURFACE_PAINTING_WOBBLE_AB 0x00AB // Painting wobble (CCM Right) +#define SURFACE_PAINTING_WOBBLE_AC 0x00AC // Painting wobble (WF Left) +#define SURFACE_PAINTING_WOBBLE_AD 0x00AD // Painting wobble (WF Middle) +#define SURFACE_PAINTING_WOBBLE_AE 0x00AE // Painting wobble (WF Right) +#define SURFACE_PAINTING_WOBBLE_AF 0x00AF // Painting wobble (JRB Left) +#define SURFACE_PAINTING_WOBBLE_B0 0x00B0 // Painting wobble (JRB Middle) +#define SURFACE_PAINTING_WOBBLE_B1 0x00B1 // Painting wobble (JRB Right) +#define SURFACE_PAINTING_WOBBLE_B2 0x00B2 // Painting wobble (LLL Left) +#define SURFACE_PAINTING_WOBBLE_B3 0x00B3 // Painting wobble (LLL Middle) +#define SURFACE_PAINTING_WOBBLE_B4 0x00B4 // Painting wobble (LLL Right) +#define SURFACE_PAINTING_WOBBLE_B5 0x00B5 // Painting wobble (SSL Left) +#define SURFACE_PAINTING_WOBBLE_B6 0x00B6 // Painting wobble (SSL Middle) +#define SURFACE_PAINTING_WOBBLE_B7 0x00B7 // Painting wobble (SSL Right) +#define SURFACE_PAINTING_WOBBLE_B8 0x00B8 // Painting wobble (Unused - Left) +#define SURFACE_PAINTING_WOBBLE_B9 0x00B9 // Painting wobble (Unused - Middle) +#define SURFACE_PAINTING_WOBBLE_BA 0x00BA // Painting wobble (Unused - Right) +#define SURFACE_PAINTING_WOBBLE_BB 0x00BB // Painting wobble (DDD - Left), makes the painting wobble if touched +#define SURFACE_PAINTING_WOBBLE_BC 0x00BC // Painting wobble (Unused, DDD - Middle) +#define SURFACE_PAINTING_WOBBLE_BD 0x00BD // Painting wobble (Unused, DDD - Right) +#define SURFACE_PAINTING_WOBBLE_BE 0x00BE // Painting wobble (WDW Left) +#define SURFACE_PAINTING_WOBBLE_BF 0x00BF // Painting wobble (WDW Middle) +#define SURFACE_PAINTING_WOBBLE_C0 0x00C0 // Painting wobble (WDW Right) +#define SURFACE_PAINTING_WOBBLE_C1 0x00C1 // Painting wobble (THI Tiny - Left) +#define SURFACE_PAINTING_WOBBLE_C2 0x00C2 // Painting wobble (THI Tiny - Middle) +#define SURFACE_PAINTING_WOBBLE_C3 0x00C3 // Painting wobble (THI Tiny - Right) +#define SURFACE_PAINTING_WOBBLE_C4 0x00C4 // Painting wobble (TTM Left) +#define SURFACE_PAINTING_WOBBLE_C5 0x00C5 // Painting wobble (TTM Middle) +#define SURFACE_PAINTING_WOBBLE_C6 0x00C6 // Painting wobble (TTM Right) +#define SURFACE_PAINTING_WOBBLE_C7 0x00C7 // Painting wobble (Unused, TTC - Left) +#define SURFACE_PAINTING_WOBBLE_C8 0x00C8 // Painting wobble (Unused, TTC - Middle) +#define SURFACE_PAINTING_WOBBLE_C9 0x00C9 // Painting wobble (Unused, TTC - Right) +#define SURFACE_PAINTING_WOBBLE_CA 0x00CA // Painting wobble (Unused, SL - Left) +#define SURFACE_PAINTING_WOBBLE_CB 0x00CB // Painting wobble (Unused, SL - Middle) +#define SURFACE_PAINTING_WOBBLE_CC 0x00CC // Painting wobble (Unused, SL - Right) +#define SURFACE_PAINTING_WOBBLE_CD 0x00CD // Painting wobble (THI Huge - Left) +#define SURFACE_PAINTING_WOBBLE_CE 0x00CE // Painting wobble (THI Huge - Middle) +#define SURFACE_PAINTING_WOBBLE_CF 0x00CF // Painting wobble (THI Huge - Right) +#define SURFACE_PAINTING_WOBBLE_D0 0x00D0 // Painting wobble (HMC & COTMC - Left), makes the painting wobble if touched +#define SURFACE_PAINTING_WOBBLE_D1 0x00D1 // Painting wobble (Unused, HMC & COTMC - Middle) +#define SURFACE_PAINTING_WOBBLE_D2 0x00D2 // Painting wobble (Unused, HMC & COTMC - Right) +#define SURFACE_PAINTING_WARP_D3 0x00D3 // Painting warp (BOB Left) +#define SURFACE_PAINTING_WARP_D4 0x00D4 // Painting warp (BOB Middle) +#define SURFACE_PAINTING_WARP_D5 0x00D5 // Painting warp (BOB Right) +#define SURFACE_PAINTING_WARP_D6 0x00D6 // Painting warp (CCM Left) +#define SURFACE_PAINTING_WARP_D7 0x00D7 // Painting warp (CCM Middle) +#define SURFACE_PAINTING_WARP_D8 0x00D8 // Painting warp (CCM Right) +#define SURFACE_PAINTING_WARP_D9 0x00D9 // Painting warp (WF Left) +#define SURFACE_PAINTING_WARP_DA 0x00DA // Painting warp (WF Middle) +#define SURFACE_PAINTING_WARP_DB 0x00DB // Painting warp (WF Right) +#define SURFACE_PAINTING_WARP_DC 0x00DC // Painting warp (JRB Left) +#define SURFACE_PAINTING_WARP_DD 0x00DD // Painting warp (JRB Middle) +#define SURFACE_PAINTING_WARP_DE 0x00DE // Painting warp (JRB Right) +#define SURFACE_PAINTING_WARP_DF 0x00DF // Painting warp (LLL Left) +#define SURFACE_PAINTING_WARP_E0 0x00E0 // Painting warp (LLL Middle) +#define SURFACE_PAINTING_WARP_E1 0x00E1 // Painting warp (LLL Right) +#define SURFACE_PAINTING_WARP_E2 0x00E2 // Painting warp (SSL Left) +#define SURFACE_PAINTING_WARP_E3 0x00E3 // Painting warp (SSL Medium) +#define SURFACE_PAINTING_WARP_E4 0x00E4 // Painting warp (SSL Right) +#define SURFACE_PAINTING_WARP_E5 0x00E5 // Painting warp (Unused - Left) +#define SURFACE_PAINTING_WARP_E6 0x00E6 // Painting warp (Unused - Medium) +#define SURFACE_PAINTING_WARP_E7 0x00E7 // Painting warp (Unused - Right) +#define SURFACE_PAINTING_WARP_E8 0x00E8 // Painting warp (DDD - Left) +#define SURFACE_PAINTING_WARP_E9 0x00E9 // Painting warp (DDD - Middle) +#define SURFACE_PAINTING_WARP_EA 0x00EA // Painting warp (DDD - Right) +#define SURFACE_PAINTING_WARP_EB 0x00EB // Painting warp (WDW Left) +#define SURFACE_PAINTING_WARP_EC 0x00EC // Painting warp (WDW Middle) +#define SURFACE_PAINTING_WARP_ED 0x00ED // Painting warp (WDW Right) +#define SURFACE_PAINTING_WARP_EE 0x00EE // Painting warp (THI Tiny - Left) +#define SURFACE_PAINTING_WARP_EF 0x00EF // Painting warp (THI Tiny - Middle) +#define SURFACE_PAINTING_WARP_F0 0x00F0 // Painting warp (THI Tiny - Right) +#define SURFACE_PAINTING_WARP_F1 0x00F1 // Painting warp (TTM Left) +#define SURFACE_PAINTING_WARP_F2 0x00F2 // Painting warp (TTM Middle) +#define SURFACE_PAINTING_WARP_F3 0x00F3 // Painting warp (TTM Right) +#define SURFACE_TTC_PAINTING_1 0x00F4 // Painting warp (TTC Left) +#define SURFACE_TTC_PAINTING_2 0x00F5 // Painting warp (TTC Medium) +#define SURFACE_TTC_PAINTING_3 0x00F6 // Painting warp (TTC Right) +#define SURFACE_PAINTING_WARP_F7 0x00F7 // Painting warp (SL Left) +#define SURFACE_PAINTING_WARP_F8 0x00F8 // Painting warp (SL Middle) +#define SURFACE_PAINTING_WARP_F9 0x00F9 // Painting warp (SL Right) +#define SURFACE_PAINTING_WARP_FA 0x00FA // Painting warp (THI Tiny - Left) +#define SURFACE_PAINTING_WARP_FB 0x00FB // Painting warp (THI Tiny - Middle) +#define SURFACE_PAINTING_WARP_FC 0x00FC // Painting warp (THI Tiny - Right) +#define SURFACE_WOBBLING_WARP 0x00FD // Pool warp (HMC & DDD) +#define SURFACE_TRAPDOOR 0x00FF // Bowser Left trapdoor, has no action defined + +#define SURFACE_IS_QUICKSAND(cmd) (cmd >= 0x21 && cmd < 0x28) // Doesn't include SURFACE_INSTANT_MOVING_QUICKSAND +#define SURFACE_IS_NOT_HARD(cmd) (cmd != SURFACE_HARD && \ + !(cmd >= 0x35 && cmd <= 0x37)) +#define SURFACE_IS_PAINTING_WARP(cmd) (cmd >= 0xD3 && cmd < 0xFD) + +#define SURFACE_CLASS_DEFAULT 0x0000 +#define SURFACE_CLASS_VERY_SLIPPERY 0x0013 +#define SURFACE_CLASS_SLIPPERY 0x0014 +#define SURFACE_CLASS_NOT_SLIPPERY 0x0015 + +#define SURFACE_FLAG_DYNAMIC (1 << 0) +#define SURFACE_FLAG_NO_CAM_COLLISION (1 << 1) +#define SURFACE_FLAG_X_PROJECTION (1 << 3) + +// These are effectively unique "surface" types like those defined higher +// And they are used as collision commands to load certain functions +#define TERRAIN_LOAD_VERTICES 0x0040 // Begins vertices list for collision triangles +#define TERRAIN_LOAD_CONTINUE 0x0041 // Stop loading vertices but continues to load other collision commands +#define TERRAIN_LOAD_END 0x0042 // End the collision list +#define TERRAIN_LOAD_OBJECTS 0x0043 // Loads in certain objects for level start +#define TERRAIN_LOAD_ENVIRONMENT 0x0044 // Loads water/HMC gas + +#define TERRAIN_LOAD_IS_SURFACE_TYPE_LOW(cmd) (cmd < 0x40) +#define TERRAIN_LOAD_IS_SURFACE_TYPE_HIGH(cmd) (cmd >= 0x65) + +// Terrain types defined by the level script command terrain_type (cmd_31) +#define TERRAIN_GRASS 0x0000 +#define TERRAIN_STONE 0x0001 +#define TERRAIN_SNOW 0x0002 +#define TERRAIN_SAND 0x0003 +#define TERRAIN_SPOOKY 0x0004 +#define TERRAIN_WATER 0x0005 +#define TERRAIN_SLIDE 0x0006 +#define TERRAIN_MASK 0x0007 + +// These collision commands are unique "surface" types like those defined higher + +// Collision Data Routine Initiate +#define COL_INIT() TERRAIN_LOAD_VERTICES + +// Collision Vertices Read Initiate +#define COL_VERTEX_INIT(vtxNum) vtxNum + +// Collision Vertex +#define COL_VERTEX(x, y, z) x, y, z + +// Collision Tris Initiate +#define COL_TRI_INIT(surfType, triNum) surfType, triNum + +// Collision Tri +#define COL_TRI(v1, v2, v3) v1, v2, v3 + +// Collision Tri With Special Params +#define COL_TRI_SPECIAL(v1, v2, v3, param) v1, v2, v3, param + +// Collision Tris Stop Loading +#define COL_TRI_STOP() TERRAIN_LOAD_CONTINUE + +// End Collision Data +#define COL_END() TERRAIN_LOAD_END + +// Special Object Initiate +#define COL_SPECIAL_INIT(num) TERRAIN_LOAD_OBJECTS, num + +// Water Boxes Initiate +#define COL_WATER_BOX_INIT(num) TERRAIN_LOAD_ENVIRONMENT, num + +// Water Box +#define COL_WATER_BOX(id, x1, z1, x2, z2, y) id, x1, z1, x2, z2, y + +#endif // SURFACE_TERRAINS_H diff --git a/src/include/types.h b/src/include/types.h new file mode 100644 index 0000000..33584b5 --- /dev/null +++ b/src/include/types.h @@ -0,0 +1,348 @@ +#ifndef TYPES_H +#define TYPES_H + +// This file contains various data types used in Super Mario 64 that don't yet +// have an appropriate header. + +// #include +#include "macros.h" +#include "PR/ultratypes.h" + +// Certain functions are marked as having return values, but do not +// actually return a value. This causes undefined behavior, which we'd rather +// avoid on modern GCC. This only impacts -O2 and can matter for both the function +// itself and functions that call it. +#ifdef AVOID_UB + #define BAD_RETURN(cmd) void +#else + #define BAD_RETURN(cmd) cmd +#endif + + +struct Controller +{ + /*0x00*/ s16 rawStickX; // + /*0x02*/ s16 rawStickY; // + /*0x04*/ float stickX; // [-64, 64] positive is right + /*0x08*/ float stickY; // [-64, 64] positive is up + /*0x0C*/ float stickMag; // distance from center [0, 64] + /*0x10*/ u16 buttonDown; + /*0x12*/ u16 buttonPressed; +///*0x14*/ OSContStatus *statusData; +///*0x18*/ OSContPad *controllerData; +#ifdef VERSION_SH + /*0x1C*/ int port; +#endif +}; + +typedef f32 Vec2f[2]; +typedef f32 Vec3f[3]; // X, Y, Z, where Y is up +typedef s16 Vec3s[3]; +typedef s32 Vec3i[3]; +typedef f32 Vec4f[4]; +typedef s16 Vec4s[4]; + +typedef f32 Mat4[4][4]; + +typedef uintptr_t GeoLayout; +typedef uintptr_t LevelScript; +typedef s16 Movtex; +typedef s16 MacroObject; +typedef s16 Collision; +typedef s16 Trajectory; +typedef s16 PaintingData; +typedef uintptr_t BehaviorScript; + +enum SpTaskState { + SPTASK_STATE_NOT_STARTED, + SPTASK_STATE_RUNNING, + SPTASK_STATE_INTERRUPTED, + SPTASK_STATE_FINISHED, + SPTASK_STATE_FINISHED_DP +}; + +// struct SPTask +// { +// /*0x00*/ OSTask task; +// /*0x40*/ OSMesgQueue *msgqueue; +// /*0x44*/ OSMesg msg; +// /*0x48*/ enum SpTaskState state; +// }; // size = 0x4C, align = 0x8 +// +// struct VblankHandler +// { +// OSMesgQueue *queue; +// OSMesg msg; +// }; + +#define ANIM_FLAG_NOLOOP (1 << 0) // 0x01 +#define ANIM_FLAG_FORWARD (1 << 1) // 0x02 +#define ANIM_FLAG_2 (1 << 2) // 0x04 +#define ANIM_FLAG_HOR_TRANS (1 << 3) // 0x08 +#define ANIM_FLAG_VERT_TRANS (1 << 4) // 0x10 +#define ANIM_FLAG_5 (1 << 5) // 0x20 +#define ANIM_FLAG_6 (1 << 6) // 0x40 +#define ANIM_FLAG_7 (1 << 7) // 0x80 + +struct Animation { + /*0x00*/ s16 flags; + /*0x02*/ s16 animYTransDivisor; + /*0x04*/ s16 startFrame; + /*0x06*/ s16 loopStart; + /*0x08*/ s16 loopEnd; + /*0x0A*/ s16 unusedBoneCount; + /*0x0C*/ const s16 *values; + /*0x10*/ const u16 *index; + /*0x14*/ u32 length; // only used with Mario animations to determine how much to load. 0 otherwise. +}; + +#define ANIMINDEX_NUMPARTS(animindex) (sizeof(animindex) / sizeof(u16) / 6 - 1) + +struct GraphNode +{ + /*0x00*/ s16 type; // structure type + /*0x02*/ s16 flags; // hi = drawing layer, lo = rendering modes + /*0x04*/ struct GraphNode *prev; + /*0x08*/ struct GraphNode *next; + /*0x0C*/ struct GraphNode *parent; + /*0x10*/ struct GraphNode *children; +}; + +struct AnimInfo +{ + /*0x00 0x38*/ s16 animID; + /*0x02 0x3A*/ s16 animYTrans; + /*0x04 0x3C*/ struct Animation *curAnim; + /*0x08 0x40*/ s16 animFrame; + /*0x0A 0x42*/ u16 animTimer; + /*0x0C 0x44*/ s32 animFrameAccelAssist; + /*0x10 0x48*/ s32 animAccel; +}; + +struct GraphNodeObject +{ + /*0x00*/ struct GraphNode node; + /*0x14*/ struct GraphNode *sharedChild; + /*0x18*/ s8 areaIndex; + /*0x19*/ s8 activeAreaIndex; + /*0x1A*/ Vec3s angle; + /*0x20*/ Vec3f pos; + /*0x2C*/ Vec3f scale; + /*0x38*/ struct AnimInfo animInfo; + /*0x4C*/ struct SpawnInfo *unk4C; + /*0x50*/ Mat4 *throwMatrix; // matrix ptr + /*0x54*/ Vec3f cameraToObject; +}; + +struct ObjectNode +{ + struct GraphNodeObject gfx; + struct ObjectNode *next; + struct ObjectNode *prev; +}; + +// NOTE: Since ObjectNode is the first member of Object, it is difficult to determine +// whether some of these pointers point to ObjectNode or Object. + +struct Object +{ + /*0x000*/ struct ObjectNode header; + /*0x068*/ struct Object *parentObj; + /*0x06C*/ struct Object *prevObj; + /*0x070*/ u32 collidedObjInteractTypes; + /*0x074*/ s16 activeFlags; + /*0x076*/ s16 numCollidedObjs; + /*0x078*/ struct Object *collidedObjs[4]; + /*0x088*/ + union + { + // Object fields. See object_fields.h. + u32 asU32[0x50]; + s32 asS32[0x50]; + s16 asS16[0x50][2]; + f32 asF32[0x50]; +#if !IS_64_BIT + s16 *asS16P[0x50]; + s32 *asS32P[0x50]; + struct Animation **asAnims[0x50]; + struct Waypoint *asWaypoint[0x50]; + struct ChainSegment *asChainSegment[0x50]; + struct Object *asObject[0x50]; + struct Surface *asSurface[0x50]; + void *asVoidPtr[0x50]; + const void *asConstVoidPtr[0x50]; +#endif + } rawData; +#if IS_64_BIT + union { + s16 *asS16P[0x50]; + s32 *asS32P[0x50]; + struct Animation **asAnims[0x50]; + struct Waypoint *asWaypoint[0x50]; + struct ChainSegment *asChainSegment[0x50]; + struct Object *asObject[0x50]; + struct Surface *asSurface[0x50]; + void *asVoidPtr[0x50]; + const void *asConstVoidPtr[0x50]; + } ptrData; +#endif + /*0x1C8*/ u32 unused1; + /*0x1CC*/ const BehaviorScript *curBhvCommand; + /*0x1D0*/ u32 bhvStackIndex; + /*0x1D4*/ uintptr_t bhvStack[8]; + /*0x1F4*/ s16 bhvDelayTimer; + /*0x1F6*/ s16 respawnInfoType; + /*0x1F8*/ f32 hitboxRadius; + /*0x1FC*/ f32 hitboxHeight; + /*0x200*/ f32 hurtboxRadius; + /*0x204*/ f32 hurtboxHeight; + /*0x208*/ f32 hitboxDownOffset; + /*0x20C*/ const BehaviorScript *behavior; + /*0x210*/ u32 unused2; + /*0x214*/ struct Object *platform; + /*0x218*/ void *collisionData; + /*0x21C*/ Mat4 transform; + /*0x25C*/ void *respawnInfo; +}; + +struct ObjectHitbox +{ + /*0x00*/ u32 interactType; + /*0x04*/ u8 downOffset; + /*0x05*/ s8 damageOrCoinValue; + /*0x06*/ s8 health; + /*0x07*/ s8 numLootCoins; + /*0x08*/ s16 radius; + /*0x0A*/ s16 height; + /*0x0C*/ s16 hurtboxRadius; + /*0x0E*/ s16 hurtboxHeight; +}; + +struct Waypoint +{ + s16 flags; + Vec3s pos; +}; + +struct Surface +{ + /*0x00*/ s16 type; + /*0x02*/ s16 force; + /*0x04*/ s8 flags; + /*0x05*/ s8 room; + /*0x06*/ s16 lowerY; + /*0x08*/ s16 upperY; + /*0x0A*/ Vec3s vertex1; + /*0x10*/ Vec3s vertex2; + /*0x16*/ Vec3s vertex3; + /*0x1C*/ struct { + f32 x; + f32 y; + f32 z; + } normal; + /*0x28*/ f32 originOffset; + /*0x2C*/ struct Object *object; +}; + +struct MarioBodyState +{ + /*0x00*/ u32 action; + /*0x04*/ s8 capState; /// see MarioCapGSCId + /*0x05*/ s8 eyeState; + /*0x06*/ s8 handState; + /*0x07*/ s8 wingFlutter; /// whether Mario's wing cap wings are fluttering + /*0x08*/ s16 modelState; + /*0x0A*/ s8 grabPos; + /*0x0B*/ u8 punchState; /// 2 bits for type of punch, 6 bits for punch animation timer + /*0x0C*/ Vec3s torsoAngle; + /*0x12*/ Vec3s headAngle; + /*0x18*/ Vec3f heldObjLastPosition; /// also known as HOLP + u8 padding[4]; +}; + +struct OffsetSizePair +{ + u32 offset; + u32 size; +}; + +struct MarioAnimDmaRelatedThing +{ + u32 count; + u8 *srcAddr; + struct OffsetSizePair anim[1]; // dynamic size +}; + +struct MarioAnimation +{ + struct MarioAnimDmaRelatedThing *animDmaTable; + u8 *currentAnimAddr; + struct Animation *targetAnim; + u8 padding[4]; +}; + +struct MarioState +{ + /*0x00*/ u16 unk00; + /*0x02*/ u16 input; + /*0x04*/ u32 flags; + /*0x08*/ u32 particleFlags; + /*0x0C*/ u32 action; + /*0x10*/ u32 prevAction; + /*0x14*/ u32 terrainSoundAddend; + /*0x18*/ u16 actionState; + /*0x1A*/ u16 actionTimer; + /*0x1C*/ u32 actionArg; + /*0x20*/ f32 intendedMag; + /*0x24*/ s16 intendedYaw; + /*0x26*/ s16 invincTimer; + /*0x28*/ u8 framesSinceA; + /*0x29*/ u8 framesSinceB; + /*0x2A*/ u8 wallKickTimer; + /*0x2B*/ u8 doubleJumpTimer; + /*0x2C*/ Vec3s faceAngle; + /*0x32*/ Vec3s angleVel; + /*0x38*/ s16 slideYaw; + /*0x3A*/ s16 twirlYaw; + /*0x3C*/ Vec3f pos; + /*0x48*/ Vec3f vel; + /*0x54*/ f32 forwardVel; + /*0x58*/ f32 slideVelX; + /*0x5C*/ f32 slideVelZ; + /*0x60*/ struct Surface *wall; + /*0x64*/ struct Surface *ceil; + /*0x68*/ struct Surface *floor; + /*0x6C*/ f32 ceilHeight; + /*0x70*/ f32 floorHeight; + /*0x74*/ s16 floorAngle; + /*0x76*/ s16 waterLevel; + /*0x78*/ struct Object *interactObj; + /*0x7C*/ struct Object *heldObj; + /*0x80*/ struct Object *usedObj; + /*0x84*/ struct Object *riddenObj; + /*0x88*/ struct Object *marioObj; + /*0x8C*/ struct SpawnInfo *spawnInfo; + /*0x90*/ struct Area *area; + /*0x94*/ struct PlayerCameraState *statusForCamera; + /*0x98*/ struct MarioBodyState *marioBodyState; + /*0x9C*/ struct Controller *controller; + /*0xA0*/ struct MarioAnimation *animation; + /*0xA4*/ u32 collidedObjInteractTypes; + /*0xA8*/ s16 numCoins; + /*0xAA*/ s16 numStars; + /*0xAC*/ s8 numKeys; // Unused key mechanic + /*0xAD*/ s8 numLives; + /*0xAE*/ s16 health; + /*0xB0*/ s16 unkB0; + /*0xB2*/ u8 hurtCounter; + /*0xB3*/ u8 healCounter; + /*0xB4*/ u8 squishTimer; + /*0xB5*/ u8 fadeWarpOpacity; + /*0xB6*/ u16 capTimer; + /*0xB8*/ s16 prevNumStarsForDialog; + /*0xBC*/ f32 peakHeight; + /*0xC0*/ f32 quicksandDepth; + /*0xC4*/ f32 unkC4; +}; + +#endif // TYPES_H diff --git a/src/libsm64.c b/src/libsm64.c new file mode 100644 index 0000000..ea6a9d8 --- /dev/null +++ b/src/libsm64.c @@ -0,0 +1,154 @@ +#include + +#include "libsm64.h" + +#include +#include +#include + +#include "include/PR/os_cont.h" +#include "engine/math_util.h" +#include "include/sm64.h" +#include "shim.h" +#include "game/mario.h" +#include "game/object_stuff.h" +#include "engine/surface_load.h" +#include "engine/surface_collision.h" +#include "engine/graph_node.h" +#include "engine/geo_layout.h" +#include "assets/mario_anim_data.h" +#include "game/rendering_graph_node.h" +#include "model/geo.inc.h" +#include "gfx_adapter.h" + +static struct AllocOnlyPool *s_mario_geo_pool; +static struct GraphNode *s_mario_graph_node; + +static void update_button( bool on, u16 button ) +{ + gController.buttonPressed &= ~button; + + if( on ) + { + if(( gController.buttonDown & button ) == 0 ) + gController.buttonPressed |= button; + + gController.buttonDown |= button; + } + else + { + gController.buttonDown &= ~button; + } +} + +static struct Camera *hack_build_camera( void ) +{ + struct Camera *result = malloc( sizeof( struct Camera )); + memset( result, 0, sizeof( struct Camera )); + return result; +} + +static struct Area *hack_build_area( void ) +{ + struct Area *result = malloc( sizeof( struct Area )); + + result->index = 0; + result->flags = 1; + result->terrainType = TERRAIN_GRASS; + result->unk04 = NULL; + result->terrainData = NULL; + result->surfaceRooms = NULL; + result->macroObjects = NULL; + result->warpNodes = NULL; + result->paintingWarpNodes = NULL; + result->instantWarps = NULL; + result->objectSpawnInfos = NULL; + result->camera = hack_build_camera(); + result->unused28 = NULL; + result->whirlpools[0] = NULL; + result->whirlpools[1] = NULL; + result->dialog[0] = 0; + result->dialog[1] = 0; + result->musicParam = 0; + result->musicParam2 = 0; + + return result; +} + +void sm64_global_init( SM64DebugPrintFunctionPtr debugPrintFunction ) +{ + gDebugPrint = debugPrintFunction; + + gMarioObject = hack_allocate_mario(); + gCurrentArea = hack_build_area(); + gCurrentObject = gMarioObject; + + s_mario_geo_pool = alloc_only_pool_init(); + s_mario_graph_node = process_geo_layout( s_mario_geo_pool, mario_geo_ptr ); + + D_80339D10.animDmaTable = (void*)(&gMarioAnims); + D_80339D10.currentAnimAddr = NULL; + D_80339D10.targetAnim = malloc( 0x4000 ); + + DEBUG_LOG( "Mario animations loaded from address %lu", (uint64_t)D_80339D10.animDmaTable ); +} + +void sm64_load_surfaces( uint16_t terrainType, const struct SM64Surface *surfaceArray, size_t numSurfaces ) +{ + surface_load_for_libsm64( surfaceArray, numSurfaces ); + gCurrentArea->terrainType = terrainType; +} + +void sm64_mario_reset( int16_t marioX, int16_t marioY, int16_t marioZ ) +{ + gMarioSpawnInfoVal.startPos[0] = marioX; + gMarioSpawnInfoVal.startPos[1] = marioY; + gMarioSpawnInfoVal.startPos[2] = marioZ; + + gMarioSpawnInfoVal.startAngle[0] = 0; + gMarioSpawnInfoVal.startAngle[1] = 0; + gMarioSpawnInfoVal.startAngle[2] = 0; + + gMarioSpawnInfoVal.areaIndex = 0; + gMarioSpawnInfoVal.activeAreaIndex = 0; + gMarioSpawnInfoVal.behaviorArg = 0; + gMarioSpawnInfoVal.behaviorScript = NULL; + gMarioSpawnInfoVal.unk18 = NULL; + gMarioSpawnInfoVal.next = NULL; + + init_mario_from_save_file(); + init_mario(); + set_mario_action( gMarioState, ACT_SPAWN_SPIN_AIRBORNE, 0); + find_floor( marioX, marioY, marioZ, &gMarioState->floor ); +} + +void sm64_mario_tick( const struct SM64MarioInputs *inputs, struct SM64MarioState *outState, struct SM64MarioGeometryBuffers *outBuffers ) +{ + update_button( inputs->buttonA, A_BUTTON ); + update_button( inputs->buttonB, B_BUTTON ); + update_button( inputs->buttonZ, Z_TRIG ); + + gMarioState->area->camera->yaw = atan2s( inputs->camLookZ, inputs->camLookX ); + + gController.stickX = -64.0f * inputs->stickX; + gController.stickY = 64.0f * inputs->stickY; + gController.stickMag = sqrtf( gController.stickX*gController.stickX + gController.stickY*gController.stickY ); + + bhv_mario_update(); + + gfx_adapter_bind_output_buffers( outBuffers ); + + geo_process_root_hack_single_node( s_mario_graph_node ); + + gAreaUpdateCounter++; + + outState->health = gMarioState->health; + vec3f_copy( outState->position, gMarioState->pos ); + vec3f_copy( outState->velocity, gMarioState->vel ); + outState->faceAngle = (float)gMarioState->faceAngle[1] / 32768.0f * 3.14159f; +} + +void sm64_global_terminate( void ) +{ + // TODO free +} \ No newline at end of file diff --git a/src/libsm64.h b/src/libsm64.h new file mode 100644 index 0000000..4ca3c9f --- /dev/null +++ b/src/libsm64.h @@ -0,0 +1,49 @@ +#ifndef __LIB_SM64_H +#define __LIB_SM64_H + +#include +#include +#include + +#define SM64_GEO_BUFFER_SIZE 9216 // 1024 triangles * 9 floats per triangle + +struct SM64Surface +{ + int16_t type; + int16_t force; + int16_t vertices[3][3]; +}; + +struct SM64MarioInputs +{ + float camLookX, camLookZ; + float stickX, stickY; + uint8_t buttonA, buttonB, buttonZ; +}; + +struct SM64MarioState +{ + float position[3]; + float velocity[3]; + float faceAngle; + int16_t health; +}; + +struct SM64MarioGeometryBuffers +{ + size_t bufferMaxSize; + size_t bufferUsedSize; + float *position; + float *normal; + float *color; +}; + +typedef void (*SM64DebugPrintFunctionPtr)( const char * ); + +extern void sm64_global_init( SM64DebugPrintFunctionPtr debugPrintFunction ); +extern void sm64_load_surfaces( uint16_t terrainType, const struct SM64Surface *surfaceArray, size_t numSurfaces ); +extern void sm64_mario_reset( int16_t marioX, int16_t marioY, int16_t marioZ ); +extern void sm64_mario_tick( const struct SM64MarioInputs *inputs, struct SM64MarioState *outState, struct SM64MarioGeometryBuffers *outBuffers ); +extern void sm64_global_terminate( void ); + +#endif//__LIB_SM64_H \ No newline at end of file diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..4513f04 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,32 @@ +#include + +#include "memory.h" + +struct AllocOnlyPool +{ + int id; +}; + +struct AllocOnlyPool *alloc_only_pool_init(void) +{ + struct AllocOnlyPool *result = malloc( sizeof( struct AllocOnlyPool )); + result->id = 0x69; + return result; +} + +void *alloc_only_pool_alloc(struct AllocOnlyPool *pool, s32 size) +{ + return malloc(size); +} + +void *alloc_display_list(u32 size) +{ + return malloc(size); +// size = ALIGN8(size); +// return alloc_only_pool_alloc(gGfxAllocOnlyPool, size); +} + +void main_pool_free(struct AllocOnlyPool *pool) +{ + // TODO +} \ No newline at end of file diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..78d35b5 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,10 @@ +#pragma once + +#include "include/types.h" + +struct AllocOnlyPool; + +extern struct AllocOnlyPool *alloc_only_pool_init(void); +extern void *alloc_only_pool_alloc(struct AllocOnlyPool *pool, s32 size); +extern void *alloc_display_list(u32 size); +extern void main_pool_free(struct AllocOnlyPool *pool); \ No newline at end of file diff --git a/src/shim.c b/src/shim.c new file mode 100644 index 0000000..b13bca0 --- /dev/null +++ b/src/shim.c @@ -0,0 +1,145 @@ +#include +#include "shim.h" + +u32 gGlobalTimer = 0; +u8 gSpecialTripleJump = FALSE; +struct HudDisplay gHudDisplay; +s16 gCurrLevelNum = 0; +s16 gCameraMovementFlags = 0; +u32 gAudioRandom = 0; +s8 gShowDebugText = 0; +s8 gDebugLevelSelect = 0; +s16 gCurrSaveFileNum = 1; +struct Controller gController; +struct SpawnInfo gMarioSpawnInfoVal; +struct SpawnInfo *gMarioSpawnInfo = &gMarioSpawnInfoVal; +struct Area *gCurrentArea = NULL; +struct Object *gCurrentObject; +struct Object *gMarioObject = NULL; +struct PlayerCameraState gPlayerCameraState; +struct MarioAnimation D_80339D10; +struct MarioState gMarioStateVal; +struct MarioState *gMarioState = &gMarioStateVal; +SM64DebugPrintFunctionPtr gDebugPrint = NULL; + + +void hack_load_mario_animation(struct MarioAnimation *a, u32 index) +{ + struct MarioAnimDmaRelatedThing *sp20 = a->animDmaTable; + u8 *addr; + u32 size; + + if (index < sp20->count) { + addr = sp20->srcAddr + sp20->anim[index].offset; + size = sp20->anim[index].size; + + if (a->currentAnimAddr != addr) { + u32 a0 = sp20->anim[index].offset; + u32 b0 = sp20->anim[index].size; + + memcpy( (u8*)a->targetAnim, (u8*)sp20 + a0, b0 ); + a->currentAnimAddr = addr; + + struct Animation *targetAnim = a->targetAnim; + targetAnim->values =(void*)( (u8 *)targetAnim + (uintptr_t)targetAnim->values ); + targetAnim->index = (void*)( (u8 *)targetAnim + (uintptr_t)targetAnim->index ); + } + } +} + +void *segmented_to_virtual(const void *addr) +{ + return (void*)addr; +} + +void *virtual_to_segmented(u32 segment, const void *addr) +{ + return (void*)addr; +} + +void func_80320A4C(u8 bankIndex, u8 arg1) +{ +} + +void lower_background_noise(s32 a) +{ +} + +void raise_background_noise(s32 a) +{ +} + +void set_camera_mode(struct Camera *c, s16 mode, s16 frames) +{ +} + +void print_text_fmt_int(s32 x, s32 y, const char *str, s32 n) +{ +} + +s16 level_trigger_warp(struct MarioState *m, s32 warpOp) +{ + return 0; +} + +void play_cap_music(u16 seqArgs) +{ +} + +void fadeout_cap_music(void) +{ +} + +void stop_cap_music(void) +{ +} + +void play_infinite_stairs_music(void) +{ +} + +s32 save_file_get_total_star_count(s32 fileIndex, s32 minCourse, s32 maxCourse) +{ + return 0; +} + +u32 save_file_get_flags(void) +{ + return 0; +} + +void save_file_set_flags(u32 flags) +{ +} + +void save_file_clear_flags(u32 flags) +{ +} + +void spawn_wind_particles(s16 pitch, s16 yaw) +{ +} + +void set_camera_shake_from_hit(s16 shake) +{ +} + +void load_level_init_text(u32 arg) +{ +} + +void spawn_default_star(f32 sp20, f32 sp24, f32 sp28) +{ +} + +void play_shell_music(void) +{ +} + +void stop_shell_music(void) +{ +} + +u16 level_control_timer(s32 timerOp) +{ +} \ No newline at end of file diff --git a/src/shim.h b/src/shim.h new file mode 100644 index 0000000..1624a54 --- /dev/null +++ b/src/shim.h @@ -0,0 +1,82 @@ +#pragma once + +#include "include/types.h" +#include "game/area.h" +#include "game/level_update.h" +#include "libsm64.h" + +#define COURSE_MIN 0 +#define COURSE_MAX 14 +#define LEVEL_LLL 99 +#define LEVEL_SSL 98 +#define LEVEL_DDD 97 +#define LEVEL_TTC 96 +#define LEVEL_CASTLE 95 +#define LEVEL_THI 94 + +// From graph_node.h, trying not to pull that in yet +#define GRAPH_RENDER_INVISIBLE (1 << 4) + +#define play_sound(a,b) ({}) +#define enable_time_stop() ({}) +#define disable_time_stop() ({}) +#define play_cutscene_music(a) ({}) + +struct SurfaceNode +{ + struct SurfaceNode *next; + struct Surface *surface; +}; + +extern u32 gGlobalTimer; +extern u8 gSpecialTripleJump; +extern struct HudDisplay gHudDisplay; +extern s16 gCurrLevelNum; +extern s16 gCameraMovementFlags; +extern u32 gAudioRandom; +extern s8 gShowDebugText; +extern s8 gDebugLevelSelect; +extern s16 gCurrSaveFileNum; +extern struct Controller gController; +extern struct SpawnInfo gMarioSpawnInfoVal; +extern struct SpawnInfo *gMarioSpawnInfo; +extern struct Area *gCurrentArea; +extern struct Object *gCurrentObject; +extern struct Object *gMarioObject; +extern struct PlayerCameraState gPlayerCameraState; +extern struct MarioAnimation D_80339D10; +extern struct MarioState *gMarioState; +extern SM64DebugPrintFunctionPtr gDebugPrint; + +#define DEBUG_LOG( ... ) do { \ + if( gDebugPrint ) { \ + char debugStr[1024]; \ + sprintf( debugStr, __VA_ARGS__ ); \ + gDebugPrint( debugStr ); \ + } \ +} while(0) + +extern void hack_load_mario_animation(struct MarioAnimation *a, u32 index); +extern void *segmented_to_virtual(const void *addr); +extern void *virtual_to_segmented(u32 segment, const void *addr); +extern void func_80320A4C(u8 bankIndex, u8 arg1); +extern void raise_background_noise(s32 a); +extern void lower_background_noise(s32 a); +extern void set_camera_mode(struct Camera *c, s16 mode, s16 frames); +extern void print_text_fmt_int(s32 x, s32 y, const char *str, s32 n); +extern s16 level_trigger_warp(struct MarioState *m, s32 warpOp); +extern void play_cap_music(u16 seqArgs); +extern void fadeout_cap_music(void); +extern void stop_cap_music(void); +extern void play_infinite_stairs_music(void); +extern s32 save_file_get_total_star_count(s32 fileIndex, s32 minCourse, s32 maxCourse); +extern u32 save_file_get_flags(void); +extern void save_file_set_flags(u32 flags); +extern void save_file_clear_flags(u32 flags); +extern void spawn_wind_particles(s16 pitch, s16 yaw); +extern void set_camera_shake_from_hit(s16 shake); +extern void load_level_init_text(u32 arg); +extern void spawn_default_star(f32 sp20, f32 sp24, f32 sp28); +extern void play_shell_music(void); +extern void stop_shell_music(void); +extern u16 level_control_timer(s32 timerOp); \ No newline at end of file