@ -86,108 +86,123 @@ const void SaveFile::SaveGame(){
game - > SetQuitAllowed ( false ) ;
std : : filesystem : : create_directories ( " save_file_path " _S ) ;
utils : : datafile saveFile ;
utils : : datafile saveSystemFile ;
utils : : datafile : : INITIAL_SETUP_COMPLETE = false ;
for ( size_t itemCount = 0 ; auto & item : Inventory : : GetInventory ( ) ) {
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Amt " ] . SetInt ( item - > Amt ( ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Enhancement Level " ] . SetInt ( item - > EnhancementLevel ( ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Item Name " ] . SetString ( item - > ActualName ( ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Equip Slot " ] . SetInt ( int ( Inventory : : GetSlotEquippedIn ( item ) ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Locked " ] . SetBool ( item - > IsLocked ( ) ) ;
uint8_t loadoutSlotNumber = 255 ;
for ( int i = 0 ; i < game - > loadout . size ( ) ; i + + ) {
if ( item = = game - > GetLoadoutItem ( i ) ) { loadoutSlotNumber = i ; break ; }
{
utils : : datafile saveFile ;
utils : : datafile : : INITIAL_SETUP_COMPLETE = false ;
for ( size_t itemCount = 0 ; auto & item : Inventory : : GetInventory ( ) ) {
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Amt " ] . SetInt ( item - > Amt ( ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Enhancement Level " ] . SetInt ( item - > EnhancementLevel ( ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Item Name " ] . SetString ( item - > ActualName ( ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Equip Slot " ] . SetInt ( int ( Inventory : : GetSlotEquippedIn ( item ) ) ) ;
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Locked " ] . SetBool ( item - > IsLocked ( ) ) ;
uint8_t loadoutSlotNumber = 255 ;
for ( int i = 0 ; i < game - > loadout . size ( ) ; i + + ) {
if ( item = = game - > GetLoadoutItem ( i ) ) { loadoutSlotNumber = i ; break ; }
}
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " LoadoutSlot " ] . SetInt ( loadoutSlotNumber ) ;
for ( const auto & [ attr , val ] : item - > RandomStats ( ) ) {
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Attributes " ] [ std : : string ( attr . ActualName ( ) ) ] . SetReal ( val ) ;
}
itemCount + + ;
}
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " LoadoutSlot " ] . SetInt ( loadoutSlotNumber ) ;
for ( const auto & [ attr , val ] : item - > RandomStats ( ) ) {
saveFile [ " Items " ] [ std : : format ( " Item[{}] " , itemCount ) ] [ " Attributes " ] [ std : : string ( attr . ActualName ( ) ) ] . SetReal ( val ) ;
saveFile [ " Player " ] [ " Class " ] . SetString ( game - > GetPlayer ( ) - > GetClassName ( ) ) ;
saveFile [ " Player " ] [ " Level " ] . SetInt ( game - > GetPlayer ( ) - > Level ( ) ) ;
saveFile [ " Player " ] [ " Money " ] . SetInt ( game - > GetPlayer ( ) - > GetMoney ( ) ) ;
saveFile [ " Player " ] [ " Current EXP " ] . SetInt ( game - > GetPlayer ( ) - > CurrentXP ( ) ) ;
saveFile [ " Player " ] [ " Total EXP " ] . SetInt ( game - > GetPlayer ( ) - > TotalXP ( ) ) ;
for ( const auto & [ attr , val ] : game - > GetPlayer ( ) - > GetBaseStats ( ) ) {
saveFile [ " Player " ] [ " Base Stats " ] [ std : : string ( attr . ActualName ( ) ) ] . SetReal ( val ) ;
}
itemCount + + ;
}
saveFile [ " Player " ] [ " Class " ] . SetString ( game - > GetPlayer ( ) - > GetClassName ( ) ) ;
saveFile [ " Player " ] [ " Level " ] . SetInt ( game - > GetPlayer ( ) - > Level ( ) ) ;
saveFile [ " Player " ] [ " Money " ] . SetInt ( game - > GetPlayer ( ) - > GetMoney ( ) ) ;
saveFile [ " Player " ] [ " Current EXP " ] . SetInt ( game - > GetPlayer ( ) - > CurrentXP ( ) ) ;
saveFile [ " Player " ] [ " Total EXP " ] . SetInt ( game - > GetPlayer ( ) - > TotalXP ( ) ) ;
for ( const auto & [ attr , val ] : game - > GetPlayer ( ) - > GetBaseStats ( ) ) {
saveFile [ " Player " ] [ " Base Stats " ] [ std : : string ( attr . ActualName ( ) ) ] . SetReal ( val ) ;
}
for ( const std : : string & unlockName : Unlock : : unlocks ) {
if ( unlockName = = " WORLD_MAP " ) continue ; //This is a special exception, because the world map is not an actual stage.
saveFile [ " Unlocks " ] [ unlockName ] . SetString ( " True " ) ;
auto opt_cp = State_OverworldMap : : ConnectionPointFromString ( unlockName ) ;
if ( ! opt_cp . has_value ( ) ) continue ; //Harmless, we probably just deleted the map.
if ( opt_cp . value ( ) - > Visited ( ) ) {
saveFile [ " Unlocks " ] [ unlockName ] . SetString ( " True " , 1U ) ;
} else {
saveFile [ " Unlocks " ] [ unlockName ] . SetString ( " False " , 1U ) ;
for ( const std : : string & unlockName : Unlock : : unlocks ) {
if ( unlockName = = " WORLD_MAP " ) continue ; //This is a special exception, because the world map is not an actual stage.
saveFile [ " Unlocks " ] [ unlockName ] . SetString ( " True " ) ;
auto opt_cp = State_OverworldMap : : ConnectionPointFromString ( unlockName ) ;
if ( ! opt_cp . has_value ( ) ) continue ; //Harmless, we probably just deleted the map.
if ( opt_cp . value ( ) - > Visited ( ) ) {
saveFile [ " Unlocks " ] [ unlockName ] . SetString ( " True " , 1U ) ;
} else {
saveFile [ " Unlocks " ] [ unlockName ] . SetString ( " False " , 1U ) ;
}
}
}
for ( auto & [ taskName , task ] : Tutorial : : taskList ) {
saveFile [ " Tutorial " ] [ std : : to_string ( int ( taskName ) ) ] . SetBool ( Tutorial : : TaskIsComplete ( taskName ) ) ;
}
saveFile [ " Overworld Map Location " ] . SetString ( State_OverworldMap : : GetCurrentConnectionPoint ( ) . name ) ;
saveFile [ " Chapter " ] . SetInt ( game - > GetCurrentChapter ( ) ) ;
saveFile [ " Save Name " ] . SetString ( std : : string ( GetSaveFileName ( ) ) ) ;
saveFile [ " Game Time " ] . SetReal ( game - > GetRuntime ( ) ) ;
saveFile [ " TravelingMerchant " ] . SetString ( std : : string ( Merchant : : GetCurrentTravelingMerchant ( ) . GetKeyName ( ) ) ) ;
for ( auto & [ taskName , task ] : Tutorial : : taskList ) {
saveFile [ " Tutorial " ] [ std : : to_string ( int ( taskName ) ) ] . SetBool ( Tutorial : : TaskIsComplete ( taskName ) ) ;
}
# pragma region Save Keyboard / Controller mappings
//NOTE: We are shadowing code from InputKeyboardWindow! If at some point the retrival method for getting input displays changes, we likely will be changing the code here as well!
//ALSO NOTE: The menu inputs are saved to the system file while gameplay inputs are per-character and saved to the character settings file!
const int menuRowCount = DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) % 2 = = 0 ? DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) / 2 : DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) / 2 + 1 ;
const int menuColCount = 2 ;
for ( int row = 0 ; row < menuRowCount ; row + + ) {
for ( int col = 0 ; col < menuColCount ; col + + ) {
int inputID = row * menuColCount + col ;
if ( DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) % 2 = = 1 & & col = = 1 & & row = = menuRowCount - 1 ) continue ; //We only continue on a blank space when we have an odd number of elements.
saveSystemFile [ " Menu Keyboard Input_ " + " Inputs.Menu Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( KEY ) . value ( ) . GetKeyCode ( ) ) ;
saveSystemFile [ " Menu Controller Input_ " + " Inputs.Menu Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( CONTROLLER ) . value ( ) . GetKeyCode ( ) ) ;
saveFile [ " Overworld Map Location " ] . SetString ( State_OverworldMap : : GetCurrentConnectionPoint ( ) . name ) ;
saveFile [ " Chapter " ] . SetInt ( game - > GetCurrentChapter ( ) ) ;
saveFile [ " Save Name " ] . SetString ( std : : string ( GetSaveFileName ( ) ) ) ;
saveFile [ " Game Time " ] . SetReal ( game - > GetRuntime ( ) ) ;
saveFile [ " TravelingMerchant " ] . SetString ( std : : string ( Merchant : : GetCurrentTravelingMerchant ( ) . GetKeyName ( ) ) ) ;
# pragma region Save Keyboard / Controller mappings
//NOTE: We are shadowing code from InputKeyboardWindow! If at some point the retrival method for getting input displays changes, we likely will be changing the code here as well!
//ALSO NOTE: The menu inputs are saved to the system file while gameplay inputs are per-character and saved to the character settings file!
const int menuRowCount = DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) % 2 = = 0 ? DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) / 2 : DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) / 2 + 1 ;
const int menuColCount = 2 ;
for ( int row = 0 ; row < menuRowCount ; row + + ) {
for ( int col = 0 ; col < menuColCount ; col + + ) {
int inputID = row * menuColCount + col ;
if ( DATA . GetProperty ( " Inputs.Menu Input Names " ) . GetValueCount ( ) % 2 = = 1 & & col = = 1 & & row = = menuRowCount - 1 ) continue ; //We only continue on a blank space when we have an odd number of elements.
saveSystemFile [ " Menu Keyboard Input_ " + " Inputs.Menu Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( KEY ) . value ( ) . GetKeyCode ( ) ) ;
saveSystemFile [ " Menu Controller Input_ " + " Inputs.Menu Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( CONTROLLER ) . value ( ) . GetKeyCode ( ) ) ;
}
}
}
const int ingameControlsRowCount = DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) % 2 = = 0 ? DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) / 2 : DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) / 2 + 1 ;
const int ingameControlsColCount = 2 ;
for ( int row = 0 ; row < ingameControlsRowCount ; row + + ) {
for ( int col = 0 ; col < ingameControlsColCount ; col + + ) {
int inputID = row * menuColCount + col ;
if ( DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) % 2 = = 1 & & col = = 1 & & row = = ingameControlsRowCount - 1 ) continue ; //We only continue on a blank space when we have an odd number of elements.
saveFile [ " Gameplay Keyboard Input_ " + " Inputs.Gameplay Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Gameplay Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( KEY ) . value ( ) . GetKeyCode ( ) ) ;
saveFile [ " Gameplay Controller Input_ " + " Inputs.Gameplay Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Gameplay Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( CONTROLLER ) . value ( ) . GetKeyCode ( ) ) ;
const int ingameControlsRowCount = DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) % 2 = = 0 ? DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) / 2 : DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) / 2 + 1 ;
const int ingameControlsColCount = 2 ;
for ( int row = 0 ; row < ingameControlsRowCount ; row + + ) {
for ( int col = 0 ; col < ingameControlsColCount ; col + + ) {
int inputID = row * menuColCount + col ;
if ( DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) % 2 = = 1 & & col = = 1 & & row = = ingameControlsRowCount - 1 ) continue ; //We only continue on a blank space when we have an odd number of elements.
saveFile [ " Gameplay Keyboard Input_ " + " Inputs.Gameplay Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Gameplay Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( KEY ) . value ( ) . GetKeyCode ( ) ) ;
saveFile [ " Gameplay Controller Input_ " + " Inputs.Gameplay Input Names " _s [ inputID ] ] . SetInt ( Component < InputDisplayComponent > ( INPUT_KEY_DISPLAY , std : : format ( " Input_{}_{} Gameplay Input Displayer " , row , col ) ) - > GetInput ( ) . GetPrimaryKey ( CONTROLLER ) . value ( ) . GetKeyCode ( ) ) ;
}
}
# pragma endregion
# pragma region Save System Settings
saveSystemFile [ " BGM Level " ] . SetReal ( Audio : : GetBGMVolume ( ) ) ;
saveSystemFile [ " SFX Level " ] . SetReal ( Audio : : GetSFXVolume ( ) ) ;
saveSystemFile [ " Show Max Health " ] . SetBool ( GameSettings : : ShowMaxHealth ( ) ) ;
saveSystemFile [ " Show Max Mana " ] . SetBool ( GameSettings : : ShowMaxMana ( ) ) ;
saveSystemFile [ " Screen Shake " ] . SetBool ( GameSettings : : ScreenShakeEnabled ( ) ) ;
saveSystemFile [ " Controller Rumble " ] . SetBool ( GameSettings : : RumbleEnabled ( ) ) ;
saveSystemFile [ " Terrain Collision Boxes " ] . SetBool ( GameSettings : : TerrainCollisionBoxesEnabled ( ) ) ;
saveSystemFile [ " Keyboard Auto-Aim " ] . SetBool ( GameSettings : : KeyboardAutoAimEnabled ( ) ) ;
saveSystemFile [ " Controller Icons " ] . SetInt ( int ( GameSettings : : GetIconType ( ) ) ) ;
saveSystemFile [ " VSync " ] . SetBool ( GameSettings : : VSyncEnabled ( ) ) ;
saveSystemFile [ " Window Pos " ] . SetInt ( game - > GetActualWindowPos ( ) . x , 0 ) ;
saveSystemFile [ " Window Pos " ] . SetInt ( game - > GetActualWindowPos ( ) . y , 1 ) ;
saveSystemFile [ " Window Size " ] . SetInt ( game - > GetWindowSize ( ) . x , 0 ) ;
saveSystemFile [ " Window Size " ] . SetInt ( game - > GetWindowSize ( ) . y , 1 ) ;
saveSystemFile [ " Fullscreen " ] . SetBool ( game - > IsFullscreen ( ) ) ;
# pragma endregion
saveFile [ " Hash " ] . SetString ( " " ) ;
for ( auto & [ mapName , chunks ] : game - > minimap . GetChunkData ( ) ) {
size_t chunkInd = 0 ;
for ( auto & chunk : chunks ) {
saveFile [ " Minimap " ] [ mapName ] . SetString ( chunk , chunkInd ) ;
chunkInd + + ;
}
}
# pragma endregion
# pragma region Save System Settings
saveSystemFile [ " BGM Level " ] . SetReal ( Audio : : GetBGMVolume ( ) ) ;
saveSystemFile [ " SFX Level " ] . SetReal ( Audio : : GetSFXVolume ( ) ) ;
saveSystemFile [ " Show Max Health " ] . SetBool ( GameSettings : : ShowMaxHealth ( ) ) ;
saveSystemFile [ " Show Max Mana " ] . SetBool ( GameSettings : : ShowMaxMana ( ) ) ;
saveSystemFile [ " Screen Shake " ] . SetBool ( GameSettings : : ScreenShakeEnabled ( ) ) ;
saveSystemFile [ " Controller Rumble " ] . SetBool ( GameSettings : : RumbleEnabled ( ) ) ;
saveSystemFile [ " Terrain Collision Boxes " ] . SetBool ( GameSettings : : TerrainCollisionBoxesEnabled ( ) ) ;
saveSystemFile [ " Keyboard Auto-Aim " ] . SetBool ( GameSettings : : KeyboardAutoAimEnabled ( ) ) ;
saveSystemFile [ " Controller Icons " ] . SetInt ( int ( GameSettings : : GetIconType ( ) ) ) ;
saveSystemFile [ " VSync " ] . SetBool ( GameSettings : : VSyncEnabled ( ) ) ;
saveSystemFile [ " Window Pos " ] . SetInt ( game - > GetActualWindowPos ( ) . x , 0 ) ;
saveSystemFile [ " Window Pos " ] . SetInt ( game - > GetActualWindowPos ( ) . y , 1 ) ;
saveSystemFile [ " Window Size " ] . SetInt ( game - > GetWindowSize ( ) . x , 0 ) ;
saveSystemFile [ " Window Size " ] . SetInt ( game - > GetWindowSize ( ) . y , 1 ) ;
saveSystemFile [ " Fullscreen " ] . SetBool ( game - > IsFullscreen ( ) ) ;
# pragma endregion
saveFile [ " Hash " ] . SetString ( " " ) ;
utils : : datafile : : Write ( saveFile , " save_file_path " _S + std : : format ( " save.{:04} " , saveFileID ) ) ;
std : : string fileHash = util : : GetHash ( " save_file_path " _S + std : : format ( " save.{:04} " , saveFileID ) ) ;
saveFile [ " Hash " ] . SetString ( fileHash ) ;
utils : : datafile : : Write ( saveFile , " save_file_path " _S + std : : format ( " save.{:04} " , saveFileID ) ) ; //Once the hash has been computed and added, save the file a second time.
# pragma region Save save file and prep File Hash
utils : : datafile : : Write ( saveFile , " save_file_path " _S + std : : format ( " save.{:04} " , saveFileID ) ) ;
std : : string fileHash = util : : GetHash ( " save_file_path " _S + std : : format ( " save.{:04} " , saveFileID ) ) ;
saveFile [ " Hash " ] . SetString ( fileHash ) ;
utils : : datafile : : Write ( saveFile , " save_file_path " _S + std : : format ( " save.{:04} " , saveFileID ) ) ; //Once the hash has been computed and added, save the file a second time.
# pragma endregion
//WARNING! DO NOT WRITE ANY CODE BELOW HERE!!!!! THE HASH HAS ALREADY BEEN WRITTEN.
//FILES BECOME CORRUPTED IF THE SAVE FILE IS MODIFIED FROM HERE ONWARDS.
}
utils : : datafile : : Write ( saveSystemFile , " save_file_path " _S + " system.conf " ) ;
utils : : datafile metadata ;
if ( onlineMode ) {
@ -379,6 +394,15 @@ void SaveFile::LoadFile(){
game - > GetPlayer ( ) - > RecalculateEquipStats ( ) ;
if ( loadFile . HasProperty ( " TravelingMerchant " ) ) Merchant : : SetTravelingMerchant ( loadFile [ " TravelingMerchant " ] . GetString ( ) ) ;
if ( loadFile . HasProperty ( " Minimap " ) ) {
for ( auto & [ key , size ] : loadFile [ " Minimap " ] . GetKeys ( ) ) {
for ( const std : : string & chunk : loadFile [ " Minimap " ] [ key ] . GetValues ( ) ) {
vi2d chunkPos = { stoi ( chunk . substr ( 0 , chunk . find ( ' _ ' ) ) ) , stoi ( chunk . substr ( chunk . find ( ' _ ' ) + 1 ) ) } ;
game - > minimap . UpdateChunk ( key , chunkPos ) ;
}
}
}
# pragma region Load Keyboard / Controller mappings
//NOTE: We are shadowing code from InputKeyboardWindow! If at some point the retrival method for getting input displays changes, we likely will be changing the code here as well!
const int ingameControlsRowCount = DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) % 2 = = 0 ? DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) / 2 : DATA . GetProperty ( " Inputs.Gameplay Input Names " ) . GetValueCount ( ) / 2 + 1 ;