mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-26 03:12:18 -05:00
Rando version warning on different builds (#2420)
* save build version to savefile * adjust rando hash icons to use fade in/out * add dialog message support on the file select screen and display rando version warning * remove duplicated message functions and use stubbed play state instead for rando warning * add major/minor/patch version saving to file and compare against * use strncpy and memset for build version * don't show rando warning one copy/erase screens * review feedback * Add german and french translations for rando warning Co-authored-by: PurpleHato <linkvssangoku.jr@gmail.com> --------- Co-authored-by: PurpleHato <linkvssangoku.jr@gmail.com>
This commit is contained in:
parent
27d7cb0bc1
commit
1fc6a2f08f
@ -2416,7 +2416,7 @@ void GameOver_Update(PlayState* play);
|
||||
void func_80110990(PlayState* play);
|
||||
void func_801109B0(PlayState* play);
|
||||
void Message_Init(PlayState* play);
|
||||
void func_80112098(PlayState* play);
|
||||
void Regs_InitData(PlayState* play);
|
||||
|
||||
void Title_Init(GameState* thisx);
|
||||
void Title_PrintBuildInfo(Gfx** gfxp);
|
||||
@ -2433,6 +2433,11 @@ void Heaps_Free(void);
|
||||
|
||||
CollisionHeader* BgCheck_GetCollisionHeader(CollisionContext* colCtx, s32 bgId);
|
||||
|
||||
// Exposing these methods to leverage them from the file select screen to render messages
|
||||
void Message_OpenText(PlayState* play, u16 textId);
|
||||
void Message_Decode(PlayState* play);
|
||||
void Message_DrawText(PlayState* play, Gfx** gfxP);
|
||||
|
||||
#ifdef __cplusplus
|
||||
#undef this
|
||||
};
|
||||
|
@ -46,6 +46,9 @@ extern "C"
|
||||
extern OSViMode osViModeFpalLan1;
|
||||
extern u32 __additional_scanline;
|
||||
extern u8 gBuildVersion[];
|
||||
extern s16 gBuildVersionMajor;
|
||||
extern s16 gBuildVersionMinor;
|
||||
extern s16 gBuildVersionPatch;
|
||||
extern u8 gBuildTeam[];
|
||||
extern u8 gBuildDate[];
|
||||
extern u8 gBuildMakeOption[];
|
||||
|
@ -54,6 +54,10 @@ typedef struct {
|
||||
} Inventory; // size = 0x5E
|
||||
|
||||
typedef struct {
|
||||
/* */ char buildVersion[50];
|
||||
/* */ s16 buildVersionMajor;
|
||||
/* */ s16 buildVersionMinor;
|
||||
/* */ s16 buildVersionPatch;
|
||||
/* */ u8 heartPieces;
|
||||
/* */ u8 heartContainers;
|
||||
/* */ u8 dungeonKeys[19];
|
||||
|
@ -41,6 +41,7 @@ typedef enum {
|
||||
TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200,
|
||||
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
|
||||
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
|
||||
TEXT_RANDO_SAVE_VERSION_WARNING = 0x9300,
|
||||
} TextIDs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1692,6 +1692,9 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
if (textId == TEXT_MARKET_GUARD_NIGHT && CVarGetInteger("gMarketSneak", 0) && play->sceneNum == SCENE_ENTRA_N) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_MARKET_GUARD_NIGHT);
|
||||
}
|
||||
if (textId == TEXT_RANDO_SAVE_VERSION_WARNING) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING);
|
||||
}
|
||||
if (messageEntry.textBoxType != -1) {
|
||||
font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos;
|
||||
switch (gSaveContext.language) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "z64.h"
|
||||
#include "functions.h"
|
||||
#include "macros.h"
|
||||
#include <variables.h>
|
||||
#include <Hooks.h>
|
||||
#include <libultraship/bridge.h>
|
||||
|
||||
@ -52,6 +53,11 @@ SaveManager::SaveManager() {
|
||||
info.randoSave = 0;
|
||||
info.requiresMasterQuest = 0;
|
||||
info.requiresOriginal = 0;
|
||||
|
||||
info.buildVersionMajor = 0;
|
||||
info.buildVersionMinor = 0;
|
||||
info.buildVersionPatch = 0;
|
||||
memset(&info.buildVersion, 0, sizeof(info.buildVersion));
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,6 +395,12 @@ void SaveManager::InitMeta(int fileNum) {
|
||||
// If the file is not marked as Master Quest, it could still theoretically be a rando save with all 12 MQ dungeons, in which case
|
||||
// we don't actually require a vanilla OTR.
|
||||
fileMetaInfo[fileNum].requiresOriginal = !gSaveContext.isMasterQuest && (!gSaveContext.n64ddFlag || gSaveContext.mqDungeonCount < 12);
|
||||
|
||||
fileMetaInfo[fileNum].buildVersionMajor = gSaveContext.sohStats.buildVersionMajor;
|
||||
fileMetaInfo[fileNum].buildVersionMinor = gSaveContext.sohStats.buildVersionMinor;
|
||||
fileMetaInfo[fileNum].buildVersionPatch = gSaveContext.sohStats.buildVersionPatch;
|
||||
strncpy(fileMetaInfo[fileNum].buildVersion, gSaveContext.sohStats.buildVersion, sizeof(fileMetaInfo[fileNum].buildVersion) - 1);
|
||||
fileMetaInfo[fileNum].buildVersion[sizeof(fileMetaInfo[fileNum].buildVersion) - 1] = 0;
|
||||
}
|
||||
|
||||
void SaveManager::InitFile(bool isDebug) {
|
||||
@ -559,6 +571,13 @@ void SaveManager::InitFileNormal() {
|
||||
gSaveContext.infTable[29] = 1;
|
||||
gSaveContext.sceneFlags[5].swch = 0x40000000;
|
||||
gSaveContext.pendingSale = ITEM_NONE;
|
||||
|
||||
strncpy(gSaveContext.sohStats.buildVersion, (const char*) gBuildVersion, sizeof(gSaveContext.sohStats.buildVersion) - 1);
|
||||
gSaveContext.sohStats.buildVersion[sizeof(gSaveContext.sohStats.buildVersion) - 1] = 0;
|
||||
gSaveContext.sohStats.buildVersionMajor = gBuildVersionMajor;
|
||||
gSaveContext.sohStats.buildVersionMinor = gBuildVersionMinor;
|
||||
gSaveContext.sohStats.buildVersionPatch = gBuildVersionPatch;
|
||||
|
||||
//RANDOTODO (ADD ITEMLOCATIONS TO GSAVECONTEXT)
|
||||
}
|
||||
|
||||
@ -1248,6 +1267,14 @@ void SaveManager::LoadBaseVersion3() {
|
||||
SaveManager::Instance->LoadData("gsTokens", gSaveContext.inventory.gsTokens);
|
||||
});
|
||||
SaveManager::Instance->LoadStruct("sohStats", []() {
|
||||
std::string buildVersion;
|
||||
SaveManager::Instance->LoadData("buildVersion", buildVersion);
|
||||
strncpy(gSaveContext.sohStats.buildVersion, buildVersion.c_str(), ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1);
|
||||
gSaveContext.sohStats.buildVersion[ARRAY_COUNT(gSaveContext.sohStats.buildVersion) - 1] = 0;
|
||||
SaveManager::Instance->LoadData("buildVersionMajor", gSaveContext.sohStats.buildVersionMajor);
|
||||
SaveManager::Instance->LoadData("buildVersionMinor", gSaveContext.sohStats.buildVersionMinor);
|
||||
SaveManager::Instance->LoadData("buildVersionPatch", gSaveContext.sohStats.buildVersionPatch);
|
||||
|
||||
SaveManager::Instance->LoadData("heartPieces", gSaveContext.sohStats.heartPieces);
|
||||
SaveManager::Instance->LoadData("heartContainers", gSaveContext.sohStats.heartContainers);
|
||||
SaveManager::Instance->LoadArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
|
||||
@ -1441,6 +1468,11 @@ void SaveManager::SaveBase() {
|
||||
SaveManager::Instance->SaveData("gsTokens", gSaveContext.inventory.gsTokens);
|
||||
});
|
||||
SaveManager::Instance->SaveStruct("sohStats", []() {
|
||||
SaveManager::Instance->SaveData("buildVersion", gSaveContext.sohStats.buildVersion);
|
||||
SaveManager::Instance->SaveData("buildVersionMajor", gSaveContext.sohStats.buildVersionMajor);
|
||||
SaveManager::Instance->SaveData("buildVersionMinor", gSaveContext.sohStats.buildVersionMinor);
|
||||
SaveManager::Instance->SaveData("buildVersionPatch", gSaveContext.sohStats.buildVersionPatch);
|
||||
|
||||
SaveManager::Instance->SaveData("heartPieces", gSaveContext.sohStats.heartPieces);
|
||||
SaveManager::Instance->SaveData("heartContainers", gSaveContext.sohStats.heartContainers);
|
||||
SaveManager::Instance->SaveArray("dungeonKeys", ARRAY_COUNT(gSaveContext.sohStats.dungeonKeys), [](size_t i) {
|
||||
@ -1681,6 +1713,11 @@ void SaveManager::CopyZeldaFile(int from, int to) {
|
||||
fileMetaInfo[to].randoSave = fileMetaInfo[from].randoSave;
|
||||
fileMetaInfo[to].requiresMasterQuest = fileMetaInfo[from].requiresMasterQuest;
|
||||
fileMetaInfo[to].requiresOriginal = fileMetaInfo[from].requiresOriginal;
|
||||
fileMetaInfo[to].buildVersionMajor = fileMetaInfo[from].buildVersionMajor;
|
||||
fileMetaInfo[to].buildVersionMinor = fileMetaInfo[from].buildVersionMinor;
|
||||
fileMetaInfo[to].buildVersionPatch = fileMetaInfo[from].buildVersionPatch;
|
||||
strncpy(fileMetaInfo[to].buildVersion, fileMetaInfo[from].buildVersion, sizeof(fileMetaInfo[to].buildVersion) - 1);
|
||||
fileMetaInfo[to].buildVersion[sizeof(fileMetaInfo[to].buildVersion) - 1] = 0;
|
||||
}
|
||||
|
||||
void SaveManager::DeleteZeldaFile(int fileNum) {
|
||||
|
@ -14,6 +14,10 @@ typedef struct {
|
||||
u32 requiresOriginal;
|
||||
u8 seedHash[5];
|
||||
u8 randoSave;
|
||||
char buildVersion[50];
|
||||
s16 buildVersionMajor;
|
||||
s16 buildVersionMinor;
|
||||
s16 buildVersionPatch;
|
||||
} SaveFileMetaInfo;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -153,4 +153,13 @@ extern "C" void OTRMessage_Init()
|
||||
"Tu as l'air de t'ennuyer. Tu veux&aller faire un tour?\x1B&%gOui&Non%w",
|
||||
}
|
||||
);
|
||||
CustomMessageManager::Instance->CreateMessage(
|
||||
customMessageTableID, TEXT_RANDO_SAVE_VERSION_WARNING,
|
||||
{
|
||||
TEXTBOX_TYPE_NONE_BOTTOM, TEXTBOX_POS_BOTTOM,
|
||||
"This save was created on&a different version of SoH.&&Things may be broken.",
|
||||
"Dieser Spielstand wurde auf einer&anderen Version von SoH erstellt.&&Es könnten Fehler auftreten.",
|
||||
"Cette sauvegarde a été créée sur&une version différente de SoH.&Certaines fonctionnalités&peuvent être corrompues.",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
const char gBuildVersion[] = "@PROJECT_BUILD_NAME@ (@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@.@CMAKE_PROJECT_VERSION_PATCH@)";
|
||||
const int gBuildVersionMajor = @CMAKE_PROJECT_VERSION_MAJOR@;
|
||||
const int gBuildVersionMinor = @CMAKE_PROJECT_VERSION_MINOR@;
|
||||
const int gBuildVersionPatch = @CMAKE_PROJECT_VERSION_PATCH@;
|
||||
const char gBuildTeam[] = "@PROJECT_TEAM@";
|
||||
const char gBuildDate[] = __DATE__ " " __TIME__;
|
||||
const char gBuildMakeOption[] = "";
|
||||
|
@ -163,7 +163,7 @@ void Message_Init(PlayState* play) {
|
||||
YREG(31) = 0;
|
||||
}
|
||||
|
||||
void func_80111070(void) {
|
||||
void Regs_InitDataImpl(void) {
|
||||
YREG(8) = 10;
|
||||
YREG(14) = 0;
|
||||
YREG(15) = 0;
|
||||
@ -572,6 +572,6 @@ void func_80111070(void) {
|
||||
VREG(92) = -63;
|
||||
}
|
||||
|
||||
void func_80112098(PlayState* play) {
|
||||
func_80111070();
|
||||
void Regs_InitData(PlayState* play) {
|
||||
Regs_InitDataImpl();
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ void Play_Init(GameState* thisx) {
|
||||
play->cameraPtrs[MAIN_CAM]->uid = 0;
|
||||
play->activeCamera = MAIN_CAM;
|
||||
func_8005AC48(&play->mainCamera, 0xFF);
|
||||
func_80112098(play);
|
||||
Regs_InitData(play);
|
||||
Message_Init(play);
|
||||
GameOver_Init(play);
|
||||
SoundSource_InitAll(play);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
|
||||
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
|
||||
|
||||
#define NORMAL_QUEST 0
|
||||
#define MASTER_QUEST 1
|
||||
#define RANDOMIZER_QUEST 2
|
||||
@ -387,12 +389,17 @@ void DrawSeedHashSprites(FileChooseContext* this) {
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
|
||||
|
||||
if (this->windowRot == 0 || (this->configMode == CM_QUEST_MENU && this->questType[this->buttonIndex] == RANDOMIZER_QUEST)) {
|
||||
if (this->selectMode == SM_CONFIRM_FILE) {
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0xFF, 0xFF, 0xFF, this->fileInfoAlpha[this->buttonIndex]);
|
||||
// Draw icons on the main menu, when a rando file is selected, and when quest selection is set to rando
|
||||
if ((this->configMode == CM_MAIN_MENU &&
|
||||
(this->selectMode != SM_CONFIRM_FILE || Save_GetSaveMetaInfo(this->selectedFileIndex)->randoSave == 1)) ||
|
||||
(this->configMode == CM_QUEST_MENU && this->questType[this->buttonIndex] == RANDOMIZER_QUEST)) {
|
||||
|
||||
if (this->fileInfoAlpha[this->selectedFileIndex] > 0) {
|
||||
// Use file info alpha to match fading
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0xFF, 0xFF, 0xFF, this->fileInfoAlpha[this->selectedFileIndex]);
|
||||
|
||||
u16 xStart = 64;
|
||||
// Draw Seed Icons
|
||||
// Draw Seed Icons for specific file
|
||||
for (unsigned int i = 0; i < 5; i++) {
|
||||
if (Save_GetSaveMetaInfo(this->selectedFileIndex)->randoSave == 1) {
|
||||
SpriteLoad(this, GetSeedTexture(Save_GetSaveMetaInfo(this->selectedFileIndex)->seedHash[i]));
|
||||
@ -402,8 +409,15 @@ void DrawSeedHashSprites(FileChooseContext* this) {
|
||||
}
|
||||
}
|
||||
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0xFF, 0xFF, 0xFF, this->fileButtonAlpha[this->buttonIndex]);
|
||||
// Fade top seed icons based on main menu fade and if save supports rando
|
||||
u8 alpha = MAX(this->optionButtonAlpha, Save_GetSaveMetaInfo(this->selectedFileIndex)->randoSave == 1 ? 0xFF : 0);
|
||||
if (alpha >= 200) {
|
||||
alpha = 0xFF;
|
||||
}
|
||||
|
||||
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0xFF, 0xFF, 0xFF, alpha);
|
||||
|
||||
// Draw Seed Icons for spoiler log
|
||||
if (strnlen(CVarGetString("gSpoilerLog", ""), 1) != 0 && fileSelectSpoilerFileLoaded) {
|
||||
u16 xStart = 64;
|
||||
for (unsigned int i = 0; i < 5; i++) {
|
||||
@ -1340,6 +1354,47 @@ void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use file info alpha to match fading
|
||||
u8 textAlpha = this->fileInfoAlpha[fileIndex];
|
||||
if (textAlpha >= 200) {
|
||||
textAlpha = 255;
|
||||
}
|
||||
|
||||
// Draw rando seed warning when build version doesn't match for Major or Minor number
|
||||
if (Save_GetSaveMetaInfo(fileIndex)->randoSave == 1 &&
|
||||
this->menuMode == FS_MENU_MODE_SELECT &&
|
||||
(gBuildVersionMajor != Save_GetSaveMetaInfo(fileIndex)->buildVersionMajor ||
|
||||
gBuildVersionMinor != Save_GetSaveMetaInfo(fileIndex)->buildVersionMinor)) {
|
||||
|
||||
// Stub out a dummy play state to be able to use the dialog system (MessageCtx)
|
||||
PlayState dummyPlay;
|
||||
PlayState* dummyPlayPtr = &dummyPlay;
|
||||
|
||||
// Set the MessageCtx and GameState onto the dummy play state
|
||||
dummyPlayPtr->msgCtx = this->msgCtx;
|
||||
dummyPlayPtr->state = this->state;
|
||||
|
||||
// Load the custom text ID without doing a textbox
|
||||
Message_OpenText(dummyPlayPtr, TEXT_RANDO_SAVE_VERSION_WARNING);
|
||||
// Force the context into message print mode
|
||||
dummyPlayPtr->msgCtx.msgMode = MSGMODE_TEXT_NEXT_MSG;
|
||||
Message_Decode(dummyPlayPtr);
|
||||
|
||||
// Set the draw pos to end of text to render it all at once
|
||||
dummyPlayPtr->msgCtx.textDrawPos = dummyPlayPtr->msgCtx.decodedTextLen;
|
||||
dummyPlayPtr->msgCtx.textColorAlpha = textAlpha;
|
||||
|
||||
// Set position and spacing values
|
||||
R_TEXT_LINE_SPACING = 10;
|
||||
R_TEXT_INIT_XPOS = 128;
|
||||
R_TEXT_INIT_YPOS = 154;
|
||||
|
||||
Gfx* gfx = Graph_GfxPlusOne(POLY_OPA_DISP);
|
||||
Message_DrawText(dummyPlayPtr, &gfx);
|
||||
|
||||
POLY_OPA_DISP = gfx;
|
||||
}
|
||||
}
|
||||
|
||||
CLOSE_DISPS(this->state.gfxCtx);
|
||||
@ -2658,6 +2713,9 @@ void FileChoose_Init(GameState* thisx) {
|
||||
DmaMgr_SendRequest1(this->parameterSegment, (u32)_parameter_staticSegmentRomStart, size, __FILE__,
|
||||
__LINE__);
|
||||
|
||||
// Load some registers used by the dialog system
|
||||
Regs_InitData(NULL); // Passing in NULL as we dont have a playstate, and it isn't used in the func
|
||||
|
||||
Matrix_Init(&this->state);
|
||||
View_Init(&this->view, this->state.gfxCtx);
|
||||
this->state.main = FileChoose_Main;
|
||||
|
Loading…
Reference in New Issue
Block a user