tr2/savegame: save and load current music
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / Build TR1 (push) Has been skipped
Publish a pre-release / Build TR2 (push) Has been skipped
Publish a pre-release / Create a prerelease (push) Has been skipped

This allows the current music track and position to be saved and
restored on load, similar to TR1X.

Resolves #2579.
This commit is contained in:
lahm86 2025-04-17 15:03:18 +01:00
parent 0e24b65ae6
commit ec2a0452ff
16 changed files with 92 additions and 34 deletions

View file

@ -11,6 +11,7 @@
- added BSON savegame support, removing the limits imposed by the OG 8KB file size, so allowing for storing more data and offering improved feature support (legacy save files can still be read, similar to TR1) (#2662)
- added NG+, Japanese, and Japanese NG+ game mode options to the New Game page in the passport (#2731)
- added the ability for spike walls to be reset (antitriggered)
- added the current music track and timestamp to the savegame so they now persist on load (#2579)
- added waterfalls to the savegame so that they now persist on load (#2686)
- changed savegame files to be stored in the `saves` directory (#2087)
- changed the default fog distance to 22 tiles cutting off at 30 tiles to match TR1X (#1622)

View file

@ -303,6 +303,7 @@ as Notepad.
#### Audio
- added an option to control how music is played while underwater rather than simply muting it
- added the current music track and timestamp to the savegame so they now persist on load
- fixed music not playing with certain game versions
- fixed the audio not being in sync when Lara strikes the gong in Ice Palace
- fixed sound settings resuming the music

View file

@ -60,5 +60,6 @@ CFG_INT32(g_Config, audio.sound_volume, 10)
CFG_INT32(g_Config, audio.music_volume, 10)
CFG_BOOL(g_Config, audio.enable_lara_mic, false)
CFG_ENUM(g_Config, audio.underwater_music_mode, UMM_FULL, UNDERWATER_MUSIC_MODE)
CFG_ENUM(g_Config, audio.music_load_condition, MUSIC_LOAD_NON_AMBIENT, MUSIC_LOAD_CONDITION)
CFG_BOOL(g_Config, gameplay.enable_game_modes, true)
CFG_BOOL(g_Config, profile.new_game_plus_unlock, false)

View file

@ -1,5 +1,11 @@
#pragma once
typedef enum {
MUSIC_LOAD_NEVER,
MUSIC_LOAD_NON_AMBIENT,
MUSIC_LOAD_ALWAYS,
} MUSIC_LOAD_CONDITION;
#if TR_VERSION == 1
#include "./types_tr1.h"
#elif TR_VERSION == 2

View file

@ -47,12 +47,6 @@ typedef enum {
BC_PURPLE,
} BAR_COLOR;
typedef enum {
MUSIC_LOAD_NEVER,
MUSIC_LOAD_NON_AMBIENT,
MUSIC_LOAD_ALWAYS,
} MUSIC_LOAD_CONDITION;
typedef enum {
TLM_FULL,
TLM_SEMI,

View file

@ -69,6 +69,7 @@ typedef struct {
int32_t music_volume;
bool enable_lara_mic;
UNDERWATER_MUSIC_MODE underwater_music_mode;
MUSIC_LOAD_CONDITION music_load_condition;
} audio;
struct {

View file

@ -51,6 +51,10 @@ ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_CREATURE_LOT, "Creature pathfinding")
ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_SAMPLE_INFOS, "Sample information")
ENUM_MAP_DEFINE(GAME_BUFFER, GBUF_SAMPLES, "Samples")
ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_NEVER, "never")
ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_NON_AMBIENT, "non-ambient")
ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_ALWAYS, "always")
ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_FULL, "full")
ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_QUIET, "quiet")
ENUM_MAP_DEFINE(UNDERWATER_MUSIC_MODE, UMM_FULL_NO_AMBIENT, "full_no_ambient")

View file

@ -30,10 +30,6 @@ ENUM_MAP_DEFINE(TARGET_LOCK_MODE, TLM_FULL, "full-lock")
ENUM_MAP_DEFINE(TARGET_LOCK_MODE, TLM_SEMI, "semi-lock")
ENUM_MAP_DEFINE(TARGET_LOCK_MODE, TLM_NONE, "no-lock")
ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_NEVER, "never")
ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_NON_AMBIENT, "non-ambient")
ENUM_MAP_DEFINE(MUSIC_LOAD_CONDITION, MUSIC_LOAD_ALWAYS, "always")
ENUM_MAP_DEFINE(STAT_DETAIL_MODE, SDM_MINIMAL, "minimal")
ENUM_MAP_DEFINE(STAT_DETAIL_MODE, SDM_DETAILED, "detailed")
ENUM_MAP_DEFINE(STAT_DETAIL_MODE, SDM_FULL, "full")

View file

