mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 05:25:14 +00:00
c9e4efdb31
clean up custom level code fixed a bug where custom level course numbers weren't used by dynos warps removed a bunch of unused dynos code fix demos triggering incorrectly allowed the right Ctrl key to be used when opening the in game console fixed a softlock that was possible to experience when talking to the snowman in CCM fixed the bug where you can permanently lose your cap (bug created by my own PR from beta 32) fix the moderator feature I made a while back; I am amazed it even worked at all before fixed dynos warp initial actions being skipped (read ec8aabc for explanation) completely changed the way star names and course names work
443 lines
14 KiB
C++
443 lines
14 KiB
C++
#include "dynos.cpp.h"
|
|
extern "C" {
|
|
#include "game/segment2.h"
|
|
#include "game/save_file.h"
|
|
#include "levels/scripts.h"
|
|
#include "pc/lua/utils/smlua_level_utils.h"
|
|
|
|
}
|
|
|
|
//
|
|
// Const
|
|
//
|
|
|
|
extern "C" {
|
|
extern const BehaviorScript *sWarpBhvSpawnTable[];
|
|
#include "engine/level_script.h"
|
|
}
|
|
|
|
//
|
|
// Data
|
|
//
|
|
|
|
struct DynosWarp {
|
|
/* 0 */ s16 mArea = 0;
|
|
/* 1 */ s16 mId = 0;
|
|
/* 2 */ s16 mType = -1;
|
|
/* 3 */ s16 mPosX = 0;
|
|
/* 4 */ s16 mPosY = 0;
|
|
/* 5 */ s16 mPosZ = 0;
|
|
/* 6 */ s16 mAngle = 0;
|
|
/* 7 */ s16 mDestLevel = 0;
|
|
/* 8 */ s16 mDestArea = 0;
|
|
/* 9 */ s16 mDestId = 0;
|
|
};
|
|
|
|
struct DynosLevelScript {
|
|
void *mLevelScript;
|
|
s32 mModIndex;
|
|
};
|
|
|
|
#define DYNOS_LEVEL_MOD_INDEX_VANILLA (-1)
|
|
|
|
static DynosLevelScript sDynosLevelScripts[LEVEL_COUNT] = { { NULL, DYNOS_LEVEL_MOD_INDEX_VANILLA } };
|
|
extern void *gDynosLevelScriptsOriginal[LEVEL_COUNT];
|
|
static Array<DynosWarp> sDynosLevelWarps[LEVEL_COUNT] = { Array<DynosWarp>() };
|
|
|
|
u64 DynOS_Level_CmdGet(void *aCmd, u64 aOffset) {
|
|
u64 _Offset = (((aOffset) & 3llu) | (((aOffset) & ~3llu) << (sizeof(void *) >> 3llu)));
|
|
return *((u64 *) (u64(aCmd) + _Offset));
|
|
}
|
|
|
|
LvlCmd *DynOS_Level_CmdNext(LvlCmd *aCmd) {
|
|
u64 aCmdSize = aCmd->mSize;
|
|
u64 _Offset = (((aCmdSize) & 3llu) | (((aCmdSize) & ~3llu) << (sizeof(void *) >> 3llu)));
|
|
return (LvlCmd*) (u64(aCmd) + _Offset);
|
|
}
|
|
|
|
void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFunction)(u8, void *));
|
|
|
|
//
|
|
// Init
|
|
//
|
|
|
|
static s32 DynOS_Level_PreprocessMasterScript(u8 aType, void *aCmd) {
|
|
static bool sDynosScriptExecLevelTable = false;
|
|
static s32 sDynosLevelNum = -1;
|
|
|
|
if (sDynosScriptExecLevelTable) {
|
|
|
|
// JUMP_IF
|
|
if (aType == 0x0C) {
|
|
sDynosLevelNum = (s32) DynOS_Level_CmdGet(aCmd, 0x04);
|
|
return 0;
|
|
}
|
|
|
|
// EXECUTE
|
|
if (aType == 0x00) {
|
|
void *_Script = (void *) DynOS_Level_CmdGet(aCmd, 0x0C);
|
|
if (sDynosLevelNum >= 0 && sDynosLevelNum < LEVEL_COUNT && !sDynosLevelScripts[sDynosLevelNum].mLevelScript) {
|
|
sDynosLevelScripts[sDynosLevelNum].mLevelScript = _Script;
|
|
sDynosLevelScripts[sDynosLevelNum].mModIndex = DYNOS_LEVEL_MOD_INDEX_VANILLA;
|
|
gDynosLevelScriptsOriginal[sDynosLevelNum] = _Script;
|
|
}
|
|
sDynosLevelNum = -1;
|
|
return 2;
|
|
}
|
|
|
|
// EXIT or SLEEP
|
|
if (aType == 0x02 || aType == 0x03) {
|
|
return 3;
|
|
}
|
|
} else if (aType == 0x06) { // JUMP_LINK
|
|
sDynosScriptExecLevelTable = true;
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static s32 sDynosCurrentLevelNum;
|
|
static s32 DynOS_Level_PreprocessScript(u8 aType, void *aCmd) {
|
|
static u8 sDynosAreaIndex = 0;
|
|
static auto _GetWarpStruct = [](u8 aArea, u8 aId) -> DynosWarp * {
|
|
for (s32 i = 0; i != sDynosLevelWarps[sDynosCurrentLevelNum].Count(); ++i) {
|
|
if (sDynosLevelWarps[sDynosCurrentLevelNum][i].mArea == aArea &&
|
|
sDynosLevelWarps[sDynosCurrentLevelNum][i].mId == aId) {
|
|
return &sDynosLevelWarps[sDynosCurrentLevelNum][i];
|
|
}
|
|
}
|
|
DynosWarp _Warp;
|
|
_Warp.mArea = aArea;
|
|
_Warp.mId = aId;
|
|
sDynosLevelWarps[sDynosCurrentLevelNum].Add(_Warp);
|
|
return &sDynosLevelWarps[sDynosCurrentLevelNum][sDynosLevelWarps[sDynosCurrentLevelNum].Count() - 1];
|
|
};
|
|
|
|
// AREA
|
|
if (aType == 0x1F) {
|
|
sDynosAreaIndex = (u8) DynOS_Level_CmdGet(aCmd, 2);
|
|
}
|
|
|
|
// OBJECT
|
|
else if (aType == 0x24) {
|
|
const BehaviorScript *bhv = (const BehaviorScript *) DynOS_Level_CmdGet(aCmd, 20);
|
|
for (s32 i = 0; i < 20; ++i) {
|
|
if (sWarpBhvSpawnTable[i] == bhv) {
|
|
DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, ((((u32) DynOS_Level_CmdGet(aCmd, 16)) >> 16) & 0xFF));
|
|
if (_Warp->mType == -1) {
|
|
_Warp->mType = i;
|
|
_Warp->mPosX = (s16) DynOS_Level_CmdGet(aCmd, 4);
|
|
_Warp->mPosY = (s16) DynOS_Level_CmdGet(aCmd, 6);
|
|
_Warp->mPosZ = (s16) DynOS_Level_CmdGet(aCmd, 8);
|
|
_Warp->mAngle = (s16)((((s32)((s16) DynOS_Level_CmdGet(aCmd, 12))) * 0x8000) / 180);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// WARP_NODE
|
|
else if (aType == 0x26) {
|
|
DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, (u8) DynOS_Level_CmdGet(aCmd, 2));
|
|
if (_Warp->mDestLevel == 0) {
|
|
_Warp->mDestLevel = (u8) DynOS_Level_CmdGet(aCmd, 3);
|
|
_Warp->mDestArea = (u8) DynOS_Level_CmdGet(aCmd, 4);
|
|
_Warp->mDestId = (u8) DynOS_Level_CmdGet(aCmd, 5);
|
|
}
|
|
}
|
|
|
|
// PAINTING_WARP_NODE
|
|
else if (aType == 0x27) {
|
|
DynosWarp *_Warp = _GetWarpStruct(sDynosAreaIndex, (u8) DynOS_Level_CmdGet(aCmd, 2));
|
|
if (_Warp->mDestLevel == 0) {
|
|
_Warp->mDestLevel = (u8) DynOS_Level_CmdGet(aCmd, 3);
|
|
_Warp->mDestArea = (u8) DynOS_Level_CmdGet(aCmd, 4);
|
|
_Warp->mDestId = (u8) DynOS_Level_CmdGet(aCmd, 5);
|
|
}
|
|
}
|
|
|
|
// SLEEP or SLEEP_BEFORE_EXIT
|
|
else if (aType == 0x03 || aType == 0x04) {
|
|
return 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Runs only once
|
|
void DynOS_Level_Init() {
|
|
static bool sInited = false;
|
|
if (!sInited) {
|
|
|
|
// Level warps
|
|
for (sDynosCurrentLevelNum = 0; sDynosCurrentLevelNum != LEVEL_COUNT; ++sDynosCurrentLevelNum) {
|
|
sDynosLevelScripts[sDynosCurrentLevelNum].mLevelScript = gDynosLevelScriptsOriginal[sDynosCurrentLevelNum];
|
|
sDynosLevelScripts[sDynosCurrentLevelNum].mModIndex = DYNOS_LEVEL_MOD_INDEX_VANILLA;
|
|
if (sDynosLevelScripts[sDynosCurrentLevelNum].mLevelScript) {
|
|
DynOS_Level_ParseScript(sDynosLevelScripts[sDynosCurrentLevelNum].mLevelScript, DynOS_Level_PreprocessScript);
|
|
}
|
|
}
|
|
|
|
// Done
|
|
sInited = true;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Common
|
|
//
|
|
|
|
s8 DynOS_Level_GetCourse(s32 aLevel) {
|
|
return get_level_course_num(aLevel);
|
|
}
|
|
|
|
void DynOS_Level_Override(void* originalScript, void* newScript, s32 modIndex) {
|
|
for (s32 i = 0; i < LEVEL_COUNT; i++) {
|
|
if (sDynosLevelScripts[i].mLevelScript == originalScript) {
|
|
sDynosCurrentLevelNum = i;
|
|
sDynosLevelWarps[i].Clear();
|
|
DynOS_Level_ParseScript(newScript, DynOS_Level_PreprocessScript);
|
|
sDynosLevelScripts[i].mLevelScript = newScript;
|
|
sDynosLevelScripts[i].mModIndex = modIndex;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DynOS_Level_Unoverride() {
|
|
for (s32 i = 0; i < LEVEL_COUNT; i++) {
|
|
sDynosCurrentLevelNum = i;
|
|
sDynosLevelWarps[i].Clear();
|
|
sDynosLevelScripts[i].mLevelScript = gDynosLevelScriptsOriginal[i];
|
|
sDynosLevelScripts[i].mModIndex = DYNOS_LEVEL_MOD_INDEX_VANILLA;
|
|
DynOS_Level_ParseScript(sDynosLevelScripts[i].mLevelScript, DynOS_Level_PreprocessScript);
|
|
}
|
|
}
|
|
|
|
const void *DynOS_Level_GetScript(s32 aLevel) {
|
|
if (aLevel >= CUSTOM_LEVEL_NUM_START) {
|
|
struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel);
|
|
if (!info || !info->script) { return NULL; }
|
|
return info->script;
|
|
}
|
|
|
|
DynOS_Level_Init();
|
|
return sDynosLevelScripts[aLevel].mLevelScript;
|
|
}
|
|
|
|
s32 DynOS_Level_GetModIndex(s32 aLevel) {
|
|
if (aLevel >= CUSTOM_LEVEL_NUM_START) {
|
|
struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel);
|
|
if (!info || !info->script) { return DYNOS_LEVEL_MOD_INDEX_VANILLA; }
|
|
return info->modIndex;
|
|
}
|
|
|
|
DynOS_Level_Init();
|
|
return sDynosLevelScripts[aLevel].mModIndex;
|
|
}
|
|
|
|
bool DynOS_Level_IsVanillaLevel(s32 aLevel) {
|
|
DynOS_Level_Init();
|
|
|
|
if (aLevel >= LEVEL_MIN && aLevel < LEVEL_COUNT) {
|
|
return sDynosLevelScripts[aLevel].mLevelScript == gDynosLevelScriptsOriginal[aLevel];
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Level Script Preprocessing
|
|
// - Ifs are always true
|
|
// - Skips are always false
|
|
// - Loops break after the first loop
|
|
//
|
|
|
|
struct Stack {
|
|
u64 mData[32];
|
|
s32 mBaseIndex;
|
|
s32 mTopIndex;
|
|
};
|
|
|
|
template <typename T>
|
|
static void StackPush(Stack& aStack, const T &aValue) {
|
|
if (aStack.mTopIndex >= 0) {
|
|
aStack.mData[aStack.mTopIndex] = u64(aValue);
|
|
aStack.mTopIndex++;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
static T StackPop(Stack& aStack) {
|
|
if (aStack.mTopIndex <= 0) {
|
|
return (T) 0;
|
|
}
|
|
aStack.mTopIndex--;
|
|
return (T) aStack.mData[aStack.mTopIndex];
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdExecute(Stack &aStack, LvlCmd *aCmd) {
|
|
StackPush(aStack, DynOS_Level_CmdNext(aCmd));
|
|
StackPush(aStack, aStack.mBaseIndex);
|
|
aStack.mBaseIndex = aStack.mTopIndex;
|
|
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 12);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdExitAndExecute(Stack &aStack, LvlCmd *aCmd) {
|
|
aStack.mTopIndex = aStack.mBaseIndex;
|
|
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 12);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdExit(Stack &aStack, LvlCmd *aCmd) {
|
|
aStack.mTopIndex = aStack.mBaseIndex;
|
|
aStack.mBaseIndex = StackPop<s32>(aStack);
|
|
return StackPop<LvlCmd *>(aStack);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJump(Stack &aStack, LvlCmd *aCmd) {
|
|
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 4);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJumpLink(Stack &aStack, LvlCmd *aCmd) {
|
|
StackPush(aStack, DynOS_Level_CmdNext(aCmd));
|
|
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 4);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdReturn(Stack &aStack, UNUSED LvlCmd *aCmd) {
|
|
return StackPop<LvlCmd *>(aStack);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJumpLinkPushArg(Stack &aStack, LvlCmd *aCmd) {
|
|
StackPush(aStack, DynOS_Level_CmdNext(aCmd));
|
|
StackPush(aStack, DynOS_Level_CmdGet(aCmd, 2));
|
|
return DynOS_Level_CmdNext(aCmd);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJumpRepeat(Stack &aStack, LvlCmd *aCmd) {
|
|
aStack.mTopIndex -= 2;
|
|
return DynOS_Level_CmdNext(aCmd);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdLoopBegin(Stack &aStack, LvlCmd *aCmd) {
|
|
StackPush(aStack, DynOS_Level_CmdNext(aCmd));
|
|
StackPush(aStack, 0);
|
|
return DynOS_Level_CmdNext(aCmd);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdLoopUntil(Stack &aStack, LvlCmd *aCmd) {
|
|
aStack.mTopIndex -= 2;
|
|
return DynOS_Level_CmdNext(aCmd);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJumpIf(Stack &aStack, LvlCmd *aCmd) {
|
|
StackPush(aStack, DynOS_Level_CmdNext(aCmd)); /* Not an error, that's intentional */
|
|
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 8);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJumpLinkIf(Stack &aStack, LvlCmd *aCmd) {
|
|
StackPush(aStack, DynOS_Level_CmdNext(aCmd));
|
|
return (LvlCmd *) DynOS_Level_CmdGet(aCmd, 8);
|
|
}
|
|
|
|
static LvlCmd *DynOS_Level_CmdJumpArea(Stack &aStack, LvlCmd *aCmd, s32 (*aPreprocessFunction)(u8, void *)) {
|
|
DynOS_Level_ParseScript((const void *) DynOS_Level_CmdGet(aCmd, 8), aPreprocessFunction);
|
|
return DynOS_Level_CmdNext(aCmd);
|
|
}
|
|
|
|
void DynOS_Level_ParseScript(const void *aScript, s32 (*aPreprocessFunction)(u8, void *)) {
|
|
Stack _Stack;
|
|
_Stack.mBaseIndex = -1;
|
|
_Stack.mTopIndex = 0;
|
|
for (LvlCmd *_Cmd = (LvlCmd *) aScript; _Cmd != NULL;) {
|
|
u8 _CmdType = (_Cmd->mType & 0xFF);
|
|
s32 _Action = aPreprocessFunction(_CmdType, (void *) _Cmd);
|
|
switch (_Action) {
|
|
case 0:
|
|
switch (_CmdType) {
|
|
case 0x00: _Cmd = DynOS_Level_CmdExecute(_Stack, _Cmd); break;
|
|
case 0x01: _Cmd = DynOS_Level_CmdExitAndExecute(_Stack, _Cmd); break;
|
|
case 0x02: _Cmd = DynOS_Level_CmdExit(_Stack, _Cmd); break;
|
|
case 0x05: _Cmd = DynOS_Level_CmdJump(_Stack, _Cmd); break;
|
|
case 0x06: _Cmd = DynOS_Level_CmdJumpLink(_Stack, _Cmd); break;
|
|
case 0x07: _Cmd = DynOS_Level_CmdReturn(_Stack, _Cmd); break;
|
|
case 0x08: _Cmd = DynOS_Level_CmdJumpLinkPushArg(_Stack, _Cmd); break;
|
|
case 0x09: _Cmd = DynOS_Level_CmdJumpRepeat(_Stack, _Cmd); break;
|
|
case 0x0A: _Cmd = DynOS_Level_CmdLoopBegin(_Stack, _Cmd); break;
|
|
case 0x0B: _Cmd = DynOS_Level_CmdLoopUntil(_Stack, _Cmd); break;
|
|
case 0x0C: _Cmd = DynOS_Level_CmdJumpIf(_Stack, _Cmd); break;
|
|
case 0x0D: _Cmd = DynOS_Level_CmdJumpLinkIf(_Stack, _Cmd); break;
|
|
|
|
// coop
|
|
case 0x42: _Cmd = DynOS_Level_CmdJumpArea(_Stack, _Cmd, aPreprocessFunction); break;
|
|
|
|
default: _Cmd = DynOS_Level_CmdNext(_Cmd); break;
|
|
} break;
|
|
|
|
case 1:
|
|
_Cmd = DynOS_Level_CmdNext(_Cmd);
|
|
break;
|
|
|
|
case 2:
|
|
_Cmd = DynOS_Level_CmdReturn(_Stack, _Cmd);
|
|
break;
|
|
|
|
case 3:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Level Script Utilities
|
|
//
|
|
|
|
s16 *DynOS_Level_GetWarp(s32 aLevel, s32 aArea, u8 aWarpId) {
|
|
if (aLevel >= CUSTOM_LEVEL_NUM_START) {
|
|
struct CustomLevelInfo* info = smlua_level_util_get_info(aLevel);
|
|
if (!info || !info->script) { return NULL; }
|
|
sDynosCurrentLevelNum = 1;
|
|
DynOS_Level_ParseScript(info->script, DynOS_Level_PreprocessScript);
|
|
for (const auto &_Warp : sDynosLevelWarps[1]) {
|
|
if (_Warp.mArea == aArea && _Warp.mId == aWarpId) {
|
|
return (s16 *) &_Warp;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
DynOS_Level_Init();
|
|
if (aLevel >= 0 && aLevel < LEVEL_COUNT) {
|
|
for (const auto &_Warp : sDynosLevelWarps[aLevel]) {
|
|
if (_Warp.mArea == aArea && _Warp.mId == aWarpId) {
|
|
return (s16 *) &_Warp;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
s16 *DynOS_Level_GetWarpEntry(s32 aLevel, s32 aArea) {
|
|
DynOS_Level_Init();
|
|
|
|
// override vanilla castle warps
|
|
if (DynOS_Level_GetCourse(aLevel) == COURSE_NONE && aLevel >= 0 && aLevel < LEVEL_COUNT) {
|
|
extern const LevelScript level_castle_grounds_entry[];
|
|
extern const LevelScript level_castle_inside_entry[];
|
|
extern const LevelScript level_castle_courtyard_entry[];
|
|
if (sDynosLevelScripts[aLevel].mLevelScript == level_castle_inside_entry) {
|
|
return DynOS_Level_GetWarp(aLevel, aArea, (aArea == 3) ? 0x00 : 0x01);
|
|
} else if (sDynosLevelScripts[aLevel].mLevelScript == level_castle_grounds_entry) {
|
|
return DynOS_Level_GetWarp(aLevel, aArea, 0x00);
|
|
} else if (sDynosLevelScripts[aLevel].mLevelScript == level_castle_courtyard_entry) {
|
|
return DynOS_Level_GetWarp(aLevel, aArea, 0x01);
|
|
}
|
|
}
|
|
|
|
return DynOS_Level_GetWarp(aLevel, aArea, 0x0A);
|
|
}
|
|
|
|
s16 *DynOS_Level_GetWarpDeath(s32 aLevel, s32 aArea) {
|
|
DynOS_Level_Init();
|
|
s16 *_Warp = DynOS_Level_GetWarp(aLevel, aArea, 0xF1);
|
|
if (!_Warp) _Warp = DynOS_Level_GetWarp(aLevel, aArea, 0xF3);
|
|
return _Warp;
|
|
}
|