Skip to content

Commit

Permalink
Release 2.3.1 (#16)
Browse files Browse the repository at this point in the history
This release introduces the following bug fixes and new features:

* [bugfix] Fix stage classification for Metal Cap stage.
* [bugfix] Fixes invalid splitting when opening moat door.
* [bugfix] Fixes parsing of split name when sub splits are used.
* [bugfix] Fixes handing of usamune's non-stop mode which is different from the GameShark code distributed.
* [bugfix] Fixes key grab splitting logic for non-stop.
* [new feature] Introduces start on file select in addition to start on logo's first frame.
  • Loading branch information
fmichea authored Sep 17, 2022
1 parent 280d641 commit 0ce7ede
Showing 1 changed file with 108 additions and 9 deletions.
117 changes: 108 additions & 9 deletions sm64-livesplit-autosplitter.asl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Version: 2.3.0
// Version: 2.3.1

// Code: https://github.com/n64decomp/sm64/
// Address map: https://github.com/SM64-TAS-ABC/STROOP/tree/Development/STROOP/Mappings
Expand Down Expand Up @@ -43,9 +43,28 @@ state("Project64") {
byte warpDestinationJP : "Project64.exe", 0xD6A1C, 0x339EDA; // N64 addr: 0x80339ED8 + 0x4 (struct field)
byte warpDestinationUS : "Project64.exe", 0xD6A1C, 0x33B24A; // N64 addr: 0x8033B248 + 0x4 (struct field)

// Non-stop code writes 2400 to these addresses when enabled, changing star grab interactions.
// Files from gSaveBuffer, each file is 0x70 apart.
uint fileAFlagsJP : "Project64.exe", 0xD6A1C, 0x207B08;
uint fileAFlagsUS : "Project64.exe", 0xD6A1C, 0x207708;

uint fileBFlagsJP : "Project64.exe", 0xD6A1C, 0x207B78;
uint fileBFlagsUS : "Project64.exe", 0xD6A1C, 0x207778;

uint fileCFlagsJP : "Project64.exe", 0xD6A1C, 0x207BE8;
uint fileCFlagsUS : "Project64.exe", 0xD6A1C, 0x2077E8;

uint fileDFlagsJP : "Project64.exe", 0xD6A1C, 0x207C58;
uint fileDFlagsUS : "Project64.exe", 0xD6A1C, 0x207858;

// Non-stop code writes 0/2400 to this address when enabled, changing interactions.
ushort nonStopInteractionOverwriteJP : "Project64.exe", 0xD6A1C, 0x24DC1E; // N64 addr: 0x8024DC1C (found in Gameshark Code).
ushort nonStopInteractionOverwriteUS : "Project64.exe", 0xD6A1C, 0x24DDBE; // N64 addr: 0x8024DDBC (found in Gameshark Code).

byte menuSelectedButtonIDJP : "Project64.exe", 0xD6A1C, 0x1A7BD3; // N64 addr: 0x801A7BD0
byte menuSelectedButtonIDUS : "Project64.exe", 0xD6A1C, 0x1A7D13; // N64 addr: 0x801A7D10

short menuClickPosJP : "Project64.exe", 0xD6A1C, 0x1A7BE8;
short menuClickPosUS : "Project64.exe", 0xD6A1C, 0x1A7D28;
}

startup {
Expand Down Expand Up @@ -181,6 +200,7 @@ startup {
STAGE_INDEXES[WDW_STAGE_INDEX] = true;
STAGE_INDEXES[WF_STAGE_INDEX] = true;
STAGE_INDEXES[WMOTR_STAGE_INDEX] = true;
STAGE_INDEXES[COTMC_STAGE_INDEX] = true;

bool[] CASTLE_STAGE_INDEXES = new bool[0x100];
CASTLE_STAGE_INDEXES[CASTLE_INSIDE_STAGE_INDEX] = true;
Expand Down Expand Up @@ -226,6 +246,7 @@ startup {
string DISABLE_RESET_AFTER_END = "disableResetAfterEnd";
string DISABLE_RTA_MODE = "disableRTAMode";
string DISABLE_BOWSER_REDS_DELAYED_SPLIT = "disableBowserRedsDelayedSplit";
string DISABLE_AUTO_START_ON_FILE_D = "disableAutoStartOnFileD";

// Splitting time is controlled depending on the type of split this is.
int SPLIT_TYPE_MANUAL = -1;
Expand Down Expand Up @@ -265,7 +286,10 @@ startup {
ushort FIXED_CAMERA_HUD = 0x4;
ushort FIXED_CAMERA_CDOWN_HUD = 0xC;

ushort NON_STOP_OVERWRITE_VALUE = 0x2400;
ushort NON_STOP_OVERWRITE_VALUE_GAMESHARK = 0x2400;
ushort NON_STOP_OVERWRITE_VALUE_USAMUNE = 0x0;

uint KEY_FLAGS = 0x10 | 0x20;

// Allows defining a 3D rectangular box to checkpoint mario's position for various splitting conditions.
Func<byte, float, float, float, float, float, float, dynamic> create3DBox = delegate(byte stageIndex, float x1, float y1, float z1, float x2, float y2, float z2) {
Expand Down Expand Up @@ -389,11 +413,14 @@ startup {
data.isJapaneseVersion = false;
data.isRTAMode = false;
data.isBlindfoldedMode = false;
data.selectedFileID = -1;

data.previousStage = 0;
data.previousCategoryName = "";
data.wantToReset = false;

data.updateCounter = (ushort) 0;

return data;
};

Expand Down Expand Up @@ -423,6 +450,7 @@ startup {
settingsD.disableResetAfterEnd = false;
settingsD.disableRTAMode = false;
settingsD.disableBowserRedsDelayedSplit = false;
settingsD.disableAutoStartOnFileD = false;

return settingsD;
};
Expand Down Expand Up @@ -453,6 +481,8 @@ startup {
result += "\n";
result += string.Format(" varsD.data.isJapaneseVersion = {0}\n", varsD.data.isJapaneseVersion);
result += string.Format(" varsD.data.isRTAMode = {0}\n", varsD.data.isRTAMode);
result += string.Format(" varsD.data.isBlindfoldedMode = {0}\n", varsD.data.isBlindfoldedMode);
result += string.Format(" varsD.data.selectedFileID = {0}\n", varsD.data.selectedFileID);
result += string.Format(" varsD.data.previousStage = {0}\n", varsD.data.previousStage);
result += string.Format(" varsD.data.previousCategoryName = {0}\n", varsD.data.previousCategoryName);
result += "\n";
Expand All @@ -463,6 +493,7 @@ startup {
result += string.Format(" varsD.settings.currentSplitName = {0}\n", varsD.settings.currentSplitName);
result += string.Format(" varsD.settings.splitCount = {0}\n", varsD.settings.splitCount);
result += string.Format(" varsD.settings.forceLaunchOnStart = {0}\n", varsD.settings.forceLaunchOnStart);
result += string.Format(" varsD.settings.disableAutoStartOnFileD = {0}\n", varsD.settings.disableAutoStartOnFileD);
result += string.Format(" varsD.settings.forceJPGameVersion = {0}\n", varsD.settings.forceJPGameVersion);
result += string.Format(" varsD.settings.forceUSGameVersion = {0}\n", varsD.settings.forceUSGameVersion);
result += string.Format(" varsD.settings.disableResetAfterEnd = {0}\n", varsD.settings.disableResetAfterEnd);
Expand Down Expand Up @@ -520,6 +551,27 @@ startup {
return varsD.data.isJapaneseVersion ? state.nonStopInteractionOverwriteJP : state.nonStopInteractionOverwriteUS;
};

Func<dynamic, dynamic, uint> getFileFlags = delegate(dynamic varsD, dynamic state) {
if (varsD.data.selectedFileID == 0) {
return varsD.data.isJapaneseVersion ? state.fileAFlagsJP : state.fileAFlagsUS;
} else if (varsD.data.selectedFileID == 1) {
return varsD.data.isJapaneseVersion ? state.fileBFlagsJP : state.fileBFlagsUS;
} else if (varsD.data.selectedFileID == 2) {
return varsD.data.isJapaneseVersion ? state.fileCFlagsJP : state.fileCFlagsUS;
} else if (varsD.data.selectedFileID == 3) {
return varsD.data.isJapaneseVersion ? state.fileDFlagsJP : state.fileDFlagsUS;
}
return 0;
};

Func<dynamic, dynamic, byte> getMenuSelectedButtonID = delegate(dynamic varsD, dynamic state) {
return varsD.data.isJapaneseVersion ? state.menuSelectedButtonIDJP : state.menuSelectedButtonIDUS;
};

Func<dynamic, dynamic, short> getMenuClickPos = delegate(dynamic varsD, dynamic state) {
return varsD.data.isJapaneseVersion ? state.menuClickPosJP : state.menuClickPosUS;
};

// isIn3DBox returns true if mario is currently in the defined 3D box.
Func<dynamic, dynamic, dynamic, bool> isIn3DBox = delegate(dynamic varsD, dynamic currentD, dynamic box) {
byte stageIndex = getStageIndex(varsD, currentD);
Expand Down Expand Up @@ -551,7 +603,8 @@ startup {
stageIndex_old != stageIndex_current &&
(
(STAGE_INDEXES[stageIndex_old] && CASTLE_STAGE_INDEXES[stageIndex_current]) ||
(STAGE_INDEXES[stageIndex_old] && BOWSER_FIGHT_STAGE_INDEXES[stageIndex_current])
(STAGE_INDEXES[stageIndex_old] && BOWSER_FIGHT_STAGE_INDEXES[stageIndex_current]) ||
(BOWSER_FIGHT_STAGE_INDEXES[stageIndex_old] && CASTLE_STAGE_INDEXES[stageIndex_current])
)
);
};
Expand Down Expand Up @@ -609,12 +662,13 @@ startup {

Func<LiveSplitState, dynamic, dynamic, dynamic, dynamic, bool> updateRunCondition = delegate(LiveSplitState timerD, dynamic settingsD, dynamic varsD, dynamic oldD, dynamic currentD) {
// DEBUGGING: Add prints here for debugging. Every DEBUG_VARS_DUMP_SECONDS seconds.
uint gameRuntime_current = getGameRuntime(varsD, currentD);
if (DEBUG_VARS_DUMP_SECONDS != 0 && (gameRuntime_current % (DEBUG_VARS_DUMP_SECONDS * 60)) == 0) {
if (DEBUG_VARS_DUMP_SECONDS != 0 && (varsD.data.updateCounter % (DEBUG_VARS_DUMP_SECONDS * 60)) == 0) {
print(string.Format("MARIO POSITION: X:{0}, Y:{1}, Z:{2}", getPositionX(varsD, currentD), getPositionY(varsD, currentD), getPositionZ(varsD, currentD)));
print(varsToString(varsD));
}

varsD.data.updateCounter += 1;

// Copy settings to var to help with testing.
varsD.settings.isResetEnabled = settingsD.ResetEnabled;
varsD.settings.categoryName = timerD.Run.CategoryName;
Expand All @@ -631,6 +685,7 @@ startup {
varsD.settings.disableResetAfterEnd = settingsD[DISABLE_RESET_AFTER_END];
varsD.settings.disableRTAMode = settingsD[DISABLE_RTA_MODE];
varsD.settings.disableBowserRedsDelayedSplit = settingsD[DISABLE_BOWSER_REDS_DELAYED_SPLIT];
varsD.settings.disableAutoStartOnFileD = settingsD[DISABLE_AUTO_START_ON_FILE_D];

// Call inner update logic.
return updateRunConditionInner(varsD, oldD, currentD);
Expand All @@ -652,6 +707,11 @@ startup {
uint music_old = getMusicTrack(varsD, oldD);
uint music_current = getMusicTrack(varsD, currentD);

byte menuSelectedButtonID_old = getMenuSelectedButtonID(varsD, oldD);
byte menuSelectedButtonID_current = getMenuSelectedButtonID(varsD, currentD);

short menuClickPos_current = getMenuClickPos(varsD, currentD);

// First frame of the logo appears on frame 4 (1.33s after launch).
if (!varsD.settings.forceLaunchOnStart && globalTimer_current == 4) {
return true;
Expand All @@ -662,6 +722,19 @@ startup {
return true;
}

// When a file is selected on the main menu, timer also starts.
if (
menuSelectedButtonID_old != menuSelectedButtonID_current &&
menuSelectedButtonID_old == 255 &&
0 <= menuSelectedButtonID_current &&
menuSelectedButtonID_current < 4 &&
(menuSelectedButtonID_current != 3 || !varsD.settings.disableAutoStartOnFileD) &&
menuClickPos_current == -10000
) {
varsD.data.selectedFileID = (int) menuSelectedButtonID_current;
return true;
}

// RTA mode, timer starts when we see star select screen, we leave a stage (fade-out) or we touch a door.
if (
!varsD.settings.disableRTAMode &&
Expand All @@ -687,6 +760,7 @@ startup {
// onResetRunCondition ensures important variable re-initialization always happens after reset.
Action<dynamic> onResetRunCondition = delegate(dynamic varsD) {
varsD.data.lastSplitIndex = -1;
varsD.data.selectedFileID = -1;
varsD.data.previousStage = 0;
varsD.data.wantToReset = false;
};
Expand Down Expand Up @@ -837,6 +911,8 @@ startup {

// parseSplitName configures splitConfig based on information within split name.
Action<dynamic, string> parseSplitName = delegate(dynamic varsD, string splitName) {
splitName = splitName.TrimStart('-');

HashSet<string> splitNameWords = splitWordsOnWhitespace(splitName);
dynamic splitConfig = varsD.data.splitConfig;

Expand Down Expand Up @@ -897,8 +973,16 @@ startup {
uint animation_old = getAnimation(varsD, oldD);
uint animation_current = getAnimation(varsD, currentD);

uint fileKeysFlag_old = getFileFlags(varsD, oldD) & KEY_FLAGS;
uint fileKeysFlag_current = getFileFlags(varsD, currentD) & KEY_FLAGS;

ushort nonStopInteractionOverwrite = getNonStopInteractionOverwrite(varsD, currentD);

bool isNonStopModeEnabled = (
nonStopInteractionOverwrite == NON_STOP_OVERWRITE_VALUE_USAMUNE ||
nonStopInteractionOverwrite == NON_STOP_OVERWRITE_VALUE_GAMESHARK
);

ushort starCount_old = getStarCount(varsD, oldD);
ushort starCount_current = getStarCount(varsD, currentD);

Expand Down Expand Up @@ -966,7 +1050,7 @@ startup {
// is on exit stage unless immediate split is added in which case the split happens immediately when the star is grabbed.
bool isStarGrabConditionInNonStopMet = (
splitConfig.type == SPLIT_TYPE_STAR_GRAB &&
nonStopInteractionOverwrite == NON_STOP_OVERWRITE_VALUE &&
isNonStopModeEnabled &&
starCount_old != starCount_current &&
starCount_current == splitConfig.starCountRequirement
);
Expand All @@ -975,7 +1059,6 @@ startup {
addLevelChangeSplittingCondition(isStarGrabConditionInNonStopMet);

// When we get a star grab animation in a bowser fight stage, we got the key.
// FIXME:
addLevelChangeSplittingCondition(
splitConfig.type == SPLIT_TYPE_KEY_GRAB &&
animation_old != animation_current &&
Expand All @@ -987,6 +1070,13 @@ startup {
optionalStarRequirementDone
);

addLevelChangeSplittingCondition(
splitConfig.type == SPLIT_TYPE_KEY_GRAB &&
isNonStopModeEnabled &&
fileKeysFlag_old != fileKeysFlag_current &&
optionalStarRequirementDone
);

// This is the last split and we are getting the last star, split immediately.
addImmediateSplittingCondition(
varsD.settings.currentSplitIndex == varsD.settings.splitCount - 1 &&
Expand Down Expand Up @@ -1090,7 +1180,7 @@ startup {
// Return the result of splitting conditions check, vars are reset in onSplit to avoid duplicate splitting.
return (
splitConditions.isSplittingImmediately ||
(splitConditions.isSplittingOnFade && stageIndex_old != stageIndex_current)
(splitConditions.isSplittingOnFade && (isStageFadeIn(stageIndex_old, stageIndex_current) || isStageFadeOut(stageIndex_old, stageIndex_current)))
);
};

Expand Down Expand Up @@ -1146,6 +1236,14 @@ startup {
state.nonStopInteractionOverwriteJP = /* isJP ? nonStopValue : */ defaultNonStopValue;
state.nonStopInteractionOverwriteUS = /* isJP ? */ defaultNonStopValue /* : nonStopValue */;

byte defaultMenuSelectedButtonID = 0xf9;
state.menuSelectedButtonIDJP = defaultMenuSelectedButtonID;
state.menuSelectedButtonIDUS = defaultMenuSelectedButtonID;

short defaultMenuClickPos = 0xfa;
state.menuClickPosJP = defaultMenuClickPos;
state.menuClickPosUS = defaultMenuClickPos;

return state;
};

Expand Down Expand Up @@ -1278,6 +1376,7 @@ startup {
settings.Add("generalSettings", true, "General Settings", "expertMode");
settings.Add(USE_DELAYED_RESET, false, "Delay reset of timer until restart (default: timer reset and start are separate events).", "generalSettings");
settings.Add(LAUNCH_ON_START, false, "Start on game launch instead of logo first frame (logo is more consistent, at 1.33s offset)", "generalSettings");
settings.Add(DISABLE_AUTO_START_ON_FILE_D, false, "Disable automatic start start on file D select.", "generalSettings");
settings.Add(DISABLE_RESET_AFTER_END, false, "Disable timer reset after game end (final star grab)", "generalSettings");
settings.Add(DISABLE_RTA_MODE, false, "Disable stage RTA mode.", "generalSettings");
settings.Add(DISABLE_BOWSER_REDS_DELAYED_SPLIT, false, "Disable bowser reds delayed split (default: split on pipe entry)", "generalSettings");
Expand Down

0 comments on commit 0ce7ede

Please sign in to comment.