Greetings.
Recently i've been working on the final title screen based on RPG / Zelda BOTW.
And so comes this idea of unlimited saves number.
(http://i.imgur.com/2gQpdsg.png)
however, i wanna display all files stats here, i already know how to do it, the problem is, the menu is a scroll type menu, with as max_position, the number of saves, however, I can't figure out how to count how many files with "save" prefix exists, if anyone have a clue, i'll gladly take it
You have to test them one by one in a loop, there is no way to get all files from a folder (yet).
You can use a break statement in Lua, so it should be pretty easy to accomplish (as long as they're named sequentially).
Okay I've been able to make it work, however, sol.game.load is slow when dealing with a lot of files, yet, there is no huge ressource consumption, whick makes the slowdown weird, so I'm limitting the files to 4
(http://i.imgur.com/10npTXF.png)
Thanks
Don't try to sol.load() all potential files, just test if they exist. With sol.file.exists().
Quote from: Christopho on March 11, 2017, 01:19:00 AM
Don't try to sol.load() all potential files, just test if they exist. With sol.file.exists().
Yes, I know this, this is how i is done currenly, but if all of them exists, we need to load them all, which cause freeze sometimes. Strangely, loading all files only takes around a megabyte on the ram, so this is weird
I mean, to display infos of a certain file, you need to sol.game.load, and this is why there are some freezes
Well, my advice is to only load the visible files in the menu when required to show them (i.e. a scrolling menu where only three are shown at a time.), store the variable that you need into a list with in a list (arrays work too). This should spread out the load time to when the user attempts to access the other files additional files. Also, you should probably make it so that it sorts by most recently saved. Most users are not going to touch their old saves unless they want to show off their progress or just mess with it.
You could also attempt to use local game_var = require("path/to/save/data.dat")
But don't know if that will work.
Quote from: YoshiMario2000 on March 12, 2017, 10:35:00 PM
Well, my advice is to only load the visible files in the menu when required to show them (i.e. a scrolling menu where only three are shown at a time.), store the variable that you need into a list with in a list (arrays work too). This should spread out the load time to when the user attempts to access the other files additional files. Also, you should probably make it so that it sorts by most recently saved. Most users are not going to touch their old saves unless they want to show off their progress or just mess with it.
You could also attempt to use local game_var = require("path/to/save/data.dat")
But don't know if that will work.
The first idea is a good solution, only loading 2 files (the visible ones) is a rather cool solution, gonna try what I can get with this solution, and sorting by the most recent save is a rather good solution, but, it might be complex to do, I mean the table containing saves need to be arranged with one parameter, the most recent file, don't know if Lua have this or if it might work with all OS.
One solution here
http://stackoverflow.com/questions/33470835/get-file-creation-time-with-lua
One other here
http://stackoverflow.com/questions/33296834/how-can-i-get-last-modified-timestamp-in-lua
At least, the menu is nearly done, so this might be not so long
https://www.youtube.com/watch?v=goCbfhRsnOY
And, one save = 1 quest save, so this helps not dealing with a lot of saves, i should re-implement file copying, there is already something that analyse if 1 file doesn't exist so it will be the first file to be created
The idea of loading files lazily (only when the info is needed to be displayed) is great!
About the second suggestion with require, forget about it. require() is meant to include other scripts, not data files. It would not work since savegames are data files that don't return anything. You would not be able to read any result. And even if you could, it would be wrong because savegame files can have changed in the meantime if the user has played a while, has saved, and then went back to the savegame menu. Remember that require() only loads the script file the first time and keep its result in cache.
Quote from: Christopho on March 13, 2017, 11:51:10 AM
The idea of loading files lazily (only when the info is needed to be displayed) is great!
About the second suggestion with require, forget about it. require() is meant to include other scripts, not data files. It would not work since savegames are data files that don't return anything. You would not be able to read any result. And even if you could, it would be wrong because savegame files can have changed in the meantime if the user has played a while, has saved, and then went back to the savegame menu. Remember that require() only loads the script file the first time and keep its result in cache.
Yes that's a cool idea, it could break the freeze when loading a lot of savegames, yet, I am liking YoshiMario2000's idea of sorting the save menu by the most recently played game, the code already organize saves so, even if save 3 is missing but save 4 exists, it'll be correctly display save 4 in the GUI's slot 3, is there a simpler way than the links i've poster above to organize the array by the last modified file ?
Am I correct in assuming that the only reason you are "loading" the savegame files is to get access to the state of the savegame variables?
If so, perhaps what is needed is some sort of quick load feature that only gives read access to savegame variables but doesn't do any of the other loading of resources needed to actually start the game and therefor would be faster for checking multiple files.
With sol.main.load_file (and not require!) and setfenv it is possible.
Or sol.file.open and parsing the file by hand, because setfenv no longer exists in Lua 5.2 (replaced by ENV_) so you will have a problem if one day we change the Lua version.
Another solution is to save in a common different file the savegames info that you need to show, so you can load it fast. You can override the saving function "game:save()", so that this info is saved in that other file. Maybe a new instance of "game" can be used to save this in the new file, without starting that game.
Good idea! Since you can create any file, you can create one that maintains the list of savegames. Simple as that.
But don't overwrite game:save(), just create your own function (in game_manager or whatever) that calls game:save() and updates the file. It is best practice not to change the official API when it is not necessary.
Yeah i'm completely right with Diarandor's solution, yet, something is unclear because you need to open the concerned save to get the value and write it to another file, maybe using Chris's solution will work, allowing unlimited saves
That also get me to the point where input customisation need to be stored in a file so we can bind custom inputs, even if we are not in a game, but that's something else.
I find a solution, but, I'm unable to extract the needed values in order to insert them in a table, io.lines(file) always return an error. (bad argument #1 to 'lines' (string expected, got userdata))
EDIT: it works, but now how can I extract the value I need from this certain file ?
Doing through setfenv, don't know if that's the right way
function title_screen:do_file(i, name)
local slot = self.slots[i]
local environment = {
prop = function()
slot.max_health = _max_life,
slot.current_health = _current_life
slot.player_name = player_name
slot.tunic = _ability_tunic
slot.shield = _ability_shield
slot.sword = _ability_sword
end,
}
setmetatable(environment, {
__index = function()
return function() end
end
})
local chunk = sol.main.load_file(name)
setfenv(chunk, environment)
chunk()
end
Just use file:lines() instead of io.lines(). io.lines takes a filename, not a file.
setfenv also works (parsing the data file as Lua) but I don't like it so much because it is a function that no longer exists in Lua 5.2. If we switch to a newer Lua version than 5.1 one day, there will be a compatibility issue. Not a big deal, because Lua 5.2 introduces a new (and better) way, but still some manual changes required.
I finally got the file to load and store values in a array, things like hearts display need to be done completely manually.
Thanks for the help
Is it possible to arrange an array in order to display at first the last game played, the modification date of the file might be enough but is it possible through lua ?
100 saves slots should be enough, there are no more lags whe loading all files
Edit: restict to 90 saves, above 90 saves (90 * 90), the surface won't even draw, don't know if it's a bug when a surface above 8100 pixels won't draw
I think 20 or 30 savegames is more than enough. In order to get first the last game played, I think the easiest solution is to save another variable with the index of the last savegame. Each time you save a game you should update the file with your savegame menu info (I think that is what you are already doing, but I am not sure), and at that time you can save this index too.
You can use a custom sort function to arrange things however you want. The following example assumes that the date is some number that increases over time.
local function sort_by_date(a, b)
return a.date > b.date --descending order
--return a.date < b.date --ascending order
end
local example_data = {
{ date = 56, name = "second" },
{ date = 99, name = "first" },
{ date = 21, name = "third" },
}
print"Original Order:"
for i,data in ipairs(example_data) do
print(string.format("%i - date: %f, name: %s", i, data.date or 0, data.name))
end
table.sort(example_data, sort_by_date)
print"\nSorted by Date:"
for i,data in ipairs(example_data) do
print(string.format("%i - date: %f, name: %s", i, data.date or 0, data.name))
end
Quote from: llamazing on March 14, 2017, 11:16:35 PM
You can use a custom sort function to arrange things however you want. The following example assumes that the date is some number that increases over time.
local function sort_by_date(a, b)
return a.date > b.date --descending order
--return a.date < b.date --ascending order
end
local example_data = {
{ date = 56, name = "second" },
{ date = 99, name = "first" },
{ date = 21, name = "third" },
}
print"Original Order:"
for i,data in ipairs(example_data) do
print(string.format("%i - date: %f, name: %s", i, data.date or 0, data.name))
end
table.sort(example_data, sort_by_date)
print"\nSorted by Date:"
for i,data in ipairs(example_data) do
print(string.format("%i - date: %f, name: %s", i, data.date or 0, data.name))
end
This might be the only solution, yet, the only parameter needed here is the file's last modifcation date which will be stored in file.date, then this will be fairly possible with your function
This is how infos are retrieved from a savegame file
function title_screen:do_file(i, name)
local file = sol.file.open(name)
local slot = { dungeon = {} }
-- This should be where the last modification date will be stored
-- slot.date = ???
local font, font_size = sol.language.get_dialog_font()
slot.player_name_text = sol.text_surface.create{
font = font,
font_size = font_size,
}
for line in chunk:lines() do
-- Player stats
if line:match("player_name")then
slot.player_name_text:set_text(line:sub(16, -2))
end
if line:match("_ability_shield") then
slot.shield = line:sub(19)
end
if line:match("_ability_sword") then
slot.sword = line:sub(18)
end
if line:match("_ability_tunic") then
slot.tunic = line:sub(18)
end
-- Game stats
if line:match("_current_life") then
slot.current_life = line:sub(17)
end
if line:match("_max_life") then
slot.max_life = line:sub(13)
end
if line:match("hero_mode") then
slot.hero_mode = line:sub(13)
end
if line:match("finished_game") then
slot.finished_game = line:sub(17)
end
-- Dungeon state
if line:match("dungeon_1_finished") then
slot.dungeon[1] = true
end
-- blablabla
end
file:close()
slot.savegame_number = i
self.slots[i] = slot
end
The file organisation is done somewhere else when another function, more explainatory, is called, here, i just want the last modification date
There is probably a shorter way to parse the savegame files. Here is an example of the string.gmatch (https://www.lua.org/manual/5.1/manual.html#pdf-string.gmatch) official doc:
-- The next example collects all pairs key=value from the given string into a table:
t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
t[k] = v
end
This example can be adapted to include string values (enclosed inside double quotes) and it should just work.