1
1
forked from 0ad/0ad

Adds saved game descriptions and in-game save dialog, based on patch by Crynux, fixes #2030

This was SVN commit r13579.
This commit is contained in:
historic_bruno 2013-07-21 00:23:57 +00:00
parent 311403d5ef
commit e0ea53a8ee
12 changed files with 248 additions and 38 deletions

View File

@ -0,0 +1,18 @@
function sortDecreasingDate(a, b)
{
return b.metadata.time - a.metadata.time;
}
function twoDigits(n)
{
return n < 10 ? "0" + n : n;
}
function generateLabel(metadata)
{
var t = new Date(metadata.time*1000);
var date = t.getFullYear()+"-"+twoDigits(1+t.getMonth())+"-"+twoDigits(t.getDate());
var time = twoDigits(t.getHours())+":"+twoDigits(t.getMinutes())+":"+twoDigits(t.getSeconds());
return "["+date+" "+time+"] "+metadata.initAttributes.map+(metadata.description ? " - "+metadata.description : "");
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<page>
<include>common/setup.xml</include>
<include>common/styles.xml</include>
<include>common/sprite1.xml</include>
<include>common/common_sprites.xml</include>
<include>common/common_styles.xml</include>
<include>savedgames/save.xml</include>
</page>

View File

@ -1,28 +1,8 @@
function sortDecreasingDate(a, b)
{
return b.metadata.time - a.metadata.time;
}
function twoDigits(n)
{
return n < 10 ? "0" + n : n;
}
function generateLabel(metadata)
{
var t = new Date(metadata.time*1000);
var date = t.getFullYear()+"-"+twoDigits(1+t.getMonth())+"-"+twoDigits(t.getDate());
var time = twoDigits(t.getHours())+":"+twoDigits(t.getMinutes())+":"+twoDigits(t.getSeconds());
return "["+date+" "+time+"] "+metadata.initAttributes.map;
}
function init()
{
var savedGames = Engine.GetSavedGames();
var gameSelection = getGUIObjectByName("gameSelection");
var savedGames = Engine.GetSavedGames();
if (savedGames.length == 0)
{
gameSelection.list = [ "No saved games found" ];
@ -81,9 +61,7 @@ function deleteGame()
function reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
{
error("Could not delete saved game '"+gameID+"'");
}
// Run init again to refresh saved game list
init();

View File

@ -2,13 +2,14 @@
<objects>
<script file="gui/savedgames/load.js"/>
<script file="gui/common/functions_global_object.js" />
<script file="gui/common/functions_utility_loadsave.js" />
<script file="gui/savedgames/load.js" />
<!-- Add a translucent black background to fade out the menu page -->
<object type="image" z="0" sprite="BackgroundTranslucent"/>
<object type="image" style="StoneDialog" size="50%-190 50%-200 50%+190 50%+200">
<object type="image" style="StoneDialog" size="50%-300 50%-200 50%+300 50%+200">
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
Load Game

View File

@ -0,0 +1,112 @@
var g_Descriptions;
var closeCallback;
var gameDataCallback;
function selectDescription()
{
var gameSelection = getGUIObjectByName("gameSelection");
if (gameSelection.selected != -1)
{
getGUIObjectByName("deleteGameButton").enabled = true;
var gameID = gameSelection.list_data[gameSelection.selected];
getGUIObjectByName("saveGameDesc").caption = g_Descriptions[gameID];
}
}
function init(data)
{
if (data)
{
if (data.closeCallback)
closeCallback = data.closeCallback;
if (data.gameDataCallback)
gameDataCallback = data.gameDataCallback;
}
var gameSelection = getGUIObjectByName("gameSelection");
getGUIObjectByName("deleteGameButton").enabled = false;
var savedGames = Engine.GetSavedGames();
if (savedGames.length == 0)
{
gameSelection.list = [ "No saved games found" ];
gameSelection.selected = -1;
return;
}
savedGames.sort(sortDecreasingDate);
var gameListIDs = [ game.id for each (game in savedGames) ];
var gameListLabels = [ generateLabel(game.metadata) for each (game in savedGames) ];
g_Descriptions = {};
[ g_Descriptions[game.id] = (game.metadata.description ? game.metadata.description : "") for each (game in savedGames) ];
gameSelection.list = gameListLabels;
gameSelection.list_data = gameListIDs;
gameSelection.selected = -1;
}
function saveGame()
{
var gameSelection = getGUIObjectByName("gameSelection");
var gameLabel = gameSelection.list[gameSelection.selected];
var gameID = gameSelection.list_data[gameSelection.selected];
var desc = getGUIObjectByName("saveGameDesc").caption;
var name = gameID ? gameID : "savegame";
if (gameSelection.selected != -1)
{
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCode = [function(){ reallySaveGame(name, desc, false); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently overwritten, are you sure?", "OVERWRITE SAVE", 0, btCaptions, btCode);
}
else
reallySaveGame(name, desc, true);
}
function reallySaveGame(name, desc, nameIsPrefix)
{
if (nameIsPrefix)
Engine.SaveGamePrefix(name, desc);
else
Engine.SaveGame(name, desc);
closeSave();
}
function closeSave()
{
Engine.PopGuiPage();
if (closeCallback)
closeCallback();
}
function deleteGame()
{
var gameSelection = getGUIObjectByName("gameSelection");
var gameLabel = gameSelection.list[gameSelection.selected];
var gameID = gameSelection.list_data[gameSelection.selected];
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCode = [function(){ reallyDeleteGame(gameID); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently deleted, are you sure?", "DELETE", 0, btCaptions, btCode);
}
function reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
error("Could not delete saved game '"+gameID+"'");
// Run init again to refresh saved game list
init();
}
// HACK: Engine.SaveGame* expects this function to be defined on the current page
function getSavedGameData()
{
return gameDataCallback();
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script file="gui/common/functions_global_object.js" />
<script file="gui/common/functions_utility_loadsave.js" />
<script file="gui/savedgames/save.js" />
<!-- Add a translucent black background to fade out the page -->
<object type="image" z="0" sprite="BackgroundTranslucent"/>
<object type="image" style="StoneDialog" size="50%-300 50%-200 50%+300 50%+200">
<object type="image" z="0" sprite="BackgroundTranslucent"/>
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
Save Game
</object>
<object name="gameSelection"
style="StoneList"
type="list"
size="24 24 100%-24 100%-124">
<action on="selectionchange">
selectDescription();
</action>
</object>
<object size="24 100%-124 100%-24 100%-100" name="descLabel" type="text" style="LeftLabelText">Description:</object>
<object name="saveGameDesc" size="24 100%-96 100%-24 100%-72" type="input" style="StoneInput"/>
<object name="saveButton" type="button" size="0%+25 100%-60 33%+10 100%-32" style="StoneButton">
Save
<action on="Press">saveGame();</action>
</object>
<object name="deleteGameButton" type="button" size="33%+20 100%-60 66%-15 100%-32" style="StoneButton">
Delete
<action on="Press">deleteGame();</action>
</object>
<object type="button" style="StoneButton" size="66%-5 100%-60 100%-25 100%-32">
Cancel
<action on="Press">closeSave(true);</action>
</object>
</object>
</objects>

View File

@ -167,6 +167,14 @@ function openDeleteDialog(selection)
// Menu functions
// =============================================================================
function openSave()
{
closeMenu();
closeOpenDialogs();
pauseGame();
Engine.PushGuiPage("page_savegame.xml", {"gameDataCallback": getSavedGameData, "closeCallback": resumeGame});
}
function openSettings(pause)
{
getGUIObjectByName("settingsDialogPanel").hidden = false;

View File

@ -702,8 +702,7 @@
>
Save
<action on="Press">
Engine.SaveGame();
closeMenu();
openSave();
</action>
</object>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -187,7 +187,8 @@ Status CGUIManager::ReloadChangedFiles(const VfsPath& path)
CScriptVal CGUIManager::GetSavedGameData()
{
CScriptVal data;
m_ScriptInterface.CallFunction(OBJECT_TO_JSVAL(top()->GetScriptObject()), "getSavedGameData", data);
if (!m_ScriptInterface.CallFunction(OBJECT_TO_JSVAL(top()->GetScriptObject()), "getSavedGameData", data))
LOGERROR(L"Failed to call getSavedGameData() on the current GUI page");
return data;
}

View File

@ -258,11 +258,19 @@ CScriptVal StartSavedGame(void* cbdata, std::wstring name)
return metadata.get();
}
void SaveGame(void* cbdata)
void SaveGame(void* cbdata, std::wstring filename, std::wstring description)
{
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
if (SavedGames::Save(L"quicksave", *g_Game->GetSimulation2(), guiManager, g_Game->GetPlayerID()) < 0)
if (SavedGames::Save(filename, description, *g_Game->GetSimulation2(), guiManager, g_Game->GetPlayerID()) < 0)
LOGERROR(L"Failed to save game");
}
void SaveGamePrefix(void* cbdata, std::wstring prefix, std::wstring description)
{
CGUIManager* guiManager = static_cast<CGUIManager*> (cbdata);
if (SavedGames::SavePrefix(prefix, description, *g_Game->GetSimulation2(), guiManager, g_Game->GetPlayerID()) < 0)
LOGERROR(L"Failed to save game");
}
@ -669,7 +677,8 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &StartSavedGame>("StartSavedGame");
scriptInterface.RegisterFunction<std::vector<CScriptValRooted>, &GetSavedGames>("GetSavedGames");
scriptInterface.RegisterFunction<bool, std::wstring, &DeleteSavedGame>("DeleteSavedGame");
scriptInterface.RegisterFunction<void, &SaveGame>("SaveGame");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, &SaveGame>("SaveGame");
scriptInterface.RegisterFunction<void, std::wstring, std::wstring, &SaveGamePrefix>("SaveGamePrefix");
scriptInterface.RegisterFunction<void, &QuickSave>("QuickSave");
scriptInterface.RegisterFunction<void, &QuickLoad>("QuickLoad");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2012 Wildfire Games.
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -31,7 +31,8 @@ static const int SAVED_GAME_VERSION_MAJOR = 1; // increment on incompatible chan
static const int SAVED_GAME_VERSION_MINOR = 0; // increment on compatible changes to the format
// TODO: we ought to check version numbers when loading files
Status SavedGames::Save(const std::wstring& prefix, CSimulation2& simulation, CGUIManager* gui, int playerID)
Status SavedGames::SavePrefix(const std::wstring& prefix, const std::wstring& description, CSimulation2& simulation, CGUIManager* gui, int playerID)
{
// Determine the filename to save under
const VfsPath basenameFormat(L"saves/" + prefix + L"-%04d");
@ -43,6 +44,15 @@ Status SavedGames::Save(const std::wstring& prefix, CSimulation2& simulation, CG
size_t nextSaveNumber = 0;
vfs::NextNumberedFilename(g_VFS, filenameFormat, nextSaveNumber, filename);
return Save(filename.Filename().string(), description, simulation, gui, playerID);
}
Status SavedGames::Save(const std::wstring& name, const std::wstring& description, CSimulation2& simulation, CGUIManager* gui, int playerID)
{
// Determine the filename to save under
const VfsPath basenameFormat(L"saves/" + name);
const VfsPath filename = basenameFormat.ChangeExtension(L".0adsave");
// ArchiveWriter_Zip can only write to OsPaths, not VfsPaths,
// but we'd like to handle saved games via VFS.
// To avoid potential confusion from writing with non-VFS then
@ -74,6 +84,7 @@ Status SavedGames::Save(const std::wstring& prefix, CSimulation2& simulation, CG
CScriptVal guiMetadata = simulation.GetScriptInterface().CloneValueFromOtherContext(gui->GetScriptInterface(), gui->GetSavedGameData().get());
simulation.GetScriptInterface().SetProperty(metadata.get(), "gui", guiMetadata);
}
simulation.GetScriptInterface().SetProperty(metadata.get(), "description", description);
std::string metadataString = simulation.GetScriptInterface().StringifyJSON(metadata.get(), true);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2012 Wildfire Games.
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -39,15 +39,28 @@ namespace SavedGames
{
/**
* Create new saved game archive with given prefix and simulation data
* Create new saved game archive with given name and simulation data
*
* @param prefix Create new numbered file starting with this prefix
* @param name Name to save the game with
* @param description A user-given description of the save
* @param simulation
* @param gui if not NULL, store some UI-related data with the saved game
* @param playerID ID of the player who saved this file
* @return INFO::OK if successfully saved, else an error Status
*/
Status Save(const std::wstring& prefix, CSimulation2& simulation, CGUIManager* gui, int playerID);
Status Save(const std::wstring& name, const std::wstring& description, CSimulation2& simulation, CGUIManager* gui, int playerID);
/**
* Create new saved game archive with given prefix and simulation data
*
* @param prefix Create new numbered file starting with this prefix
* @param description A user-given description of the save
* @param simulation
* @param gui if not NULL, store some UI-related data with the saved game
* @param playerID ID of the player who saved this file
* @return INFO::OK if successfully saved, else an error Status
*/
Status SavePrefix(const std::wstring& prefix, const std::wstring& description, CSimulation2& simulation, CGUIManager* gui, int playerID);
/**
* Load saved game archive with the given name