mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-20 13:24:40 -04:00
Use carousel button on prelaunch; more cleanup
This commit is contained in:
+45
-45
@@ -636,83 +636,78 @@ first in-game frame. Projection matrices match the renderer's WebGPU clip conven
|
||||
Camera operators allow overriding the main camera. When an operator callback returns true, its values replace the camera
|
||||
state for the current frame. Register and unregister using `register_camera_operator` / `unregister_camera_operator`.
|
||||
|
||||
### GameModeService (`mods/svc/gamemode.h`)
|
||||
### GameModeService (`mods/svc/game_mode.h`)
|
||||
|
||||
Allows a mod to register a game mode with callbacks for key gameplay and save lifecycle events. Registered game modes
|
||||
appear in the prelaunch menu. Game modes may use a unique set of saves by configuring `saveName`; leave it empty to use
|
||||
appear in the prelaunch menu. Game modes may use a unique set of saves by configuring `save_name`; leave it empty to use
|
||||
the vanilla `gczelda2` save.
|
||||
|
||||
```cpp
|
||||
// An example that shows registering a gamemode with function hooks that are scoped to the gamemode being active
|
||||
IMPORT_SERVICE(LogService, svc_log);
|
||||
IMPORT_SERVICE(HookService, svc_hook);
|
||||
IMPORT_SERVICE(GameModeService, svc_gamemode);
|
||||
IMPORT_SERVICE(GameModeService, svc_game_mode);
|
||||
|
||||
DEFINE_HOOK(fopAcM_createItem, CreateItem);
|
||||
|
||||
#define MY_GAME_MODE_ID "game-mode-id"
|
||||
|
||||
static HookAction myFunctionHook(ModContext *ctx, void *args, void *, void *) {
|
||||
static HookAction my_function_hook(ModContext* ctx, void* args, void*, void*) {
|
||||
// If we wish to have this hook only run while the gamemode is registered, we need to hook the function from the
|
||||
// gamemode's onActivatedFunction, and uninstall the hook during the onDeactivatedFunction. Example below.
|
||||
// Alternatively, check with `svc_gamemode->is_active(mod_ctx, MY_GAME_MODE_ID, &active) == MOD_OK && active`.
|
||||
|
||||
// Alternatively, check with `svc_game_mode->is_active(mod_ctx, MY_GAME_MODE_ID, &active) == MOD_OK && active`.
|
||||
return HOOK_CONTINUE;
|
||||
}
|
||||
|
||||
void onGameModeActivated() {
|
||||
ModResult on_game_mode_activated(void*, ModError* outError) {
|
||||
// Setup the gamemode, Add any hooks that are gamemode specific
|
||||
// Overlay any files that are gamemode specific
|
||||
ModResult result = mods::hook_add_pre<CreateItem>(svc_hook, myFunctionHook);
|
||||
ModResult result = mods::hook_add_pre<CreateItem>(svc_hook, my_function_hook);
|
||||
if (result != MOD_OK) {
|
||||
svc_log->error(mod_ctx, "failed to install hook to fopAcM_createItem");
|
||||
}
|
||||
return mods::set_error(outError, result, "failed to install fopAcM_createItem hook");
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
void onGameModeDeactivated() {
|
||||
ModResult on_game_mode_deactivated(void*, ModError* outError) {
|
||||
// Uninstall any hooks that are gamemode specific
|
||||
// Remove any file overlays that are gamemode specific
|
||||
ModResult result = mods::hook_uninstall<CreateItem>();
|
||||
if (result != MOD_OK) {
|
||||
svc_log->error(mod_ctx, "failed to uninstall CreateItem hook");
|
||||
}
|
||||
return mods::set_error(outError, result, "failed to uninstall fopAcM_createItem hook");
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
void onSaveLoaded() {
|
||||
ModResult on_save_loaded(void*, ModError*) {
|
||||
// This function will be invoked by the game as a save is loaded
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
// Register the game mode when the mod is initialized.
|
||||
const GameModeDesc gameModeDesc = {
|
||||
.gameModeId = MY_GAME_MODE_ID,
|
||||
.fullName = "My Game Mode",
|
||||
.saveName = "my-unique-save", // Custom save names should be unique, max 31 chars
|
||||
.onActivatedFunction = onGameModeActivated, // Called when the game mode is selected or launched
|
||||
.onDeactivatedFunction = onGameModeDeactivated, // Called when it is deselected or the mod is disabled
|
||||
.onPlayFunction = nullptr, // Called when "Play" is pressed on the prelaunch menu
|
||||
.onSaveLoadedFunction = onSaveLoaded, // Called when a save is loaded
|
||||
.onNewSaveFunction = nullptr, // Called after a new savefile is created
|
||||
.onNewSaveSelectFunction = nullptr, // Called during the flow before the file name select is ran (see below)
|
||||
.onGameResetFunction = nullptr, // Called when the game is reset
|
||||
.onTickFunction = nullptr, // Called every game tick while the game mode is active
|
||||
.struct_size = sizeof(GameModeDesc),
|
||||
.game_mode_id = MY_GAME_MODE_ID,
|
||||
.full_name = "My Game Mode",
|
||||
.save_name = "my-unique-save",
|
||||
.user_data = nullptr,
|
||||
.on_activated = on_game_mode_activated,
|
||||
.on_deactivated = on_game_mode_deactivated,
|
||||
.on_save_loaded = on_save_loaded,
|
||||
};
|
||||
svc_gamemode->register_game_mode(mod_ctx, &gameModeDesc);
|
||||
svc_game_mode->register_game_mode(mod_ctx, &gameModeDesc);
|
||||
```
|
||||
|
||||
A game mode can also open UI for per-save settings when creating a new file.
|
||||
A game mode can also open UI for per-save settings when creating a new file. The state begins as
|
||||
`GAME_MODE_STATE_PENDING` and remains valid until the mod selects `PROCEED` or `RETURN`.
|
||||
|
||||
```cpp
|
||||
IMPORT_SERVICE(GameModeService, svc_gamemode);
|
||||
IMPORT_SERVICE(GameModeService, svc_game_mode);
|
||||
IMPORT_SERVICE(UiService, svc_ui);
|
||||
|
||||
void onNewSaveSelect(bool *out_proceedToNameSelect, bool *out_returnToFileSelect) {
|
||||
static bool *proceedToNameSelect;
|
||||
static bool *returnToFileSelect;
|
||||
ModResult on_new_save_select(void*, GameModeNewSaveState* state, ModError* outError) {
|
||||
static GameModeNewSaveState* newSaveState;
|
||||
static UiWindowHandle windowHandle;
|
||||
|
||||
// Used within callbacks as needed
|
||||
proceedToNameSelect = out_proceedToNameSelect;
|
||||
returnToFileSelect = out_returnToFileSelect;
|
||||
newSaveState = state;
|
||||
|
||||
UiTabDesc tabs[1]{};
|
||||
|
||||
@@ -724,7 +719,7 @@ void onNewSaveSelect(bool *out_proceedToNameSelect, bool *out_returnToFileSelect
|
||||
desc.label = "Play";
|
||||
desc.help_rml = "Play Button";
|
||||
desc.on_pressed = [](ModContext* ctx, void* userdata) {
|
||||
*proceedToNameSelect = true;
|
||||
*newSaveState = GAME_MODE_STATE_PROCEED;
|
||||
svc_ui->window_close(ctx, *static_cast<UiWindowHandle*>(userdata));
|
||||
};
|
||||
desc.user_data = &windowHandle;
|
||||
@@ -737,21 +732,26 @@ void onNewSaveSelect(bool *out_proceedToNameSelect, bool *out_returnToFileSelect
|
||||
desc.tab_count = 1;
|
||||
desc.on_closed = [](ModContext *, UiWindowHandle, void *userdata) {
|
||||
// If closing the window through backing out, return to file select
|
||||
if (*proceedToNameSelect == false) {
|
||||
*returnToFileSelect = true;
|
||||
if (*newSaveState == GAME_MODE_STATE_PENDING) {
|
||||
*newSaveState = GAME_MODE_STATE_RETURN;
|
||||
}
|
||||
};
|
||||
|
||||
svc_ui->window_push(mod_ctx, &desc, &windowHandle);
|
||||
ModResult result = svc_ui->window_push(mod_ctx, &desc, &windowHandle);
|
||||
if (result != MOD_OK) {
|
||||
return mods::set_error(outError, result, "failed to open new-save settings");
|
||||
}
|
||||
return MOD_OK;
|
||||
}
|
||||
|
||||
const GameModeDesc gameModeDesc = {
|
||||
.gameModeId = "my-game-mode-id",
|
||||
.fullName = "My Game Mode",
|
||||
.saveName = "my-unique-save",
|
||||
.onNewSaveSelectFunction = onNewSaveSelect,
|
||||
.struct_size = sizeof(GameModeDesc),
|
||||
.game_mode_id = "my-game-mode-id",
|
||||
.full_name = "My Game Mode",
|
||||
.save_name = "my-unique-save",
|
||||
.on_new_save_select = on_new_save_select,
|
||||
};
|
||||
svc_gamemode->register_game_mode(mod_ctx, &gameModeDesc);
|
||||
svc_game_mode->register_game_mode(mod_ctx, &gameModeDesc);
|
||||
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user