Merge branch 'dev' into screen-shader-effects

This commit is contained in:
Agent X 2026-05-04 23:41:48 -04:00
commit 840db50ed0
21 changed files with 311 additions and 141 deletions

View file

@ -17,3 +17,6 @@ sm64coopdx is moddable via Lua, similar to Roblox and Garry's Mod's Lua APIs. To
## Wiki
The wiki is made using GitHub's wiki feature, you can go to the wiki tab or click [here](https://github.com/coop-deluxe/sm64coopdx/wiki).
## Community
We have an official Discord server open to the public [here](https://discord.gg/TJVKHS4).

View file

@ -8271,7 +8271,9 @@ HOOK_ON_FIND_WATER_LEVEL = 63 --- @type LuaHookedEventType
HOOK_ON_FIND_POISON_GAS_LEVEL = 64 --- @type LuaHookedEventType
HOOK_ON_FIND_SURFACE_ON_RAY = 65 --- @type LuaHookedEventType
HOOK_ON_DYNOS_PACK_TOGGLED = 66 --- @type LuaHookedEventType
HOOK_MAX = 67 --- @type LuaHookedEventType
HOOK_BEFORE_PLAY_MODE_UPDATE = 67 --- @type LuaHookedEventType
HOOK_ON_PLAY_MODE_UPDATE = 68 --- @type LuaHookedEventType
HOOK_MAX = 69 --- @type LuaHookedEventType
--- @alias LuaHookedEventType
--- | `HOOK_UPDATE`
@ -8341,6 +8343,8 @@ HOOK_MAX = 67 --- @type LuaHookedEventType
--- | `HOOK_ON_FIND_POISON_GAS_LEVEL`
--- | `HOOK_ON_FIND_SURFACE_ON_RAY`
--- | `HOOK_ON_DYNOS_PACK_TOGGLED`
--- | `HOOK_BEFORE_PLAY_MODE_UPDATE`
--- | `HOOK_ON_PLAY_MODE_UPDATE`
--- | `HOOK_MAX`
--- @type integer
@ -11241,6 +11245,9 @@ ANIM_FLAG_7 = (1 << 7)
--- @type integer
ANIM_FLAG_BONE_TRANS = (1 << 8)
--- @type integer
ANIM_FLAG_BONE_SCALE = (1 << 9)
--- @type integer
OBJECT_MAX_BHV_STACK = 16

View file

@ -1,17 +1,25 @@
#include <map>
#include <algorithm>
#include <unordered_map>
#include "dynos.cpp.h"
extern "C" {
#include "object_fields.h"
#include "game/level_update.h"
#include "game/object_list_processor.h"
#include "pc/configfile.h"
#include "pc/lua/smlua_hooks.h"
#include "pc/mods/mod_fs.h"
}
static ObjectList sObjectListsToOverride[] = {
OBJ_LIST_PLAYER,
OBJ_LIST_DESTRUCTIVE,
OBJ_LIST_GENACTOR,
OBJ_LIST_PUSHABLE,
OBJ_LIST_LEVEL,
OBJ_LIST_DEFAULT,
OBJ_LIST_SURFACE,
OBJ_LIST_POLELIKE,
OBJ_LIST_UNIMPORTANT
};
// Static maps/arrays
static std::map<const void*, ActorGfx>& DynosValidActors() {
static std::map<const void*, ActorGfx> sDynosValidActors;
@ -25,13 +33,11 @@ static std::vector<std::pair<std::string, void *>> &DynosCustomActors() {
static std::map<struct GraphNode *, struct GraphNode *> sModifiedGraphNodes;
// TODO: the cleanup/refactor didn't really go as planned.
// clean up the actor management code more
std::map<const void *, ActorGfx> &DynOS_Actor_GetValidActors() {
return DynosValidActors();
}
// Only used for mods with custom actors.
bool DynOS_Actor_AddCustom(s32 aModIndex, s32 aModFileIndex, const SysPath &aFilename, const char *aActorName) {
const void* georef = DynOS_Builtin_Actor_GetFromName(aActorName);
@ -51,17 +57,14 @@ bool DynOS_Actor_AddCustom(s32 aModIndex, s32 aModFileIndex, const SysPath &aFil
return false;
}
// Alloc and init the actors gfx list
// Load the graph node
u32 id = 0;
ActorGfx actorGfx = { };
actorGfx.mGfxData = _GfxData;
actorGfx.mPackIndex = MOD_PACK_INDEX;
actorGfx.mGraphNode = (GraphNode *) DynOS_Model_LoadGeo(&id, MODEL_POOL_SESSION, geoLayout, true);
if (!actorGfx.mGraphNode) {
GraphNode *graphNode = (GraphNode *) DynOS_Model_LoadGeo(&id, MODEL_POOL_SESSION, geoLayout, true);
if (!graphNode) {
PrintError(" ERROR: Couldn't load graph node for \"%s\"", actorName.c_str());
return false;
}
actorGfx.mGraphNode->georef = georef;
graphNode->georef = georef;
// Add to custom actors
if (georef == NULL) {
@ -69,6 +72,13 @@ bool DynOS_Actor_AddCustom(s32 aModIndex, s32 aModFileIndex, const SysPath &aFil
georef = geoLayout;
}
// Alloc and init the actors gfx list
ActorGfx actorGfx = {
.mGfxData = _GfxData,
.mGraphNode = graphNode,
.mPackIndex = MOD_PACK_INDEX,
};
// Add to list
DynOS_Actor_Valid(georef, actorGfx);
return true;
@ -121,44 +131,28 @@ const void *DynOS_Actor_GetLayoutFromName(const char *aActorName) {
return NULL;
}
bool DynOS_Actor_GetModIndexAndTokenFromGfxData(const GfxData *aGfxData, u32 aTokenIndex, s32 *outModIndex, s32 *outModFileIndex, const char **outToken) {
if (aGfxData) {
if (outModIndex) { *outModIndex = aGfxData->mModIndex; }
if (outModFileIndex) { *outModFileIndex = aGfxData->mModFileIndex; }
if (outToken) {
if (!aTokenIndex || aTokenIndex > aGfxData->mLuaTokenList.Count()) {
return false;
}
*outToken = aGfxData->mLuaTokenList[aTokenIndex - 1].begin(); // token index is 1-indexed
}
return true;
}
return false;
}
bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenIndex, s32 *outModIndex, s32 *outModFileIndex, const char **outToken) {
ActorGfx *_ActorGfx = DynOS_Actor_GetActorGfx(aGraphNode);
if (_ActorGfx) {
GfxData *_GfxData = _ActorGfx->mGfxData;
if (_GfxData) {
if (outModIndex) {
*outModIndex = _GfxData->mModIndex;
}
if (outModFileIndex) {
*outModFileIndex = _GfxData->mModFileIndex;
}
if (outToken) {
if (!aTokenIndex || aTokenIndex > _GfxData->mLuaTokenList.Count()) {
return false;
}
*outToken = _GfxData->mLuaTokenList[aTokenIndex - 1].begin(); // token index is 1-indexed
}
return true;
}
return DynOS_Actor_GetModIndexAndTokenFromGfxData(_ActorGfx->mGfxData, aTokenIndex, outModIndex, outModFileIndex, outToken);
} else { // try the active level
GfxData *_GfxData = DynOS_Lvl_GetActiveGfx();
if (_GfxData) {
if (outModIndex) {
*outModIndex = _GfxData->mModIndex;
}
if (outModFileIndex) {
*outModFileIndex = _GfxData->mModFileIndex;
}
if (outToken) {
if (!aTokenIndex || aTokenIndex > _GfxData->mLuaTokenList.Count()) {
return false;
}
*outToken = _GfxData->mLuaTokenList[aTokenIndex - 1].begin(); // token index is 1-indexed
}
return true;
}
return DynOS_Actor_GetModIndexAndTokenFromGfxData(DynOS_Lvl_GetActiveGfx(), aTokenIndex, outModIndex, outModFileIndex, outToken);
}
return false;
}
ActorGfx* DynOS_Actor_GetActorGfx(const GraphNode* aGraphNode) {
@ -214,13 +208,15 @@ void DynOS_Actor_Override(struct Object* obj, void** aSharedChild) {
if (it == _ValidActors.end()) { return; }
// Check if the behavior uses a character specific model
if (obj && (obj->behavior == bhvMario ||
obj->behavior == smlua_override_behavior(bhvNormalCap) ||
obj->behavior == smlua_override_behavior(bhvWingCap) ||
obj->behavior == smlua_override_behavior(bhvMetalCap) ||
obj->behavior == smlua_override_behavior(bhvVanishCap))) {
if (obj && (
obj->behavior == smlua_override_behavior(bhvMario) ||
obj->behavior == smlua_override_behavior(bhvNormalCap) ||
obj->behavior == smlua_override_behavior(bhvWingCap) ||
obj->behavior == smlua_override_behavior(bhvMetalCap) ||
obj->behavior == smlua_override_behavior(bhvVanishCap)
)) {
struct NetworkPlayer* np = network_player_from_global_index(obj->globalPlayerIndex);
if (np && np->localIndex > 0 && configDynosLocalPlayerModelOnly && it->second.mPackIndex != MOD_PACK_INDEX) {
if (np != NULL && np->localIndex > 0 && configDynosLocalPlayerModelOnly && it->second.mPackIndex != MOD_PACK_INDEX) {
return;
}
}
@ -228,10 +224,12 @@ void DynOS_Actor_Override(struct Object* obj, void** aSharedChild) {
*aSharedChild = (void*)it->second.mGraphNode;
}
// Used for both DynOS packs and actors from mods, only overrides existing actors
void DynOS_Actor_Override_All(void) {
if (!gObjectLists) { return; }
// Loop through all object lists
for (s32 list : { OBJ_LIST_PLAYER, OBJ_LIST_DESTRUCTIVE, OBJ_LIST_GENACTOR, OBJ_LIST_PUSHABLE, OBJ_LIST_LEVEL, OBJ_LIST_DEFAULT, OBJ_LIST_SURFACE, OBJ_LIST_POLELIKE, OBJ_LIST_UNIMPORTANT }) {
for (ObjectList list : sObjectListsToOverride) {
struct Object *_Head = (struct Object *) &gObjectLists[list];
for (struct Object *_Object = (struct Object *) _Head->header.next; _Object != _Head; _Object = (struct Object *) _Object->header.next) {
if (_Object->activeFlags && _Object->header.gfx.sharedChild != NULL) {

View file

@ -45,8 +45,16 @@ static s32 RetrieveCurrentAnimationIndex(struct Object *aObject) {
void DynOS_Anim_Swap(void *aPtr) {
if (!aPtr) { return; }
static Animation *pDefaultAnimation = NULL;
static Animation sGfxDataAnimation;
// Must support nested calls (held objects render inside other object render)
// and interleaving objects without corrupting swap state.
struct AnimSwapFrame {
struct Object *obj;
Animation *defaultAnim;
Animation gfxDataAnim;
};
static AnimSwapFrame sAnimSwapFrames[32] = { 0 };
static s32 sCurrAnimSwapIndex = 0;
// Does the object have a model?
struct Object *_Object = (struct Object *) aPtr;
@ -54,9 +62,20 @@ void DynOS_Anim_Swap(void *aPtr) {
return;
}
// Determine if this call is the "swap" phase or "restore" phase.
// The engine calls DynOS_Anim_Swap twice around geo_set_animation_globals.
const bool restoring = (sCurrAnimSwapIndex > 0 && sAnimSwapFrames[sCurrAnimSwapIndex - 1].obj == _Object);
// Swap the current animation with the one from the Gfx data
if (!pDefaultAnimation) {
pDefaultAnimation = _Object->header.gfx.animInfo.curAnim;
if (!restoring) {
if (sCurrAnimSwapIndex >= (s32) ARRAY_COUNT(sAnimSwapFrames)) {
return;
}
AnimSwapFrame *frame = &sAnimSwapFrames[sCurrAnimSwapIndex];
frame->obj = _Object;
frame->defaultAnim = _Object->header.gfx.animInfo.curAnim;
sCurrAnimSwapIndex++;
// ActorGfx data
ActorGfx* _ActorGfx = DynOS_Actor_GetActorGfx(_Object->header.gfx.sharedChild);
@ -100,23 +119,26 @@ void DynOS_Anim_Swap(void *aPtr) {
// Animation data
const AnimData *_AnimData = (const AnimData *) _GfxData->mAnimationTable[_AnimIndex].second;
if (_AnimData) {
sGfxDataAnimation.flags = _AnimData->mFlags;
sGfxDataAnimation.animYTransDivisor = _AnimData->mUnk02;
sGfxDataAnimation.startFrame = _AnimData->mUnk04;
sGfxDataAnimation.loopStart = _AnimData->mUnk06;
sGfxDataAnimation.loopEnd = _AnimData->mUnk08;
sGfxDataAnimation.unusedBoneCount = _AnimData->mUnk0A.second;
sGfxDataAnimation.values = (u16*)_AnimData->mValues.second.begin();
sGfxDataAnimation.index = (u16*)_AnimData->mIndex.second.begin();
sGfxDataAnimation.valuesLength = _AnimData->mValues.second.Count();
sGfxDataAnimation.indexLength = _AnimData->mIndex.second.Count();
sGfxDataAnimation.length = _AnimData->mLength;
_Object->header.gfx.animInfo.curAnim = &sGfxDataAnimation;
frame->gfxDataAnim.flags = _AnimData->mFlags;
frame->gfxDataAnim.animYTransDivisor = _AnimData->mUnk02;
frame->gfxDataAnim.startFrame = _AnimData->mUnk04;
frame->gfxDataAnim.loopStart = _AnimData->mUnk06;
frame->gfxDataAnim.loopEnd = _AnimData->mUnk08;
frame->gfxDataAnim.unusedBoneCount = _AnimData->mUnk0A.second;
frame->gfxDataAnim.values = (u16*) _AnimData->mValues.second.begin();
frame->gfxDataAnim.index = (u16*) _AnimData->mIndex.second.begin();
frame->gfxDataAnim.valuesLength = _AnimData->mValues.second.Count();
frame->gfxDataAnim.indexLength = _AnimData->mIndex.second.Count();
frame->gfxDataAnim.length = _AnimData->mLength;
_Object->header.gfx.animInfo.curAnim = &frame->gfxDataAnim;
}
// Restore the default animation
} else {
_Object->header.gfx.animInfo.curAnim = pDefaultAnimation;
pDefaultAnimation = NULL;
sCurrAnimSwapIndex--;
AnimSwapFrame *frame = &sAnimSwapFrames[sCurrAnimSwapIndex];
_Object->header.gfx.animInfo.curAnim = frame->defaultAnim;
frame->obj = NULL;
frame->defaultAnim = NULL;
}
}

View file

@ -3590,7 +3590,9 @@
| HOOK_ON_FIND_POISON_GAS_LEVEL | 64 |
| HOOK_ON_FIND_SURFACE_ON_RAY | 65 |
| HOOK_ON_DYNOS_PACK_TOGGLED | 66 |
| HOOK_MAX | 67 |
| HOOK_BEFORE_PLAY_MODE_UPDATE | 67 |
| HOOK_ON_PLAY_MODE_UPDATE | 68 |
| HOOK_MAX | 69 |
- MAX_HOOKED_BEHAVIORS
[:arrow_up_small:](#)
@ -4751,6 +4753,7 @@
- ANIM_FLAG_6
- ANIM_FLAG_7
- ANIM_FLAG_BONE_TRANS
- ANIM_FLAG_BONE_SCALE
- OBJECT_MAX_BHV_STACK
- OBJECT_NUM_REGULAR_FIELDS
- OBJECT_NUM_CUSTOM_FIELDS

View file

@ -143,7 +143,7 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh
| HOOK_ON_GEO_PROCESS | Called when a GeoLayout is processed **Note:** You must set the `hookProcess` field of the graph node to a non-zero value | [GraphNode](../structs.md#GraphNode) graphNode, `integer` matStackIndex |
| HOOK_BEFORE_GEO_PROCESS | Called before a GeoLayout is processed **Note:** You must set the `hookProcess` field of the graph node to a non-zero value | [GraphNode](../structs.md#GraphNode) graphNode, `integer` matStackIndex |
| HOOK_ON_GEO_PROCESS_CHILDREN | Called when the children of a GeoLayout node is processed **Note:** You must set the `hookProcess` field of the parent graph node to a non-zero value | [GraphNode](../structs.md#GraphNode) graphNode, `integer` matStackIndex |
| HOOK_MARIO_OVERRIDE_GEOMETRY_INPUTS | Called before running Mario's geometry input logic, return `false` to not run it. | [MarioState](../structs.md) m |
| HOOK_MARIO_OVERRIDE_GEOMETRY_INPUTS | Called before running Mario's geometry input logic, return `false` to not run it. | [MarioState](../structs.md) m |
| HOOK_ON_INTERACTIONS | Called when the Mario interactions are processed | [MarioState](../structs.md#MarioState) mario |
| HOOK_ALLOW_FORCE_WATER_ACTION | Called when executing a non-water action while under the water's surface, or vice versa. Return `false` to prevent the player from being forced out of the action at the water's surface | [MarioState](../structs.md#MarioState) mario, `boolean` isInWaterAction |
| HOOK_BEFORE_WARP | Called before the local player warps. Return a table with `destLevel`, `destArea`, `destWarpNode`, to override the warp | `integer` destLevel, `integer` destArea, `integer` destWarpNode, `integer` arg |
@ -158,6 +158,8 @@ The lua functions sent to `hook_event()` will be automatically called by SM64 wh
| HOOK_ON_FIND_POISON_GAS_LEVEL | Called after poison gas level detection completes. Return a number to override the gas level | `number` x, `number` z, `number` gasLevel |
| HOOK_ON_FIND_SURFACE_ON_RAY | Called after ray-surface intersection completes. Return `surface` to override the hit surface, or `surface, hitPos` to override both | `Vec3f` orig, `Vec3f` dir, [Surface](../structs.md#Surface) hitSurface, `Vec3f` hitPos |
| HOOK_ON_DYNOS_PACK_TOGGLED | Called after a DynOS pack is toggled | `string` dynosPackName, `boolean` enabled |
| HOOK_BEFORE_PLAY_MODE_UPDATE | Called before the play mode is ran. Return a number to override the play mode to be ran. | `number` playMode |
| HOOK_ON_PLAY_MODE_UPDATE | Called after the play mode is ran. Return a number to override the change level. | `number` playMode |
### Parameters

View file

@ -112,6 +112,7 @@ struct VblankHandler
#define ANIM_FLAG_6 (1 << 6) // 0x40
#define ANIM_FLAG_7 (1 << 7) // 0x80
#define ANIM_FLAG_BONE_TRANS (1 << 8)
#define ANIM_FLAG_BONE_SCALE (1 << 9)
struct Animation {
// TODO: Optimize this later if possible.

View file

@ -448,13 +448,15 @@ void play_transition_after_delay(s16 transType, s16 time, u8 red, u8 green, u8 b
void render_game(void) {
dynos_update_gfx();
if (gCurrentArea != NULL && !gWarpTransition.pauseRendering) {
geo_process_root(gCurrentArea->root, gViewportOverride, gViewportClip, gFBSetColor);
if (gCurrentArea != NULL) {
if (!gWarpTransition.pauseRendering) {
geo_process_root(gCurrentArea->root, gViewportOverride, gViewportClip, gFBSetColor);
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gViewportFullscreen));
gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gViewportFullscreen));
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
SCREEN_HEIGHT - BORDER_HEIGHT);
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
SCREEN_HEIGHT - BORDER_HEIGHT);
}
if (!gDjuiDisabled) {
djui_reset_hud_params();
@ -466,41 +468,45 @@ void render_game(void) {
smlua_call_event_hooks(HOOK_ON_HUD_RENDER_BEHIND, djui_reset_hud_params);
djui_gfx_displaylist_end();
}
render_hud();
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
render_text_labels();
do_cutscene_handler();
if (!gDjuiInMainMenu) {
print_displaying_credits_entry();
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
SCREEN_HEIGHT - BORDER_HEIGHT);
gPauseScreenMode = render_menus_and_dialogs();
if (!gWarpTransition.pauseRendering) {
render_hud();
if (gPauseScreenMode != 0) {
gSaveOptSelectIndex = gPauseScreenMode;
}
if (gViewportClip != NULL) {
make_viewport_clip_rect(gViewportClip);
} else
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
render_text_labels();
do_cutscene_handler();
if (!gDjuiInMainMenu) {
print_displaying_credits_entry();
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
SCREEN_HEIGHT - BORDER_HEIGHT);
gPauseScreenMode = render_menus_and_dialogs();
if (gWarpTransition.isActive) {
if (gWarpTransDelay == 0) {
gWarpTransition.isActive = !render_screen_transition(0, gWarpTransition.type, gWarpTransition.time,
&gWarpTransition.data);
if (!gWarpTransition.isActive) {
if (gWarpTransition.type & 1) {
gWarpTransition.pauseRendering = TRUE;
} else {
set_warp_transition_rgb(0, 0, 0);
}
}
if (gPauseScreenMode != 0) {
gSaveOptSelectIndex = gPauseScreenMode;
}
if (gViewportClip != NULL) {
make_viewport_clip_rect(gViewportClip);
} else {
gWarpTransDelay--;
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH,
SCREEN_HEIGHT - BORDER_HEIGHT);
}
if (gWarpTransition.isActive) {
if (gWarpTransDelay == 0) {
gWarpTransition.isActive = !render_screen_transition(0, gWarpTransition.type, gWarpTransition.time,
&gWarpTransition.data);
if (!gWarpTransition.isActive) {
if (gWarpTransition.type & 1) {
gWarpTransition.pauseRendering = TRUE;
} else {
set_warp_transition_rgb(0, 0, 0);
}
}
} else {
gWarpTransDelay--;
}
}
}
} else {

View file

@ -22,6 +22,7 @@ void bhv_bbh_tilting_trap_platform_loop(void) {
u8 playersTouched = 0;
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (!is_player_active(&gMarioStates[i])) { continue; }
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform == o) {
x += gMarioStates[i].marioObj->oPosX;
y += gMarioStates[i].marioObj->oPosY;

View file

@ -183,6 +183,7 @@ void controllable_platform_tilt_from_mario(void) {
f32 z = 0;
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform == o || gMarioStates[i].marioObj->platform == cur_obj_nearest_object_with_behavior(bhvControllablePlatformSub)) {
x += gMarioStates[i].pos[0];
z += gMarioStates[i].pos[2];

View file

@ -136,6 +136,7 @@ void dorrie_act_raise_head(void) {
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (!is_player_active(&gMarioStates[i])) { continue; }
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform != o) { continue; }
s32 dist = dist_between_objects(o, gMarioStates[0].marioObj);
if (dist <= 780.0f) { continue; }

View file

@ -23,6 +23,7 @@ void floating_platform_act_0(void) {
u8 playersTouched = 0;
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (!is_player_active(&gMarioStates[i])) { continue; }
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform == o) {
x += gMarioStates[i].marioObj->oPosX;
z += gMarioStates[i].marioObj->oPosZ;

View file

@ -291,6 +291,7 @@ static void platform_on_track_rock_ski_lift(void) {
struct Object* player = NULL;
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (!is_player_active(&gMarioStates[i])) { continue; }
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform != o) { continue; }
player = gMarioStates[i].marioObj;
break;

View file

@ -51,6 +51,7 @@ void bhv_seesaw_platform_update(void) {
u8 playersTouched = 0;
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (!is_player_active(&gMarioStates[i])) { continue; }
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform == o) {
x += gMarioStates[i].marioObj->oPosX;
y += gMarioStates[i].marioObj->oPosY;

View file

@ -87,6 +87,7 @@ void bhv_tilting_inverted_pyramid_loop(void) {
u8 playersTouched = 0;
for (s32 i = 0; i < MAX_PLAYERS; i++) {
if (!is_player_active(&gMarioStates[i])) { continue; }
if (gMarioStates[i].marioObj == NULL) { continue; }
if (gMarioStates[i].marioObj->platform != o) { continue; }
x += gMarioStates[i].marioObj->oPosX;
y += gMarioStates[i].marioObj->oPosY;
@ -135,7 +136,7 @@ void bhv_tilting_inverted_pyramid_loop(void) {
o->oTiltingPyramidMarioOnPlatform = FALSE;
}
// Approach the normals by 0.01f towards the new goal, then create a transform matrix and orient the object.
// Approach the normals by 0.01f towards the new goal, then create a transform matrix and orient the object.
// Outside of the other conditionals since it needs to tilt regardless of whether Mario is on.
o->oTiltingPyramidNormalX = approach_by_increment(dx, o->oTiltingPyramidNormalX, 0.01f);
o->oTiltingPyramidNormalY = approach_by_increment(dy, o->oTiltingPyramidNormalY, 0.01f);

View file

@ -1503,6 +1503,44 @@ UNUSED static s32 play_mode_unused(void) {
return 0;
}
s32 update_current_play_mode() {
s32 changeLevel = 0;
s16 hookPlaymode = sCurrPlayMode;
if (smlua_call_event_hooks(HOOK_BEFORE_PLAY_MODE_UPDATE, sCurrPlayMode, &hookPlaymode)) {
sCurrPlayMode = hookPlaymode;
}
switch (sCurrPlayMode) {
case PLAY_MODE_NORMAL:
changeLevel = play_mode_normal();
break;
case PLAY_MODE_PAUSED:
if (!network_check_singleplayer_pause()) {
changeLevel = play_mode_normal();
}
if (sCurrPlayMode == PLAY_MODE_PAUSED) {
changeLevel = play_mode_paused();
}
break;
case PLAY_MODE_CHANGE_AREA:
changeLevel = play_mode_change_area();
break;
case PLAY_MODE_CHANGE_LEVEL:
changeLevel = play_mode_change_level();
break;
case PLAY_MODE_FRAME_ADVANCE:
changeLevel = play_mode_frame_advance();
break;
}
s32 hookChangeLevel = changeLevel;
if (smlua_call_event_hooks(HOOK_ON_PLAY_MODE_UPDATE, sCurrPlayMode, &hookChangeLevel)) {
changeLevel = hookChangeLevel;
}
return changeLevel;
}
void update_menu_level(void) {
// figure out level
s32 curLevel = 0;
@ -1730,29 +1768,7 @@ s32 update_level(void) {
gCurrentArea->localAreaTimer++;
}
switch (sCurrPlayMode) {
case PLAY_MODE_NORMAL:
changeLevel = play_mode_normal();
break;
case PLAY_MODE_PAUSED:
if (!network_check_singleplayer_pause()) {
changeLevel = play_mode_normal();
}
if (sCurrPlayMode == PLAY_MODE_PAUSED) {
changeLevel = play_mode_paused();
}
break;
case PLAY_MODE_CHANGE_AREA:
changeLevel = play_mode_change_area();
break;
case PLAY_MODE_CHANGE_LEVEL:
changeLevel = play_mode_change_level();
break;
case PLAY_MODE_FRAME_ADVANCE:
changeLevel = play_mode_frame_advance();
break;
}
changeLevel = update_current_play_mode();
if (changeLevel) {
reset_volume();

View file

@ -383,7 +383,6 @@ void patch_mtx_interpolated(f32 delta) {
/**
* Graph node interpolation
*/
static void *sGraphNodeInterpDataMap = NULL;
struct GraphNodeInterpData *geo_get_interp_data(void *node, struct GraphNodeObject *obj) {
@ -1059,7 +1058,7 @@ static void geo_process_background(struct GraphNodeBackground *node) {
}
}
static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 animFrame, u16 **animAttribute) {
static void anim_process(Vec3f translation, Vec3s rotation, Vec3f scale, u8 *animType, s16 animFrame, u16 **animAttribute) {
if (*animType == ANIM_TYPE_TRANSLATION) {
translation[0] += retrieve_animation_value(gCurAnim, animFrame, animAttribute) * gCurAnimTranslationMultiplier;
translation[1] += retrieve_animation_value(gCurAnim, animFrame, animAttribute) * gCurAnimTranslationMultiplier;
@ -1090,6 +1089,19 @@ static void anim_process(Vec3f translation, Vec3s rotation, u8 *animType, s16 an
rotation[0] += retrieve_animation_value(gCurAnim, animFrame, animAttribute);
rotation[1] += retrieve_animation_value(gCurAnim, animFrame, animAttribute);
rotation[2] += retrieve_animation_value(gCurAnim, animFrame, animAttribute);
if (gCurAnim->flags & ANIM_FLAG_BONE_SCALE) {
s16 scaleX = retrieve_animation_value(gCurAnim, animFrame, animAttribute);
s16 scaleY = retrieve_animation_value(gCurAnim, animFrame, animAttribute);
s16 scaleZ = retrieve_animation_value(gCurAnim, animFrame, animAttribute);
if (scale != NULL) {
scale[0] *= ((f32) scaleX) / 256.0f;
scale[1] *= ((f32) scaleY) / 256.0f;
scale[2] *= ((f32) scaleZ) / 256.0f;
}
}
if (gCurAnim->flags & ANIM_FLAG_BONE_TRANS) {
*animType = ANIM_TYPE_TRANSLATION;
}
@ -1108,6 +1120,7 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
Mat4 matrix;
Vec3s rotation;
Vec3f translation;
Vec3f scale;
// Sanity check our stack index, If we above or equal to our stack size. Return to prevent OOB\.
if ((gMatStackIndex + 1) >= MATRIX_STACK_SIZE) { LOG_ERROR("Preventing attempt to exceed the maximum size %i for our matrix stack with size of %i.", MATRIX_STACK_SIZE - 1, gMatStackIndex); return; }
@ -1118,8 +1131,10 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
// current frame
vec3s_copy(rotation, gVec3sZero);
vec3s_to_vec3f(translation, node->translation);
anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
vec3f_copy(scale, gVec3fOne);
anim_process(translation, rotation, scale, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
// previous frame
@ -1130,8 +1145,10 @@ static void geo_process_animated_part(struct GraphNodeAnimatedPart *node) {
node->translation
);
vec3s_copy(rotation, gVec3sZero);
anim_process(translation, rotation, &animType, gPrevAnimFrame, &animAttribute);
vec3f_copy(scale, gVec3fOne);
anim_process(translation, rotation, scale, &animType, gPrevAnimFrame, &animAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);
);
@ -1420,8 +1437,12 @@ static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) {
static void geo_sanitize_object_gfx(void) {
geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE);
geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE_DECAL);
geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE_INTER);
geo_append_display_list(obj_sanitize_gfx, LAYER_ALPHA);
geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT);
geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT_DECAL);
geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT_INTER);
}
static struct MarioBodyState *get_mario_body_state_from_mario_object(struct Object *marioObj) {
@ -1688,6 +1709,7 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
node->fnNode.func(GEO_CONTEXT_HELD_OBJ, &node->fnNode.node, (struct DynamicPool *) gMatStack[gMatStackIndex + 1]);
}
s32 savedMatStackIndex = gMatStackIndex;
// Increment the matrix stack, If we fail to do so. Just return.
if (!increment_mat_stack()) { return; }
@ -1708,7 +1730,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
}
geo_sanitize_object_gfx();
// While rendering the held object's geo tree, ensure "current object" globals
// refer to the held object, otherwise Lua geo callbacks can accidentally
// mutate the holder's render state (e.g. make Wario limbs disappear).
struct GraphNodeObject *savedCurGraphNodeObject = gCurGraphNodeObject;
gCurGraphNodeObject = &node->objNode->header.gfx;
geo_process_node_and_siblings(node->objNode->header.gfx.sharedChild);
gCurGraphNodeObject = savedCurGraphNodeObject;
gCurGraphNodeHeldObject = NULL;
gCurAnimType = gGeoTempState.type;
gCurAnimEnabled = gGeoTempState.enabled;
@ -1717,7 +1745,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) {
gCurrAnimAttribute = gGeoTempState.attribute;
gCurAnim = gGeoTempState.anim;
gPrevAnimFrame = gGeoTempState.prevFrame;
gMatStackIndex--;
// Force-restore matrix stack index to avoid any imbalance caused by
// held object geo trees (including Lua geo callbacks).
gMatStackIndex = savedMatStackIndex;
// Reset any render-state changes performed by the held object's geo tree
// so the holder's remaining body parts render correctly.
geo_sanitize_object_gfx();
}
if (node->fnNode.node.children != NULL) {
@ -1748,7 +1782,7 @@ static void geo_process_bone(struct GraphNodeBone *node) {
vec3s_copy(rotation, node->rotation);
vec3s_to_vec3f(translation, node->translation);
vec3f_copy(scale, node->scale);
anim_process(translation, rotation, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
anim_process(translation, rotation, scale, &gCurAnimType, gCurrAnimFrame, &gCurrAnimAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStack[gMatStackIndex + 1], matrix, gMatStack[gMatStackIndex]);
@ -1764,7 +1798,7 @@ static void geo_process_bone(struct GraphNodeBone *node) {
vec3s_to_vec3f(translation, node->translation);
vec3f_copy(scale, node->scale);
}
anim_process(translation, rotation, &animType, gPrevAnimFrame, &animAttribute);
anim_process(translation, rotation, scale, &animType, gPrevAnimFrame, &animAttribute);
mtxf_rotate_xyz_and_translate(matrix, translation, rotation);
mtxf_scale_vec3f(matrix, matrix, scale);
mtxf_mul(gMatStackPrev[gMatStackIndex + 1], matrix, gMatStackPrev[gMatStackIndex]);

View file

@ -3575,7 +3575,9 @@ char gSmluaConstants[] = ""
"HOOK_ON_FIND_POISON_GAS_LEVEL=64\n"
"HOOK_ON_FIND_SURFACE_ON_RAY=65\n"
"HOOK_ON_DYNOS_PACK_TOGGLED=66\n"
"HOOK_MAX=67\n"
"HOOK_BEFORE_PLAY_MODE_UPDATE=67\n"
"HOOK_ON_PLAY_MODE_UPDATE=68\n"
"HOOK_MAX=69\n"
"MAX_HOOKED_BEHAVIORS=1024\n"
"HUD_DISPLAY_LIVES=0\n"
"HUD_DISPLAY_COINS=1\n"
@ -4675,6 +4677,7 @@ char gSmluaConstants[] = ""
"ANIM_FLAG_6=(1 << 6)\n"
"ANIM_FLAG_7=(1 << 7)\n"
"ANIM_FLAG_BONE_TRANS=(1 << 8)\n"
"ANIM_FLAG_BONE_SCALE=(1 << 9)\n"
"OBJECT_MAX_BHV_STACK=16\n"
"OBJECT_NUM_REGULAR_FIELDS=0x50\n"
"OBJECT_NUM_CUSTOM_FIELDS=0x40\n"

View file

@ -65,3 +65,5 @@ SMLUA_EVENT_HOOK(HOOK_ON_FIND_WATER_LEVEL, _, f32 x, f32 z, f32 *waterLevel) //
SMLUA_EVENT_HOOK(HOOK_ON_FIND_POISON_GAS_LEVEL, _, f32 x, f32 z, f32 *gasLevel) // Manually defined hook
SMLUA_EVENT_HOOK(HOOK_ON_FIND_SURFACE_ON_RAY, _, Vec3f orig, Vec3f dir, struct Surface **hit_surface, Vec3f hit_pos) // Manually defined hook
SMLUA_EVENT_HOOK(HOOK_ON_DYNOS_PACK_TOGGLED, HOOK_RETURN_NEVER, const char *dynosPackName, bool enabled)
SMLUA_EVENT_HOOK(HOOK_BEFORE_PLAY_MODE_UPDATE, HOOK_RETURN_NEVER, s16 playmode, OUTPUT s16 *overridePlaymode)
SMLUA_EVENT_HOOK(HOOK_ON_PLAY_MODE_UPDATE, HOOK_RETURN_NEVER, s16 playmode, OUTPUT s32 *changeLevel)

View file

@ -1893,3 +1893,67 @@ bool smlua_call_event_hooks_HOOK_ON_DYNOS_PACK_TOGGLED(const char *dynosPackName
}
return hookResult;
}
bool smlua_call_event_hooks_HOOK_BEFORE_PLAY_MODE_UPDATE(s16 playmode, s16 *overridePlaymode) {
lua_State *L = gLuaState;
if (L == NULL) { return false; }
bool hookResult = false;
struct LuaHookedEvent *hook = &sHookedEvents[HOOK_BEFORE_PLAY_MODE_UPDATE];
for (int i = 0; i < hook->count; i++) {
s32 prevTop = lua_gettop(L);
// push the callback onto the stack
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
// push playmode
lua_pushinteger(L, playmode);
// call the callback
if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) {
LOG_LUA("Failed to call the callback for hook %s - '%s/%s'", sLuaHookedEventTypeName[HOOK_BEFORE_PLAY_MODE_UPDATE], hook->mod[i]->relativePath, hook->modFile[i]->relativePath);
continue;
}
hookResult = true;
// return overridePlaymode
if (lua_type(L, -1) == LUA_TNUMBER) {
*overridePlaymode = smlua_to_integer(L, -1);
}
lua_settop(L, prevTop);
}
return hookResult;
}
bool smlua_call_event_hooks_HOOK_ON_PLAY_MODE_UPDATE(s16 playmode, s32 *changeLevel) {
lua_State *L = gLuaState;
if (L == NULL) { return false; }
bool hookResult = false;
struct LuaHookedEvent *hook = &sHookedEvents[HOOK_ON_PLAY_MODE_UPDATE];
for (int i = 0; i < hook->count; i++) {
s32 prevTop = lua_gettop(L);
// push the callback onto the stack
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
// push playmode
lua_pushinteger(L, playmode);
// call the callback
if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i], hook->modFile[i])) {
LOG_LUA("Failed to call the callback for hook %s - '%s/%s'", sLuaHookedEventTypeName[HOOK_ON_PLAY_MODE_UPDATE], hook->mod[i]->relativePath, hook->modFile[i]->relativePath);
continue;
}
hookResult = true;
// return changeLevel
if (lua_type(L, -1) == LUA_TNUMBER) {
*changeLevel = smlua_to_integer(L, -1);
}
lua_settop(L, prevTop);
}
return hookResult;
}

View file

@ -83,6 +83,8 @@ enum LuaHookedEventType {
HOOK_ON_FIND_POISON_GAS_LEVEL,
HOOK_ON_FIND_SURFACE_ON_RAY,
HOOK_ON_DYNOS_PACK_TOGGLED,
HOOK_BEFORE_PLAY_MODE_UPDATE,
HOOK_ON_PLAY_MODE_UPDATE,
HOOK_MAX,
};