Skip to content

Commit

Permalink
feat(saving): new dynamic label to run on game load
Browse files Browse the repository at this point in the history
  • Loading branch information
liana-p committed Jun 17, 2023
1 parent 8dd8a9a commit 6c3cb8b
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 3 deletions.
52 changes: 49 additions & 3 deletions docs/features/save-and-load.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**Important:** The `saveFileName` key in the `config.yaml` file is the name of the save file, and if this value is changed old saves will stop working. Once you have chosen a save file name for a game, do not change it in the future. The name you use should contain the name of your game to avoid clashes with other games

### How saving works
## How saving works

Narrat supports automatic saving and reloading, but there are some important details worth knowing about.

Expand All @@ -18,7 +18,7 @@ How saves works:
Because there is no way to identify which specific line of dialogue the player is on, **saving only saves the last label the player started,** not the exact line they reached
:::

### Save Slots
## Save Slots

A save slot is an individual save file. Each game can have any amount of save slots. There are two different ways save slots are managed, depending on how the game is configured:

Expand All @@ -37,7 +37,7 @@ saves:
If using `manual` mode, you should give the player a chance to create manual saves sometimes, as there is only one autosave which can get overwritten by starting a new game
:::

### Manual saving
## Manual saving

To let the player save manually, there are two commands:

Expand All @@ -64,6 +64,52 @@ Every time a new game is started, this script will increase the global counter d

To reset global save data, use the `reset_global_save` command.

## Run a function on game load

Sometimes, you might need your game to edit data that can't be saved. For example games can dynamically change the config after starting. A common example would be changing the player's name:

```
test_edit_config:
set data.playerName (text_field "Enter your name")
set config.characters.characters.player.name $data.playerName
```

This works fine, but if you reload the game, the player's name will be reset to its default as the game config gets loaded by the engine.

To be able to reapply your dynamic changes on every game reload, or to perform any task you want to perform when the player comes back after loading the game, you can use the `runOnReload` config key:

```config.yaml
saves:
mode: manual
slots: 10
runOnReload: "game_reload"
```

Then for example in the game code:

```
main:
jump ask_player_name
reset_config_overrides:
set config.characters.characters.player.name $data.playerName
ask_player_name:
set data.playerName (text_field "Enter your name")
run reset_config_overrides
run verify_edit_config
verify_edit_config:
talk player idle "It's me, %{$config.characters.characters.player.name}"
game_reload:
run set_config_overrides
talk helper idle "The game reloaded, welcome back %{$config.characters.characters.player.name}"
talk player idle "Wow it's me, and my name is still here!"
```

Without the `reset_config_overrides` function running on game load to add the appropriate values to the config, the player name in the config would still be its default value when reloading a save.

### The problem with saving a specific line

::: details Why we can only save on label change
Expand Down
1 change: 1 addition & 0 deletions packages/narrat/examples/games/default/data/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ layout:
saves:
mode: manual
slots: 10
runOnReload: 'test_label_reload'
screens: data/screens.yaml
buttons: data/buttons.yaml
skills: data/skills.yaml
Expand Down
17 changes: 17 additions & 0 deletions packages/narrat/examples/games/default/scripts/default.nar
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ main:
// talk player idle "Global counter is %{$global.counter}"
jump quest_demo

test_label_reload:
run set_config_overrides
"Game reloaded"
talk player idle "Welcome back"

set_config_overrides:
set config.characters.characters.player.name $data.playerName

test_edit_config:
set data.playerName (text_field "Enter your name")
run set_config_overrides
run verify_edit_config

verify_edit_config:
talk player idle "It's me, %{$config.characters.characters.player.name}"
jump achievements_demo

test_roll_if:
set data.n 2
choice:
Expand Down
1 change: 1 addition & 0 deletions packages/narrat/src/config/common-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export type DebuggingConfig = Static<typeof DebuggingConfigSchema>;
export const SavesConfigSchema = Type.Object({
mode: Type.String(),
slots: Type.Number(),
runOnReload: Type.Optional(Type.String()),
});
export type SavesConfig = Static<typeof SavesConfigSchema>;

Expand Down
5 changes: 5 additions & 0 deletions packages/narrat/src/stores/main-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { isPromise } from '@/utils/type-utils';
import { useMenu } from './menu-store';
import { useScreenObjects } from './screen-objects-store';
import { useAchievements } from './achievements-store';
import { useConfig } from './config-store';

export function defaultAppOptions(): AppOptions {
return {
Expand Down Expand Up @@ -246,6 +247,10 @@ export const useMain = defineStore('main', {
this.setLoadedData(save.saveData);
useAudio().reloadAudio(save.saveData.audio);
const vm = useVM();
const runOnReload = getConfig().saves.runOnReload;
if (typeof runOnReload === 'string') {
await useVM().runLabelFunction(runOnReload);
}
vm.jumpToLabel(save.saveData.vm.lastLabel);
}
},
Expand Down

0 comments on commit 6c3cb8b

Please sign in to comment.