File size: 12,849 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
if FirstLoad then
g_SaveGameObj = false
g_SaveLoadThread = false
g_CurrentSaveGameItemId = false
g_SaveGameDescrThread = false
end
DefineClass.SaveLoadObject = {
__parents = { "PropertyObject" },
items = false,
initialized = false,
}
function SaveLoadObject:ListSavegames()
return Savegame.ListForTag("savegame")
end
function SaveLoadObject:DoSavegame(name)
return SaveGame(name, { save_as_last = true })
end
function SaveLoadObject:DoLoadgame(name)
return LoadGame(name, { save_as_last = true })
end
function SaveLoadObject:WaitGetSaveItems()
local items = {}
local err, list = self:ListSavegames()
if not err then
for _, v in ipairs(list) do
local id = #items + 1
items[id] = {
text = v.displayname,
id = id,
savename = v.savename,
metadata = v,
}
end
end
self.items = items
if not self.initialized then
self.initialized = true
end
end
function SaveLoadObject:RemoveItem(id)
local items = self.items or empty_table
for i = #items, 1, -1 do
local item_id = items[i].id
if item_id == id then
table.remove(items, i)
elseif item_id > id then
items[i].id = item_id - 1
end
end
end
function SaveLoadObject:CalcDefaultSaveName()
local default_text = _InternalTranslate(T(278399852865, "Savegame"))
local items = self.items
local max_num = 0
for k, v in ipairs(items) do
local text = v.text
if string.match(text, "^" .. default_text) then
local number = (text == default_text) and 1 or tonumber(string.match(text, "^" .. default_text .. "%s%((%d+)%)$") or 0)
max_num = Max(max_num, number)
end
end
if max_num > 0 then
return default_text .. " (" .. max_num + 1 .. ")"
end
return default_text:trim_spaces()
end
function SaveLoadObject:ShowNewSavegameNamePopup(host, item)
if not host:IsThreadRunning("rename") then
host:CreateThread("rename", function(item)
local caption = _InternalTranslate(T(808375213123, "Enter name:"))
local savename = config.DefaultOverwriteSavegameAnswer and item and item.text or
WaitInputText(nil, caption, item and item.text or self:CalcDefaultSaveName(), 32,
function (name)
if not name:match("%w") then
return T(528136022504, "The save name must contain at least one letter or digit")
end
end)
if savename then
self:Save(item, savename)
end
end, item)
end
end
function SaveLoadObject:Save(item, name)
name = name:trim_spaces()
if name and name ~= "" then
g_SaveLoadThread = IsValidThread(g_SaveLoadThread) and g_SaveLoadThread or CreateRealTimeThread(function(name, item)
local parent = GetPreGameMainMenu() or GetInGameMainMenu()
local err, savename
if item then
if config.DefaultOverwriteSavegameAnswer or WaitQuestion(parent,
T(824112417429, "Warning"),
T{883071764117, "Are you sure you want to overwrite <savename>?", savename = '"' .. Untranslated(item.text) .. '"'},
T(689884995409, "Yes"),
T(782927325160, "No")) == "ok" then
err = DeleteGame(item.savename)
else
return
end
end
if not err or err == "File Not Found" then
err, savename = self:DoSavegame(name)
end
if not err then
CloseMenuDialogs()
else
CreateErrorMessageBox(err, "savegame", nil, parent, {savename = T{129666099950, '"<name>"', name = Untranslated(name)}, error_code = Untranslated(err)})
end
end, name, item)
end
end
function SaveLoadObject:Load(dlg, item, skipAreYouSure)
if item then
local savename = item.savename
g_SaveLoadThread = IsValidThread(g_SaveLoadThread) and g_SaveLoadThread or CreateRealTimeThread(function(dlg, savename)
local metadata = item.metadata
local err
local parent = GetPreGameMainMenu() or GetInGameMainMenu() or (dlg and dlg.parent) or terminal.desktop
if metadata and not metadata.corrupt and not metadata.incompatible then
local in_game = GameState.gameplay -- this might change during loading
local res = config.DefaultLoadAnywayAnswer or (in_game and not skipAreYouSure) and
WaitQuestion(parent, T(824112417429, "Warning"),
T(927104451536, "Are you sure you want to load this savegame? Any unsaved progress will be lost."),
T(689884995409, "Yes"), T(782927325160, "No"))
or "ok"
if res == "ok" then
err = self:DoLoadgame(savename, metadata)
if not err then
CloseMenuDialogs()
else
ProjectSpecificLoadGameFailed(dlg)
end
end
else
err = metadata and metadata.incompatible and "incompatible" or "corrupt"
end
if err then
-- parent might have been destroyed
parent = GetPreGameMainMenu() or GetInGameMainMenu() or (dlg and dlg.parent) or terminal.desktop
CreateErrorMessageBox(err, "loadgame", nil, parent, {name = '"' .. Untranslated(item.text) .. '"'})
end
end, dlg, savename)
end
end
function SaveLoadObject:Delete(dlg, list)
local list = list or dlg:ResolveId("idList")
if not list or not list.focused_item then return end
local ctrl = list[list.focused_item]
if not ctrl then return end
local item = ctrl and ctrl.context
if item then
local savename = item.savename
CreateRealTimeThread(function(dlg, item, savename)
if WaitQuestion(dlg.parent, T(824112417429, "Warning"), T{912614823850, "Are you sure you want to delete the savegame <savename>?", savename = '"' .. Untranslated(item.text) .. '"'}, T(689884995409, "Yes"), T(782927325160, "No")) == "ok" then
LoadingScreenOpen("idDeleteScreen", "delete savegame")
local err = DeleteGame(savename)
if not err then
if g_CurrentSaveGameItemId == item.id then
g_CurrentSaveGameItemId = false
DeleteThread(g_SaveGameDescrThread)
dlg.idDescription:SetVisible(false)
end
self:RemoveItem(item.id)
list:Clear()
ObjModified(self)
list:DeleteThread("SetInitialSelection")
list:SetSelection(Min(item.id, #list))
LoadingScreenClose("idDeleteScreen", "delete savegame")
else
LoadingScreenClose("idDeleteScreen", "delete savegame")
CreateErrorMessageBox("", "deletegame", nil, dlg.parent, {name = '"' .. item.text .. '"'})
end
end
end, dlg, item, savename)
end
end
function SaveLoadObjectCreateAndLoad()
g_SaveGameObj = SaveLoadObject:new()
return g_SaveGameObj
end
function OnMsg.SavegameDeleted(name)
ObjModified(g_SaveGameObj)
end
-- savegame description text
function SetSavegameDescriptionTexts(dialog, data, missing_dlcs, mods_string, mods_missing)
local playtime = T(77, "Unknown")
if data.playtime then
local h, m, s = FormatElapsedTime(data.playtime, "hms")
local hours = Untranslated(string.format("%02d", h))
local minutes = Untranslated(string.format("%02d", m))
playtime = T{7549, "<hours>:<minutes>", hours = hours, minutes = minutes}
end
if not dialog or dialog.window_state == "destroying" then return end
dialog.idSavegameTitle:SetText(Untranslated(data.displayname))
dialog.idPlaytime:SetText(T{614724487683, "Playtime <playtime>", playtime = playtime})
if dialog.idTimestamp then
dialog.idTimestamp:SetText(T(827551891632, "Saved At: ") .. Untranslated(os.date("%Y-%m-%d %H:%M", data.timestamp)))
end
if rawget(dialog, "idRevision") then
dialog.idRevision:SetText(T{220802271589, "Revision <lua_revision> - <assets_revision>", lua_revision = data.lua_revision, assets_revision = data.assets_revision or ""})
end
if rawget(dialog, "idMap") then
dialog.idMap:SetText(T{316316205743, "Map <map>", map = Untranslated(data.map)})
end
local problem_text = ""
if data and data.corrupt then
problem_text = T(384520518199, "Save file is corrupted!")
elseif data and data.incompatible then
problem_text = T(117116727535, "Please update the game to the latest version to load this savegame.")
elseif missing_dlcs and missing_dlcs ~= "" then
problem_text = T{309852317927, "Missing downloadable content: <dlcs>", dlcs = Untranslated(missing_dlcs)}
elseif mods_missing then
problem_text = T(196062882816, "There are missing mods!")
elseif data.required_lua_revision and LuaRevision < data.required_lua_revision then
problem_text = T(329542364773, "Unknown save file format!")
elseif data.lua_revision < config.SupportedSavegameLuaRevision then
problem_text = T(936146497756, "Deprecated save file format!")
end
dialog.idProblem:SetText(problem_text)
if mods_string and mods_string ~= "" then
dialog.idActiveMods:SetText(T{560410899617, "Active mods <value>",value = Untranslated(mods_string)})
else
dialog.idActiveMods:SetText("")
end
if GetUIStyleGamepad() then
dialog.idDelInfo:SetVisible(false)
else
local del_hint = not data.new_save and T(173045065615, "DEL to delete. ") or T("")
dialog.idDelInfo:SetText(del_hint)
end
end
-- implement in project specific file
function ProjectSpecificLoadGameFailed(dialog)
end
function ShowSavegameDescription(item, dialog)
if not item then return end
if g_CurrentSaveGameItemId ~= item.id then
g_CurrentSaveGameItemId = false
DeleteThread(g_SaveGameDescrThread)
g_SaveGameDescrThread = CreateRealTimeThread(function(item, dialog)
Savegame.CancelLoad()
local metadata = item.metadata
if dialog.window_state == "destroying" then return end
local description = dialog:ResolveId("idDescription")
if description then
description:SetVisible(false)
end
if config.SaveGameScreenshot then
if IsValidThread(g_SaveScreenShotThread) then
WaitMsg("SaveScreenShotEnd")
end
Sleep(210)
end
if dialog.window_state == "destroying" then return end
g_CurrentSaveGameItemId = item.id
-- we need to reload the meta from the disk in order to have the screenshot!
local data = {}
local err
if not metadata then
-- new save
data.displayname = T(4182, "<<< New Savegame >>>")
data.timestamp = os.time()
data.playtime = GetCurrentPlaytime()
data.new_save = true
data.lua_revision = config.SupportedSavegameLuaRevision
data.game_difficulty = GetGameDifficulty()
else
err = GetFullMetadata(metadata, "reload")
if metadata.corrupt then
data.corrupt = true
data.displayname = T(6907, "Damaged savegame")
elseif metadata.incompatible then
data.incompatible = true
data.displayname = T(8648, "Incompatible savegame")
else
data = table.copy(metadata)
data.displayname = Untranslated(data.displayname)
if Platform.developer then
local savename = metadata.savename:match("(.*)%.savegame%.sav$")
savename = savename:gsub("%+", " ")
savename = savename:gsub("%%(%d%d)", function(hex_code)
return string.char(tonumber("0x" .. hex_code))
end)
if savename ~= metadata.displayname then
data.displayname = Untranslated(metadata.displayname .. " - " .. savename)
end
data.displayname = Untranslated(data.displayname)
end
end
end
local mods_list, mods_string, mods_missing
local max_mods, more = 30
if data.active_mods and #data.active_mods > 0 then
mods_list = {}
for _, mod in ipairs(data.active_mods) do
--mod is a table, containing id, title, version and lua_revision or is just the id in older saves
local local_mod = table.find_value(ModsLoaded, "id", mod.id or mod) or Mods[mod.id or mod]
if #mods_list >= max_mods then
more = true
break
end
table.insert(mods_list, mod.title or (local_mod and local_mod.title))
local is_blacklisted = GetModBlacklistedReason(mod.id)
local is_deprecated = is_blacklisted and is_blacklisted == "deprecate"
if not is_deprecated and (not local_mod or not table.find(AccountStorage.LoadMods, mod.id or mod)) then
mods_missing = true
end
end
mods_string = TList(mods_list, ", ")
if more then
mods_string = mods_string .. "<nbsp>..."
end
end
local dlcs_list = {}
for _, dlc in ipairs(data.dlcs or empty_table) do
if not IsDlcAvailable(dlc.id) then
dlcs_list[#dlcs_list + 1] = dlc.name
end
end
SetSavegameDescriptionTexts(dialog, data, TList(dlcs_list), mods_string, mods_missing)
if config.SaveGameScreenshot then
local image = ""
local forced_path = not metadata and g_TempScreenshotFilePath or false
if not forced_path and Savegame._MountPoint then
local images = io.listfiles(Savegame._MountPoint, "screenshot*.jpg", "non recursive")
if #(images or "") > 0 then
image = images[1]
end
elseif forced_path and io.exists(forced_path) then
image = forced_path
end
local image_elem = dialog:ResolveId("idImage")
if image_elem then
if image ~= "" and not err then
image_elem:SetImage(image)
else
image_elem:SetImage("UI/Common/placeholder.tga")
end
end
end
local description = dialog:ResolveId("idDescription")
if description then
description:SetVisible(true)
end
end, item, dialog)
end
end |