2013-11-16 09:31:46 +00:00
# include "statemanagerimp.hpp"
2018-08-14 19:05:43 +00:00
# include <components/debug/debuglog.hpp>
2013-11-19 14:38:26 +00:00
# include <components/esm/esmwriter.hpp>
2013-11-21 11:24:24 +00:00
# include <components/esm/esmreader.hpp>
2014-01-16 11:03:23 +00:00
# include <components/esm/cellid.hpp>
# include <components/esm/loadcell.hpp>
2013-11-19 14:38:26 +00:00
2015-07-09 17:22:04 +00:00
# include <components/loadinglistener/loadinglistener.hpp>
2013-11-26 09:37:58 +00:00
# include <components/settings/settings.hpp>
2015-06-03 14:40:16 +00:00
# include <osg/Image>
# include <osgDB/Registry>
2014-01-24 16:49:16 +00:00
2014-05-19 11:43:25 +00:00
# include <boost/filesystem/fstream.hpp>
2015-01-31 22:27:34 +00:00
# include <boost/filesystem/operations.hpp>
2014-05-19 11:43:25 +00:00
2013-11-16 11:22:28 +00:00
# include "../mwbase/environment.hpp"
# include "../mwbase/world.hpp"
# include "../mwbase/journal.hpp"
# include "../mwbase/dialoguemanager.hpp"
# include "../mwbase/windowmanager.hpp"
2013-12-07 12:17:28 +00:00
# include "../mwbase/mechanicsmanager.hpp"
2013-12-12 12:15:38 +00:00
# include "../mwbase/scriptmanager.hpp"
2014-01-21 13:13:13 +00:00
# include "../mwbase/soundmanager.hpp"
2014-03-09 02:34:49 +00:00
# include "../mwbase/inputmanager.hpp"
2013-11-16 11:22:28 +00:00
2013-11-21 09:53:42 +00:00
# include "../mwworld/player.hpp"
# include "../mwworld/class.hpp"
2014-02-23 19:11:05 +00:00
# include "../mwworld/cellstore.hpp"
2014-12-19 10:26:54 +00:00
# include "../mwworld/esmstore.hpp"
2013-11-21 09:53:42 +00:00
# include "../mwmechanics/npcstats.hpp"
2015-08-21 09:12:39 +00:00
# include "../mwmechanics/actorutil.hpp"
2013-11-21 09:53:42 +00:00
2013-12-12 12:15:38 +00:00
# include "../mwscript/globalscripts.hpp"
2018-01-13 01:02:43 +00:00
# include "quicksavemanager.hpp"
2014-01-23 10:29:40 +00:00
void MWState : : StateManager : : cleanup ( bool force )
2013-11-28 10:22:34 +00:00
{
2014-01-23 10:29:40 +00:00
if ( mState ! = State_NoGame | | force )
2013-11-28 10:22:34 +00:00
{
2014-01-21 13:13:13 +00:00
MWBase : : Environment : : get ( ) . getSoundManager ( ) - > clear ( ) ;
2013-11-28 10:22:34 +00:00
MWBase : : Environment : : get ( ) . getDialogueManager ( ) - > clear ( ) ;
MWBase : : Environment : : get ( ) . getJournal ( ) - > clear ( ) ;
2020-07-14 06:53:01 +00:00
MWBase : : Environment : : get ( ) . getScriptManager ( ) - > clear ( ) ;
2014-01-25 12:34:56 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > clear ( ) ;
2020-11-18 16:28:09 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > clear ( ) ;
2014-03-09 02:34:49 +00:00
MWBase : : Environment : : get ( ) . getInputManager ( ) - > clear ( ) ;
2014-06-12 23:24:58 +00:00
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > clear ( ) ;
2013-12-12 12:15:38 +00:00
2013-11-28 10:22:34 +00:00
mState = State_NoGame ;
2018-10-09 06:21:12 +00:00
mCharacterManager . setCurrentCharacter ( nullptr ) ;
2013-11-28 10:22:34 +00:00
mTimePlayed = 0 ;
2014-04-29 17:56:33 +00:00
MWMechanics : : CreatureStats : : cleanup ( ) ;
2013-11-28 10:22:34 +00:00
}
}
2014-01-27 12:27:42 +00:00
std : : map < int , int > MWState : : StateManager : : buildContentFileIndexMap ( const ESM : : ESMReader & reader )
const
{
const std : : vector < std : : string > & current =
MWBase : : Environment : : get ( ) . getWorld ( ) - > getContentFiles ( ) ;
const std : : vector < ESM : : Header : : MasterData > & prev = reader . getGameFiles ( ) ;
std : : map < int , int > map ;
for ( int iPrev = 0 ; iPrev < static_cast < int > ( prev . size ( ) ) ; + + iPrev )
{
std : : string id = Misc : : StringUtils : : lowerCase ( prev [ iPrev ] . name ) ;
for ( int iCurrent = 0 ; iCurrent < static_cast < int > ( current . size ( ) ) ; + + iCurrent )
if ( id = = Misc : : StringUtils : : lowerCase ( current [ iCurrent ] ) )
{
map . insert ( std : : make_pair ( iPrev , iCurrent ) ) ;
break ;
}
}
return map ;
}
2013-11-25 12:00:05 +00:00
MWState : : StateManager : : StateManager ( const boost : : filesystem : : path & saves , const std : : string & game )
2013-12-19 20:08:34 +00:00
: mQuitRequest ( false ) , mAskLoadRecent ( false ) , mState ( State_NoGame ) , mCharacterManager ( saves , game ) , mTimePlayed ( 0 )
2013-11-16 09:31:46 +00:00
{
2013-11-16 10:07:23 +00:00
}
void MWState : : StateManager : : requestQuit ( )
{
mQuitRequest = true ;
}
bool MWState : : StateManager : : hasQuitRequest ( ) const
{
return mQuitRequest ;
2013-11-16 11:22:28 +00:00
}
2013-12-19 20:08:34 +00:00
void MWState : : StateManager : : askLoadRecent ( )
{
if ( MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getMode ( ) = = MWGui : : GM_MainMenu )
return ;
if ( ! mAskLoadRecent )
{
2016-03-13 23:00:11 +00:00
const MWState : : Character * character = getCurrentCharacter ( ) ;
2016-03-13 22:47:57 +00:00
if ( ! character | | character - > begin ( ) = = character - > end ( ) ) //no saves
2013-12-19 20:08:34 +00:00
{
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > pushGuiMode ( MWGui : : GM_MainMenu ) ;
}
else
{
2016-03-13 22:47:57 +00:00
MWState : : Slot lastSave = * character - > begin ( ) ;
2013-12-19 20:08:34 +00:00
std : : vector < std : : string > buttons ;
2020-10-17 08:26:35 +00:00
buttons . emplace_back ( " #{sYes} " ) ;
buttons . emplace_back ( " #{sNo} " ) ;
2013-12-20 12:04:59 +00:00
std : : string tag ( " %s " ) ;
std : : string message = MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getGameSettingString ( " sLoadLastSaveMsg " , tag ) ;
size_t pos = message . find ( tag ) ;
message . replace ( pos , tag . length ( ) , lastSave . mProfile . mDescription ) ;
2015-01-10 22:21:39 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > interactiveMessageBox ( message , buttons ) ;
2013-12-19 20:08:34 +00:00
mAskLoadRecent = true ;
}
}
}
2013-11-18 14:15:47 +00:00
MWState : : StateManager : : State MWState : : StateManager : : getState ( ) const
2013-11-16 11:22:28 +00:00
{
2013-11-18 14:15:47 +00:00
return mState ;
2013-11-16 11:22:28 +00:00
}
void MWState : : StateManager : : newGame ( bool bypass )
{
2013-11-28 10:22:34 +00:00
cleanup ( ) ;
2013-11-16 11:22:28 +00:00
if ( ! bypass )
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > setNewGame ( true ) ;
2014-07-25 10:17:15 +00:00
2015-06-15 14:19:05 +00:00
try
{
2020-05-09 17:17:49 +00:00
Log ( Debug : : Info ) < < " Starting a new game " ;
2015-06-15 14:19:05 +00:00
MWBase : : Environment : : get ( ) . getScriptManager ( ) - > getGlobalScripts ( ) . addStartup ( ) ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > startNewGame ( bypass ) ;
mState = State_Running ;
2017-07-23 07:04:58 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > fadeScreenOut ( 0 ) ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > fadeScreenIn ( 1 ) ;
2015-06-15 14:19:05 +00:00
}
catch ( std : : exception & e )
{
std : : stringstream error ;
error < < " Failed to start new game: " < < e . what ( ) ;
2014-07-25 10:17:15 +00:00
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < error . str ( ) ;
2015-06-15 14:19:05 +00:00
cleanup ( true ) ;
2014-07-25 10:23:18 +00:00
2015-06-15 14:19:05 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > pushGuiMode ( MWGui : : GM_MainMenu ) ;
std : : vector < std : : string > buttons ;
2020-10-17 08:26:35 +00:00
buttons . emplace_back ( " #{sOk} " ) ;
2015-06-15 14:19:05 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > interactiveMessageBox ( error . str ( ) , buttons ) ;
}
2013-11-16 11:22:28 +00:00
}
2013-11-18 14:38:08 +00:00
void MWState : : StateManager : : endGame ( )
{
mState = State_Ended ;
}
2013-11-19 14:38:26 +00:00
2018-07-26 16:54:08 +00:00
void MWState : : StateManager : : resumeGame ( )
{
mState = State_Running ;
}
2013-11-24 14:19:56 +00:00
void MWState : : StateManager : : saveGame ( const std : : string & description , const Slot * slot )
2013-11-19 14:38:26 +00:00
{
2016-03-13 23:00:11 +00:00
MWState : : Character * character = getCurrentCharacter ( ) ;
2014-06-02 12:46:41 +00:00
try
{
2016-03-13 23:03:02 +00:00
if ( ! character )
{
MWWorld : : ConstPtr player = MWMechanics : : getPlayer ( ) ;
std : : string name = player . get < ESM : : NPC > ( ) - > mBase - > mName ;
character = mCharacterManager . createCharacter ( name ) ;
mCharacterManager . setCurrentCharacter ( character ) ;
}
2014-06-02 12:46:41 +00:00
ESM : : SavedGame profile ;
2013-11-19 14:38:26 +00:00
2014-06-02 12:46:41 +00:00
MWBase : : World & world = * MWBase : : Environment : : get ( ) . getWorld ( ) ;
2013-11-21 09:53:42 +00:00
2014-06-02 12:46:41 +00:00
MWWorld : : Ptr player = world . getPlayerPtr ( ) ;
2013-11-21 09:53:42 +00:00
2014-06-02 12:46:41 +00:00
profile . mContentFiles = world . getContentFiles ( ) ;
2013-11-25 12:00:05 +00:00
2014-09-13 00:58:01 +00:00
profile . mPlayerName = player . get < ESM : : NPC > ( ) - > mBase - > mName ;
2014-06-02 12:46:41 +00:00
profile . mPlayerLevel = player . getClass ( ) . getNpcStats ( player ) . getLevel ( ) ;
2014-03-27 21:32:42 +00:00
2014-06-02 12:46:41 +00:00
std : : string classId = player . get < ESM : : NPC > ( ) - > mBase - > mClass ;
if ( world . getStore ( ) . get < ESM : : Class > ( ) . isDynamic ( classId ) )
profile . mPlayerClassName = world . getStore ( ) . get < ESM : : Class > ( ) . find ( classId ) - > mName ;
else
profile . mPlayerClassId = classId ;
profile . mPlayerCell = world . getCellName ( ) ;
2020-06-03 07:32:28 +00:00
profile . mInGameTime = world . getEpochTimeStamp ( ) ;
2014-06-02 12:46:41 +00:00
profile . mTimePlayed = mTimePlayed ;
profile . mDescription = description ;
2020-05-09 17:17:49 +00:00
Log ( Debug : : Info ) < < " Making a screenshot for saved game ' " < < description < < " ' " ; ;
2015-06-03 14:40:16 +00:00
writeScreenshot ( profile . mScreenshot ) ;
2014-06-02 12:46:41 +00:00
if ( ! slot )
2016-03-13 23:00:11 +00:00
slot = character - > createSlot ( profile ) ;
2014-06-02 12:46:41 +00:00
else
2016-03-13 23:00:11 +00:00
slot = character - > updateSlot ( slot , profile ) ;
2013-11-19 14:38:26 +00:00
2016-07-30 17:24:03 +00:00
// Make sure the animation state held by references is up to date before saving the game.
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > persistAnimationStates ( ) ;
2020-05-12 10:51:07 +00:00
Log ( Debug : : Info ) < < " Writing saved game ' " < < description < < " ' for character ' " < < profile . mPlayerName < < " ' " ;
2020-05-09 17:17:49 +00:00
2015-12-05 00:05:24 +00:00
// Write to a memory stream first. If there is an exception during the save process, we don't want to trash the
// existing save file we are overwriting.
std : : stringstream stream ;
2014-01-27 12:27:42 +00:00
2014-06-02 12:46:41 +00:00
ESM : : ESMWriter writer ;
2014-01-27 12:27:42 +00:00
2020-05-29 08:26:02 +00:00
for ( const std : : string & contentFile : MWBase : : Environment : : get ( ) . getWorld ( ) - > getContentFiles ( ) )
writer . addMaster ( contentFile , 0 ) ; // not using the size information anyway -> use value of 0
2014-06-02 12:46:41 +00:00
2015-06-30 15:26:33 +00:00
writer . setFormat ( ESM : : SavedGame : : sCurrentFormat ) ;
2015-06-30 01:54:56 +00:00
2014-06-24 22:11:25 +00:00
// all unused
2015-06-30 15:26:33 +00:00
writer . setVersion ( 0 ) ;
2014-06-24 22:11:25 +00:00
writer . setType ( 0 ) ;
writer . setAuthor ( " " ) ;
writer . setDescription ( " " ) ;
2014-06-02 12:46:41 +00:00
int recordCount = 1 // saved game header
+ MWBase : : Environment : : get ( ) . getJournal ( ) - > countSavedGameRecords ( )
+ MWBase : : Environment : : get ( ) . getWorld ( ) - > countSavedGameRecords ( )
+ MWBase : : Environment : : get ( ) . getScriptManager ( ) - > getGlobalScripts ( ) . countSavedGameRecords ( )
+ MWBase : : Environment : : get ( ) . getDialogueManager ( ) - > countSavedGameRecords ( )
2014-06-12 23:24:58 +00:00
+ MWBase : : Environment : : get ( ) . getWindowManager ( ) - > countSavedGameRecords ( )
2016-10-20 00:12:01 +00:00
+ MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > countSavedGameRecords ( )
+ MWBase : : Environment : : get ( ) . getInputManager ( ) - > countSavedGameRecords ( ) ;
2014-06-02 12:46:41 +00:00
writer . setRecordCount ( recordCount ) ;
2014-01-27 12:27:42 +00:00
2014-06-02 12:46:41 +00:00
writer . save ( stream ) ;
2014-01-27 12:27:42 +00:00
2014-06-02 12:46:41 +00:00
Loading : : Listener & listener = * MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getLoadingScreen ( ) ;
2018-10-28 07:44:14 +00:00
int messagesCount = MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getMessagesCount ( ) ;
2015-01-11 17:01:06 +00:00
// Using only Cells for progress information, since they typically have the largest records by far
listener . setProgressRange ( MWBase : : Environment : : get ( ) . getWorld ( ) - > countSavedGameCells ( ) ) ;
2018-10-28 07:44:14 +00:00
listener . setLabel ( " #{sNotifyMessage4} " , true , messagesCount > 0 ) ;
2013-12-03 13:28:46 +00:00
2014-06-02 12:46:41 +00:00
Loading : : ScopedLoad load ( & listener ) ;
2013-12-03 13:28:46 +00:00
2014-06-02 12:46:41 +00:00
writer . startRecord ( ESM : : REC_SAVE ) ;
slot - > mProfile . save ( writer ) ;
writer . endRecord ( ESM : : REC_SAVE ) ;
2014-04-28 09:29:57 +00:00
2014-06-02 12:46:41 +00:00
MWBase : : Environment : : get ( ) . getJournal ( ) - > write ( writer , listener ) ;
MWBase : : Environment : : get ( ) . getDialogueManager ( ) - > write ( writer , listener ) ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > write ( writer , listener ) ;
MWBase : : Environment : : get ( ) . getScriptManager ( ) - > getGlobalScripts ( ) . write ( writer , listener ) ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > write ( writer , listener ) ;
2014-06-12 23:24:58 +00:00
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > write ( writer , listener ) ;
2016-10-20 00:12:01 +00:00
MWBase : : Environment : : get ( ) . getInputManager ( ) - > write ( writer , listener ) ;
2014-04-28 09:29:57 +00:00
2014-06-02 12:46:41 +00:00
// Ensure we have written the number of records that was estimated
if ( writer . getRecordCount ( ) ! = recordCount + 1 ) // 1 extra for TES3 record
2018-08-14 19:05:43 +00:00
Log ( Debug : : Warning ) < < " Warning: number of written savegame records does not match. Estimated: " < < recordCount + 1 < < " , written: " < < writer . getRecordCount ( ) ;
2014-04-28 09:29:57 +00:00
2014-06-02 12:46:41 +00:00
writer . close ( ) ;
2013-11-28 10:22:34 +00:00
2014-06-07 15:48:40 +00:00
if ( stream . fail ( ) )
2015-12-05 00:05:24 +00:00
throw std : : runtime_error ( " Write operation failed (memory stream) " ) ;
// All good, write to file
boost : : filesystem : : ofstream filestream ( slot - > mPath , std : : ios : : binary ) ;
filestream < < stream . rdbuf ( ) ;
if ( filestream . fail ( ) )
throw std : : runtime_error ( " Write operation failed (file stream) " ) ;
2014-06-07 15:48:40 +00:00
2014-06-02 12:46:41 +00:00
Settings : : Manager : : setString ( " character " , " Saves " ,
slot - > mPath . parent_path ( ) . filename ( ) . string ( ) ) ;
}
catch ( const std : : exception & e )
{
std : : stringstream error ;
error < < " Failed to save game: " < < e . what ( ) ;
2013-11-28 10:22:34 +00:00
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < error . str ( ) ;
2013-11-26 09:37:58 +00:00
2014-06-02 12:46:41 +00:00
std : : vector < std : : string > buttons ;
2020-10-17 08:26:35 +00:00
buttons . emplace_back ( " #{sOk} " ) ;
2015-01-10 22:21:39 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > interactiveMessageBox ( error . str ( ) , buttons ) ;
2014-06-07 15:48:40 +00:00
// If no file was written, clean up the slot
2016-03-13 23:03:02 +00:00
if ( character & & slot & & ! boost : : filesystem : : exists ( slot - > mPath ) )
2016-03-13 23:00:11 +00:00
{
character - > deleteSlot ( slot ) ;
character - > cleanup ( ) ;
}
2014-06-02 12:46:41 +00:00
}
2013-11-19 14:38:26 +00:00
}
2014-04-24 08:14:17 +00:00
void MWState : : StateManager : : quickSave ( std : : string name )
{
2014-05-02 09:20:43 +00:00
if ( ! ( mState = = State_Running & &
MWBase : : Environment : : get ( ) . getWorld ( ) - > getGlobalInt ( " chargenstate " ) = = - 1 // char gen
& & MWBase : : Environment : : get ( ) . getWindowManager ( ) - > isSavingAllowed ( ) ) )
2014-04-25 06:39:40 +00:00
{
//You can not save your game right now
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > messageBox ( " #{sSaveGameDenied} " ) ;
2014-04-24 08:14:17 +00:00
return ;
2014-04-24 07:06:36 +00:00
}
2014-04-24 07:54:47 +00:00
2018-01-13 01:02:43 +00:00
int maxSaves = Settings : : Manager : : getInt ( " max quicksaves " , " Saves " ) ;
if ( maxSaves < 1 )
maxSaves = 1 ;
2016-03-13 23:00:11 +00:00
Character * currentCharacter = getCurrentCharacter ( ) ; //Get current character
2018-01-13 01:02:43 +00:00
QuickSaveManager saveFinder = QuickSaveManager ( name , maxSaves ) ;
2014-04-24 08:14:17 +00:00
2016-03-13 23:00:11 +00:00
if ( currentCharacter )
2014-04-24 08:14:17 +00:00
{
2020-05-29 08:26:02 +00:00
for ( auto & save : * currentCharacter )
2016-03-13 23:00:11 +00:00
{
2018-01-13 01:02:43 +00:00
//Visiting slots allows the quicksave finder to find the oldest quicksave
2020-05-29 08:26:02 +00:00
saveFinder . visitSave ( & save ) ;
2016-03-13 23:00:11 +00:00
}
2014-04-24 07:06:36 +00:00
}
2014-04-24 08:14:17 +00:00
2018-01-13 01:02:43 +00:00
//Once all the saves have been visited, the save finder can tell us which
//one to replace (or create)
2018-02-14 02:01:15 +00:00
saveGame ( name , saveFinder . getNextQuickSaveSlot ( ) ) ;
2014-04-24 07:06:36 +00:00
}
2015-01-07 02:03:56 +00:00
void MWState : : StateManager : : loadGame ( const std : : string & filepath )
{
2020-05-29 08:26:02 +00:00
for ( const auto & character : mCharacterManager )
2015-01-07 02:03:56 +00:00
{
2020-05-29 08:26:02 +00:00
for ( const auto & slot : character )
2015-01-07 02:03:56 +00:00
{
if ( slot . mPath = = boost : : filesystem : : path ( filepath ) )
{
loadGame ( & character , slot . mPath . string ( ) ) ;
return ;
}
}
}
2016-03-13 23:00:11 +00:00
MWState : : Character * character = getCurrentCharacter ( ) ;
2015-01-07 02:03:56 +00:00
loadGame ( character , filepath ) ;
}
void MWState : : StateManager : : loadGame ( const Character * character , const std : : string & filepath )
2013-11-21 11:24:24 +00:00
{
2014-01-23 10:29:40 +00:00
try
2013-12-03 13:28:46 +00:00
{
2014-01-23 10:29:40 +00:00
cleanup ( ) ;
2013-12-07 12:17:28 +00:00
2020-05-12 10:51:07 +00:00
Log ( Debug : : Info ) < < " Reading save file " < < boost : : filesystem : : path ( filepath ) . filename ( ) . string ( ) ;
2020-05-09 17:17:49 +00:00
2014-01-23 10:29:40 +00:00
ESM : : ESMReader reader ;
2015-01-07 02:03:56 +00:00
reader . open ( filepath ) ;
2013-12-15 15:16:50 +00:00
2015-06-30 15:26:42 +00:00
if ( reader . getFormat ( ) > ESM : : SavedGame : : sCurrentFormat )
throw std : : runtime_error ( " This save file was created using a newer version of OpenMW and is thus not supported. Please upgrade to the newest OpenMW version to load this file. " ) ;
2014-01-27 12:27:42 +00:00
std : : map < int , int > contentFileMap = buildContentFileIndexMap ( reader ) ;
2014-04-28 09:29:57 +00:00
Loading : : Listener & listener = * MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getLoadingScreen ( ) ;
2018-10-28 07:44:14 +00:00
int messagesCount = MWBase : : Environment : : get ( ) . getWindowManager ( ) - > getMessagesCount ( ) ;
2014-04-28 09:29:57 +00:00
2015-01-11 16:54:15 +00:00
listener . setProgressRange ( 100 ) ;
2018-10-28 07:44:14 +00:00
listener . setLabel ( " #{sLoadingMessage14} " , false , messagesCount > 0 ) ;
2014-04-28 09:29:57 +00:00
Loading : : ScopedLoad load ( & listener ) ;
2015-01-11 19:07:17 +00:00
bool firstPersonCam = false ;
2015-01-11 16:54:15 +00:00
size_t total = reader . getFileSize ( ) ;
int currentPercent = 0 ;
2014-01-23 10:29:40 +00:00
while ( reader . hasMoreRecs ( ) )
{
ESM : : NAME n = reader . getRecName ( ) ;
reader . getRecHeader ( ) ;
2016-05-07 17:32:51 +00:00
switch ( n . intval )
2014-01-23 10:29:40 +00:00
{
case ESM : : REC_SAVE :
2015-01-07 02:03:56 +00:00
{
ESM : : SavedGame profile ;
profile . load ( reader ) ;
2015-01-10 22:58:55 +00:00
if ( ! verifyProfile ( profile ) )
{
cleanup ( true ) ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > pushGuiMode ( MWGui : : GM_MainMenu ) ;
return ;
}
2015-01-07 02:03:56 +00:00
mTimePlayed = profile . mTimePlayed ;
2020-05-12 10:51:07 +00:00
Log ( Debug : : Info ) < < " Loading saved game ' " < < profile . mDescription < < " ' for character ' " < < profile . mPlayerName < < " ' " ;
2015-01-07 02:03:56 +00:00
}
2014-01-23 10:29:40 +00:00
break ;
case ESM : : REC_JOUR :
2015-01-22 16:47:00 +00:00
case ESM : : REC_JOUR_LEGACY :
2014-01-23 10:29:40 +00:00
case ESM : : REC_QUES :
2016-05-07 17:32:51 +00:00
MWBase : : Environment : : get ( ) . getJournal ( ) - > readRecord ( reader , n . intval ) ;
2014-01-23 10:29:40 +00:00
break ;
2014-02-16 11:54:27 +00:00
case ESM : : REC_DIAS :
2016-05-07 17:32:51 +00:00
MWBase : : Environment : : get ( ) . getDialogueManager ( ) - > readRecord ( reader , n . intval ) ;
2014-02-16 11:54:27 +00:00
break ;
2014-01-23 10:29:40 +00:00
case ESM : : REC_ALCH :
case ESM : : REC_ARMO :
case ESM : : REC_BOOK :
case ESM : : REC_CLAS :
case ESM : : REC_CLOT :
case ESM : : REC_ENCH :
case ESM : : REC_NPC_ :
case ESM : : REC_SPEL :
case ESM : : REC_WEAP :
case ESM : : REC_GLOB :
case ESM : : REC_PLAY :
case ESM : : REC_CSTA :
2014-03-20 06:25:52 +00:00
case ESM : : REC_WTHR :
2014-05-10 22:32:22 +00:00
case ESM : : REC_DYNA :
2014-05-14 07:47:49 +00:00
case ESM : : REC_ACTC :
2014-05-17 03:21:17 +00:00
case ESM : : REC_PROJ :
case ESM : : REC_MPRJ :
2014-09-21 11:37:20 +00:00
case ESM : : REC_ENAB :
2014-12-17 00:05:32 +00:00
case ESM : : REC_LEVC :
case ESM : : REC_LEVI :
2020-06-02 19:59:37 +00:00
case ESM : : REC_CREA :
2020-10-29 12:55:24 +00:00
case ESM : : REC_CONT :
2016-05-07 17:32:51 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > readRecord ( reader , n . intval , contentFileMap ) ;
2015-01-11 19:07:17 +00:00
break ;
2014-01-23 10:29:40 +00:00
2015-01-11 19:07:17 +00:00
case ESM : : REC_CAM_ :
reader . getHNT ( firstPersonCam , " FIRS " ) ;
2014-01-23 10:29:40 +00:00
break ;
case ESM : : REC_GSCR :
2021-04-07 15:30:21 +00:00
MWBase : : Environment : : get ( ) . getScriptManager ( ) - > getGlobalScripts ( ) . readRecord ( reader , n . intval , contentFileMap ) ;
2014-01-23 10:29:40 +00:00
break ;
2014-01-25 17:20:17 +00:00
case ESM : : REC_GMAP :
2014-05-01 19:16:32 +00:00
case ESM : : REC_KEYS :
2014-05-12 19:04:02 +00:00
case ESM : : REC_ASPL :
2014-08-17 03:45:35 +00:00
case ESM : : REC_MARK :
2014-05-12 19:04:02 +00:00
2016-05-07 17:32:51 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > readRecord ( reader , n . intval ) ;
2014-01-25 17:20:17 +00:00
break ;
2014-06-12 23:24:58 +00:00
case ESM : : REC_DCOU :
2015-02-04 20:18:43 +00:00
case ESM : : REC_STLN :
2014-06-12 23:24:58 +00:00
2016-05-07 17:32:51 +00:00
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > readRecord ( reader , n . intval ) ;
2014-06-12 23:24:58 +00:00
break ;
2016-10-20 00:12:01 +00:00
case ESM : : REC_INPU :
MWBase : : Environment : : get ( ) . getInputManager ( ) - > readRecord ( reader , n . intval ) ;
break ;
2014-01-23 10:29:40 +00:00
default :
// ignore invalid records
2018-08-14 19:05:43 +00:00
Log ( Debug : : Warning ) < < " Warning: Ignoring unknown record: " < < n . toString ( ) ;
2014-01-23 10:29:40 +00:00
reader . skipRecord ( ) ;
}
2015-01-11 16:54:15 +00:00
int progressPercent = static_cast < int > ( float ( reader . getFileOffset ( ) ) / total * 100 ) ;
if ( progressPercent > currentPercent )
{
listener . increaseProgress ( progressPercent - currentPercent ) ;
currentPercent = progressPercent ;
}
2013-12-03 13:28:46 +00:00
}
2013-11-21 11:24:24 +00:00
2014-01-23 10:29:40 +00:00
mCharacterManager . setCurrentCharacter ( character ) ;
2013-11-21 19:24:58 +00:00
2014-01-23 10:29:40 +00:00
mState = State_Running ;
2013-11-26 09:37:58 +00:00
2016-03-13 22:37:14 +00:00
if ( character )
Settings : : Manager : : setString ( " character " , " Saves " ,
2015-01-07 02:03:56 +00:00
character - > getPath ( ) . filename ( ) . string ( ) ) ;
2013-12-05 13:22:08 +00:00
2014-01-25 12:34:56 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > setNewGame ( false ) ;
2021-03-23 20:07:57 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > saveLoaded ( ) ;
2014-01-23 10:29:40 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > setupPlayer ( ) ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > renderPlayer ( ) ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > updatePlayer ( ) ;
MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > playerLoaded ( ) ;
2013-12-07 12:17:28 +00:00
2015-01-11 19:07:17 +00:00
if ( firstPersonCam ! = MWBase : : Environment : : get ( ) . getWorld ( ) - > isFirstPerson ( ) )
MWBase : : Environment : : get ( ) . getWorld ( ) - > togglePOV ( ) ;
2018-05-26 11:35:48 +00:00
MWWorld : : ConstPtr ptr = MWMechanics : : getPlayer ( ) ;
2014-04-24 07:06:36 +00:00
2016-04-14 22:49:36 +00:00
if ( ptr . isInCell ( ) )
{
const ESM : : CellId & cellId = ptr . getCell ( ) - > getCell ( ) - > getCellId ( ) ;
2014-01-16 11:03:23 +00:00
2016-04-14 22:49:36 +00:00
// Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again
MWBase : : Environment : : get ( ) . getWorld ( ) - > changeToCell ( cellId , ptr . getRefData ( ) . getPosition ( ) , false , false ) ;
}
else
{
// Cell no longer exists (i.e. changed game files), choose a default cell
2020-05-09 17:17:49 +00:00
Log ( Debug : : Warning ) < < " Warning: Player character's cell no longer exists, changing to the default cell " ;
2016-04-14 22:49:36 +00:00
MWWorld : : CellStore * cell = MWBase : : Environment : : get ( ) . getWorld ( ) - > getExterior ( 0 , 0 ) ;
float x , y ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > indexToPosition ( 0 , 0 , x , y , false ) ;
ESM : : Position pos ;
pos . pos [ 0 ] = x ;
pos . pos [ 1 ] = y ;
pos . pos [ 2 ] = 0 ; // should be adjusted automatically (adjustPlayerPos=true)
pos . rot [ 0 ] = 0 ;
pos . rot [ 1 ] = 0 ;
pos . rot [ 2 ] = 0 ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > changeToCell ( cell - > getCell ( ) - > getCellId ( ) , pos , true , false ) ;
}
2014-05-17 03:21:17 +00:00
2021-02-25 23:12:14 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > updateProjectilesCasters ( ) ;
2015-01-11 16:54:15 +00:00
// Vanilla MW will restart startup scripts when a save game is loaded. This is unintuitive,
2015-01-06 18:29:33 +00:00
// but some mods may be using it as a reload detector.
2014-07-25 10:17:15 +00:00
MWBase : : Environment : : get ( ) . getScriptManager ( ) - > getGlobalScripts ( ) . addStartup ( ) ;
2016-02-27 11:53:07 +00:00
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
// But make sure the flag is cleared anyway in case it was set from an earlier game.
2014-05-17 03:21:17 +00:00
MWBase : : Environment : : get ( ) . getWorld ( ) - > markCellAsUnchanged ( ) ;
2014-01-23 10:29:40 +00:00
}
catch ( const std : : exception & e )
{
2014-06-02 12:46:41 +00:00
std : : stringstream error ;
error < < " Failed to load saved game: " < < e . what ( ) ;
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < error . str ( ) ;
2014-01-23 10:29:40 +00:00
cleanup ( true ) ;
2014-05-20 15:11:26 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > pushGuiMode ( MWGui : : GM_MainMenu ) ;
2014-06-02 12:46:41 +00:00
std : : vector < std : : string > buttons ;
2020-10-17 08:26:35 +00:00
buttons . emplace_back ( " #{sOk} " ) ;
2015-01-10 22:21:39 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > interactiveMessageBox ( error . str ( ) , buttons ) ;
2014-01-23 10:29:40 +00:00
}
2013-11-21 11:24:24 +00:00
}
2014-04-24 08:14:17 +00:00
void MWState : : StateManager : : quickLoad ( )
{
2016-03-13 23:00:11 +00:00
if ( Character * currentCharacter = getCurrentCharacter ( ) )
2015-01-11 16:54:15 +00:00
{
2016-03-13 22:48:44 +00:00
if ( currentCharacter - > begin ( ) = = currentCharacter - > end ( ) )
2015-01-11 16:54:15 +00:00
return ;
2016-03-13 22:48:44 +00:00
loadGame ( currentCharacter , currentCharacter - > begin ( ) - > mPath . string ( ) ) ; //Get newest save
2015-01-11 16:54:15 +00:00
}
2014-04-24 07:14:47 +00:00
}
2014-04-28 18:57:45 +00:00
void MWState : : StateManager : : deleteGame ( const MWState : : Character * character , const MWState : : Slot * slot )
{
mCharacterManager . deleteSlot ( character , slot ) ;
}
2016-03-13 23:00:11 +00:00
MWState : : Character * MWState : : StateManager : : getCurrentCharacter ( )
2013-11-19 14:38:26 +00:00
{
2016-03-13 23:00:11 +00:00
return mCharacterManager . getCurrentCharacter ( ) ;
2013-11-21 10:10:18 +00:00
}
MWState : : StateManager : : CharacterIterator MWState : : StateManager : : characterBegin ( )
{
return mCharacterManager . begin ( ) ;
}
MWState : : StateManager : : CharacterIterator MWState : : StateManager : : characterEnd ( )
{
return mCharacterManager . end ( ) ;
}
2013-11-28 08:33:50 +00:00
void MWState : : StateManager : : update ( float duration )
{
mTimePlayed + = duration ;
2014-01-25 21:20:46 +00:00
// Note: It would be nicer to trigger this from InputManager, i.e. the very beginning of the frame update.
if ( mAskLoadRecent )
{
int iButton = MWBase : : Environment : : get ( ) . getWindowManager ( ) - > readPressedButton ( ) ;
2016-03-13 23:00:11 +00:00
MWState : : Character * curCharacter = getCurrentCharacter ( ) ;
2014-06-09 22:22:00 +00:00
if ( iButton = = 0 & & curCharacter )
2014-01-25 21:20:46 +00:00
{
mAskLoadRecent = false ;
//Load last saved game for current character
2014-06-09 22:22:00 +00:00
2014-01-25 21:20:46 +00:00
MWState : : Slot lastSave = * curCharacter - > begin ( ) ;
2015-01-07 02:03:56 +00:00
loadGame ( curCharacter , lastSave . mPath . string ( ) ) ;
2014-01-25 21:20:46 +00:00
}
else if ( iButton = = 1 )
{
mAskLoadRecent = false ;
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > pushGuiMode ( MWGui : : GM_MainMenu ) ;
}
}
2013-11-28 08:33:50 +00:00
}
2015-01-10 22:58:55 +00:00
bool MWState : : StateManager : : verifyProfile ( const ESM : : SavedGame & profile ) const
{
const std : : vector < std : : string > & selectedContentFiles = MWBase : : Environment : : get ( ) . getWorld ( ) - > getContentFiles ( ) ;
bool notFound = false ;
2020-05-29 08:26:02 +00:00
for ( const std : : string & contentFile : profile . mContentFiles )
2015-01-10 22:58:55 +00:00
{
2020-05-29 08:26:02 +00:00
if ( std : : find ( selectedContentFiles . begin ( ) , selectedContentFiles . end ( ) , contentFile )
2015-01-10 22:58:55 +00:00
= = selectedContentFiles . end ( ) )
{
2020-05-29 08:26:02 +00:00
Log ( Debug : : Warning ) < < " Warning: Saved game dependency " < < contentFile < < " is missing. " ;
2015-01-10 22:58:55 +00:00
notFound = true ;
}
}
if ( notFound )
{
std : : vector < std : : string > buttons ;
2020-10-17 08:26:35 +00:00
buttons . emplace_back ( " #{sYes} " ) ;
buttons . emplace_back ( " #{sNo} " ) ;
2015-01-10 22:58:55 +00:00
MWBase : : Environment : : get ( ) . getWindowManager ( ) - > interactiveMessageBox ( " #{sMissingMastersMsg} " , buttons , true ) ;
int selectedButton = MWBase : : Environment : : get ( ) . getWindowManager ( ) - > readPressedButton ( ) ;
if ( selectedButton = = 1 | | selectedButton = = - 1 )
return false ;
}
return true ;
}
2015-06-03 14:40:16 +00:00
void MWState : : StateManager : : writeScreenshot ( std : : vector < char > & imageData ) const
{
int screenshotW = 259 * 2 , screenshotH = 133 * 2 ; // *2 to get some nice antialiasing
osg : : ref_ptr < osg : : Image > screenshot ( new osg : : Image ) ;
MWBase : : Environment : : get ( ) . getWorld ( ) - > screenshot ( screenshot . get ( ) , screenshotW , screenshotH ) ;
osgDB : : ReaderWriter * readerwriter = osgDB : : Registry : : instance ( ) - > getReaderWriterForExtension ( " jpg " ) ;
if ( ! readerwriter )
{
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < " Error: Unable to write screenshot, can't find a jpg ReaderWriter " ;
2015-06-03 14:40:16 +00:00
return ;
}
std : : ostringstream ostream ;
osgDB : : ReaderWriter : : WriteResult result = readerwriter - > writeImage ( * screenshot , ostream ) ;
if ( ! result . success ( ) )
{
2018-08-14 19:05:43 +00:00
Log ( Debug : : Error ) < < " Error: Unable to write screenshot: " < < result . message ( ) < < " code " < < result . status ( ) ;
2015-06-03 14:40:16 +00:00
return ;
}
std : : string data = ostream . str ( ) ;
imageData = std : : vector < char > ( data . begin ( ) , data . end ( ) ) ;
}