@ -2,11 +2,13 @@
#include "game/game_flow.h"
#include "game/inventory.h"
#include "game/lara/control.h"
#include "game/music.h"
#include "game/objects/general/lift.h"
#include "game/savegame.h"
#include "global/vars.h"
#include <libtrx/bson.h>
#include <libtrx/config.h>
#include <libtrx/debug.h>
#include <libtrx/game/camera.h>
#include <libtrx/game/music.h>
@ -303,6 +305,15 @@ static JSON_OBJECT *M_DumpMusic(void)
JSON_ArrayAppendInt(track_arr, Music_GetTrackFlags(i));
}
JSON_ObjectAppendArray(music_obj, "flags", track_arr);
const MUSIC_TRACK_ID current_track = Music_GetCurrentPlayingTrack();
const bool is_ambient = current_track == Music_GetCurrentLoopedTrack();
JSON_OBJECT *const current_obj = JSON_ObjectNew();
JSON_ObjectAppendInt(current_obj, "current_track", current_track);
JSON_ObjectAppendDouble(current_obj, "timestamp", Music_GetTimestamp());
JSON_ObjectAppendBool(current_obj, "is_ambient", is_ambient);
JSON_ObjectAppendObject(music_obj, "current", current_obj);
return music_obj;
}
@ -330,6 +341,35 @@ static bool M_LoadMusic(JSON_OBJECT *const music_obj)
Music_SetTrackFlags(i, JSON_ArrayGetInt(track_arr, i, 0));
}
const JSON_OBJECT *const current_obj =
JSON_ObjectGetObject(music_obj, "current");
if (current_obj == nullptr
|| g_Config.audio.music_load_condition == MUSIC_LOAD_NEVER) {
return true;
}
const int16_t current_track =
JSON_ObjectGetInt(current_obj, "current_track", -1);
double timestamp = JSON_ObjectGetDouble(current_obj, "timestamp", -1.0);
if (current_track != MX_INACTIVE) {
const bool is_ambient =
JSON_ObjectGetBool(music_obj, "is_ambient", false);
if (is_ambient) {
if (g_Config.audio.music_load_condition == MUSIC_LOAD_NON_AMBIENT) {
return true;
}
Music_Play(current_track, MPM_LOOPED);
} else {
Music_Play(current_track, MPM_ALWAYS);
}
if (!Music_SeekTimestamp(timestamp)) {
LOG_WARNING(
"Could not load current track %d at timestamp %" PRId64 ".",
current_track, timestamp);
}
}
return true;
}

View file

@ -54,11 +54,6 @@
"semi-lock": "Semi lock",
"no-lock": "No lock"
},
"music_load_condition": {
"never": "Never",
"non-ambient": "Non-ambient",
"always": "Always"
},
"stat_mode": {
"minimal": "Minimal",
"detailed": "Detailed",

View file

@ -58,18 +58,6 @@
"semi-lock": "Parziale",
"no-lock": "Nessuno"
},
"underwater_music": {
"full": "Completa",
"quiet": "Attenuata",
"full_no_ambient": "Completa ma senza suoni ambientali",
"quiet_no_ambient": "Attenuata ma senza suoni ambientali",
"none": "Assente"
},
"music_load_condition": {
"never": "Mai",
"non-ambient": "Non ambientale",
"always": "Sempre"
},
"stat_mode": {
"minimal": "Minimo",
"detailed": "Dettagliato",

View file

@ -114,6 +114,10 @@
"Title": "Gun/explosion lighting",
"Description": "Enables dynamic lighting to be generated for gunshots and explosions."
},
"music_load_condition": {
"Title": "Load music track",
"Description": "Loads the music track that was playing when the game was saved.\n- Never: do not restore music tracks on load.\n- Non-ambient: restore only non-ambient music tracks on load.\n- Always: restore any kind of music track on load."
},
"enable_lara_mic" : {
"Title": "Microphone at Lara",
"Description": "Set the microphone to be at Lara's position. If disabled, the microphone will be at the camera's position."

View file

@ -15,13 +15,6 @@
"any": "Qualsiasi",
"16:9": "16:9",
"4:3": "4:3"
},
"underwater_music": {
"full": "Completa",
"quiet": "Attenuata",
"full_no_ambient": "Completa ma senza suoni ambientali",
"quiet_no_ambient": "Attenuata ma senza suoni ambientali",
"none": "Assente"
}
},
"Properties": {
@ -121,6 +114,10 @@
"Title": "Illuminazione spari/esplosioni",
"Description": "Abilita l'illuminazione dinamica per spari ed esplosioni."
},
"music_load_condition": {
"Title": "Carica la traccia musicale",
"Description": "Carica la traccia musicale che era in riproduzione al momento del salvataggio della partita.\n- Mai: non ripristina le tracce musicali al caricamento.\n- Non ambientale: ripristina le tracce musicali ma non le tracce ambientali al caricamento.\n- Sempre: ripristina qualsiasi tipo di traccia musicale al caricamento."
},
"enable_lara_mic" : {
"Title": "Microfono su Lara",
"Description": "Imposta il microfono nella posizione in cui si trova Lara. Se disabilitato, il microfono sarà nella posizione della telecamera."

View file

@ -19,6 +19,11 @@
"full_no_ambient",
"quiet_no_ambient",
"none"
],
"music_load_condition": [
"never",
"non-ambient",
"always"
]
},
"CategorisedProperties": [
@ -189,6 +194,12 @@
"DataType": "Bool",
"DefaultValue": false
},
{
"Field": "music_load_condition",
"DataType": "Enum",
"EnumKey": "music_load_condition",
"DefaultValue": "non-ambient"
},
{
"Field": "underwater_music_mode",
"DataType": "Enum",

View file

@ -44,6 +44,11 @@
"jpg": "JPEG",
"png": "PNG"
},
"music_load_condition": {
"never": "Never",
"non-ambient": "Non-ambient",
"always": "Always"
},
"underwater_music": {
"full": "Full",
"quiet": "Quiet",

View file

@ -38,5 +38,19 @@
"graphics": "Grafica",
"sound": "Suono",
"ui": "Interfaccia utente"
},
"Enums": {
"underwater_music": {
"full": "Completa",
"quiet": "Attenuata",
"full_no_ambient": "Completa ma senza suoni ambientali",
"quiet_no_ambient": "Attenuata ma senza suoni ambientali",
"none": "Assente"
},
"music_load_condition": {
"never": "Mai",
"non-ambient": "Non ambientale",
"always": "Sempre"
}
}
}