Skip to content
Snippets Groups Projects
Forked from tome / Tales of MajEyal
6511 commits behind the upstream repository.
LoadGame.lua 10.66 KiB
-- TE4 - T-Engine 4
-- Copyright (C) 2009 - 2014 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org

require "engine.class"
local Module = require "engine.Module"
local Dialog = require "engine.ui.Dialog"
local TreeList = require "engine.ui.TreeList"
local Button = require "engine.ui.Button"
local Textzone = require "engine.ui.Textzone"
local Separator = require "engine.ui.Separator"
local Checkbox = require "engine.ui.Checkbox"
local Savefile = require "engine.Savefile"
local Downloader = require "engine.dialogs.Downloader"

module(..., package.seeall, class.inherit(Dialog))

function _M:init(force_compat)
	Dialog.init(self, "Load Game", game.w * 0.8, game.h * 0.8)

	self.c_compat = Checkbox.new{default=force_compat, width=math.floor(self.iw / 3 - 40), title="Show older versions", on_change=function() self:switch() end}
	self.c_force_addons = Checkbox.new{default=false, width=math.floor(self.iw / 3 - 40), title="Ignore unloadable addons"}
	self.c_play = Button.new{text="  Play!  ", fct=function(text) self:playSave() end}
	self.c_delete = Button.new{text="Delete", fct=function(text) self:deleteSave() end}
	self.c_desc = Textzone.new{width=math.floor(self.iw / 3 * 2 - 10), height=self.ih - self.c_delete.h - 10, text=""}

	self:generateList()

	self.save_sel = nil
	self.c_tree = TreeList.new{width=math.floor(self.iw / 3 - 10), height=self.ih, scrollbar=true, columns={
		{width=100, display_prop="name"},
	}, tree=self.tree,
		fct=function(item)
			if self.save_sel == item then self:playSave() end
			if self.save_sel then self.save_sel.color = nil self.c_tree:drawItem(self.save_sel) end
			item.color = function() return colors.simple(colors.LIGHT_GREEN) end
			self.save_sel = item
			self.c_tree:drawItem(item)
			if item.usable then self:toggleDisplay(self.c_play, true) end
			self:toggleDisplay(self.c_delete, true)
		end,
		select=function(item, sel) self:select(item) end,
	}

	local sep = Separator.new{dir="horizontal", size=self.ih - 10}
	local uis = {
		{left=0, top=0, ui=self.c_tree},
		{left=self.c_tree.w+sep.w, top=0, ui=self.c_desc},
		{right=0, bottom=0, ui=self.c_delete, hidden=true},
		{left=0, bottom=0, ui=self.c_play, hidden=true},
		{left=self.c_tree.w + 5, top=5, ui=sep},
	}
	if __module_extra_info.show_ignore_addons_not_loading then
		uis[#uis+1] = {left=self.c_tree.w - self.c_compat.w, bottom=self.c_force_addons.h, ui=self.c_compat}
		uis[#uis+1] = {left=self.c_tree.w - self.c_force_addons.w, bottom=0, ui=self.c_force_addons}
	else
		uis[#uis+1] = {left=self.c_tree.w - self.c_compat.w, bottom=0, ui=self.c_compat}
	end
	self:loadUI(uis)
	self:setFocus(self.c_tree)
	self:setupUI(false, true)

	self.key:addBinds{
		EXIT = function() game:unregisterDialog(self) end,
	}
end

function _M:generateList()
	local list = Module:listSavefiles(self.c_compat.checked)
	self.tree = {}
	local found = false
	for i = #list, 1, -1 do
		local m = list[i]
		if m.is_boot then table.remove(list, i) m.savefiles={} end

		local nodes = {}
		local mod_addons = {}

		for j, save in ipairs(m.savefiles) do
			local mod_string = ("%s-%d.%d.%d"):format(m.short_name, save.module_version and save.module_version[1] or -1, save.module_version and save.module_version[2] or -1, save.module_version and save.module_version[3] or -1)
			save.module_string = mod_string
			local mod = list[mod_string]
			if not mod and self.c_compat.checked and m.versions and m.versions[1] then mod = m.versions[1] end
			if mod and save.loadable then
				local laddons = mod_addons[mod]
				if not laddons then
					laddons = table.reversekey(Module:listAddons(mod, true), "short_name")
					mod_addons[mod] = laddons
				end
				local addons = {}
				save.usable = true
				for i, add in ipairs(save.addons or {}) do
					if laddons[add] then addons[#addons+1] = "#LIGHT_GREEN#"..add.."#WHITE#"
					else addons[#addons+1] = "#LIGHT_RED#"..add.."#WHITE#" save.usable = false
					end
				end
				save.mod = mod
				save.base_name = save.short_name
				save.zone = Textzone.new{
					width=self.c_desc.w,
					height=self.c_desc.h,
					text=("#{bold}##GOLD#%s: %s#WHITE##{normal}#\nGame version: %d.%d.%d\nRequires addons: %s\n\n%s"):format(mod.long_name, save.name, save.module_version and save.module_version[1] or -1, save.module_version and save.module_version[2] or -1, save.module_version and save.module_version[3] or -1, save.addons and table.concat(addons, ", ") or "none", save.description)
				}
				if save.screenshot then
					local w, h = save.screenshot:getSize()
					save.screenshot = { save.screenshot:glTexture() }
					save.screenshot.w, save.screenshot.h = w, h
				end
				table.sort(nodes, function(a, b) return (a.timestamp or 0) > (b.timestamp or 0) end)
				table.insert(nodes, save)
				found = true
			end
		end

		if #nodes > 0 then
			local mod = m.versions[1]
			table.insert(self.tree, {
				name=tstring{{"font","bold"}, {"color","GOLD"}, mod.name, {"font","normal"}},
				fct=function() end,
				shown=true,
				nodes=nodes,
				zone = Textzone.new{width=self.c_desc.w, height=self.c_desc.h, text="#{bold}##GOLD#"..mod.long_name.."#WHITE##{normal}#\n\n"..mod.description}
			})
		end
	end
end

function _M:switch()
	self:generateList()
	self.c_tree.tree = self.tree
	self.c_tree:generate()
end

function _M:on_focus(id, ui)
	if self.focus_ui and self.focus_ui.ui == self.c_tree then
		self:select(self.cur_sel)
	else
		self:select(self.save_sel)
	end
end

function _M:select(item)
	if item and self.uis[2] then
		self.uis[2].ui = item.zone
		self.cur_sel = item
	else
		self.cur_sel = nil
	end
end

function _M:innerDisplay(x, y, nb_keyframes)
	if not self.cur_sel or not self.cur_sel.screenshot then return end
	local s = self.cur_sel.screenshot
	local r = s.w / s.h
	local w = math.min(s.w, self.c_desc.w - 20)
	local h = w / r
	h = math.min(h, self.ih / 1.7)
	w = h * r
	s[1]:toScreenFull(x + self.ix + self.iw - self.c_desc.w + 10, y + self.ih - h - 20, w, h, s[2] * w / s.w, s[3] * h / s.h)
end

function _M:playSave(ignore_mod_compat)
	if not self.save_sel then return end

	if self.save_sel.module == "tome" and engine.version_compare(self.save_sel.module_version, {1, 2, 0}) == "lower" then
		Dialog:yesnoLongPopup("Incompatible savefile", [[Due to huge changes in 1.2.0 all previous savefiles will not work with it.
This savefile requires a game version lower than 1.2.0 and thus can not be loaded.

But despair not, if you wish to finish it you can simply download the old version corresponding to the savefile on #{italic}##LIGHT_BLUE#http://te4.org/download#WHITE##{normal}#.

We apologize for the annoyance, most of the time we try to keep compatibility but this once it was simply not possible.]],
			700, function(ret) if ret then
				util.browserOpenUrl("http://te4.org/download", {webview=true, steam=true})
			end end, "Go to download in your browser", "Cancel"
		)
		return
	end

	if not ignore_mod_compat and self.save_sel.module_string ~= self.save_sel.mod.version_string then
		Dialog:yesnocancelLongPopup("Original game version not found", "This savefile was created with game version %s. You can try loading it with the current version if you wish or download the data files of the old version to ensure compatibility (this is a big download but only required once).", 500, function(ret, cancel)
			if cancel then return end
			if ret then
				self:installOldGame(self.save_sel.module_string)
			else
				self:playSave(true)
			end
		end, "Install old data", "Run with newer version", "Cancel", true)
		return
	end

	if config.settings.cheat and not self.save_sel.cheat then
		Dialog:yesnoPopup("Developer Mode", "#LIGHT_RED#WARNING: #LAST#Loading a savefile while in developer mode will permanently invalidate it. Proceed?", function(ret) if not ret then
			Module:instanciate(self.save_sel.mod, self.save_sel.base_name, false)
		end end, "Cancel", "Load anyway", true)
	else
		local extra_info = nil
		if self.c_force_addons.checked then extra_info = "ignore_addons_not_loading=true" end
		Module:instanciate(self.save_sel.mod, self.save_sel.base_name, false, nil, extra_info)
	end
end

function _M:deleteSave()
	if not self.save_sel then return end

	Dialog:yesnoPopup("Delete savefile", "Really delete #{bold}##GOLD#"..self.cur_sel.name.."#WHITE##{normal}#", function(ret)
		if ret then
			local base = Module:setupWrite(self.save_sel.mod)
			local save = Savefile.new(self.save_sel.base_name)
			save:delete()
			save:close()
			fs.umount(base)

			game.save_list = Module:listSavefiles()

			local d = new()
			d.__showup = false
			game:replaceDialog(self, d)
		end
	end, "Delete", "Cancel")
end

function _M:installOldGame(version_string)
	if not core.webview then return end
	print("[OLD MODULE] checking for install", version_string)

	local dls = {}
	if version_string == "tome-1.2.5" then
		dls[#dls+1] = {url="http://te4.org/dl/modules/tome/tome-1.2.5-gfx.team", name="tome-1.2.5-gfx.team"}
		dls[#dls+1] = {url="http://te4.org/dl/modules/tome/tome-1.2.5-music.team", name="tome-1.2.5-music.team"}
		dls[#dls+1] = {url="http://te4.org/dl/modules/tome/tome-1.2.5.team", name="tome-1.2.5.team"}
	end

	if #dls == 0 then
		Dialog:simplePopup("Old game data", "No data available for this game version.")
		return
	end

	local co co = coroutine.create(function()
	local all_ok = true
	for i, dl in ipairs(dls) do
		local modfile = "/modules/"..dl.name
		print("[OLD MODULE] download file from", dl.url, "to", modfile)
		local d = Downloader.new{title="Downloading old game data: #LIGHT_GREEN#"..dl.name, co=co, dest=modfile..".tmp", url=dl.url, allow_downloads={modules=true}}
		local ok = d:start()
		if ok then
			local wdir = fs.getWritePath()
			local _, _, dir, name = modfile:find("(.+)/([^/]+)$")
			if dir then
				fs.setWritePath(fs.getRealPath(dir))
				fs.delete(name)
				fs.rename(name..".tmp", name)
				fs.setWritePath(wdir)
			end
		else all_ok = false
		end
	end
	if all_ok then Dialog:simplePopup("Old game data", "Old game data for "..version_string.." correctly installed. You can now play.") self:regen()
	else Dialog:simplePopup("Old game data", "Failed to install.") end
	end)
	print(coroutine.resume(co))
end

function _M:regen()
	local d = new(self.c_compat.checked)
	d.__showup = false
	game:replaceDialog(self, d)
	self.next_dialog = d
end