vn/savescreen.lua ---Functions related to the save/load screens -- module("vn.savescreen", package.seeall) local KEY_SAVE_LAST = "vn.save.lastSaved" -- Property value gets set in onSave() local KEY_SAVE_PAGE = "vn.save.lastPage" local SCREENSHOT_WIDTH = 256 local SCREENSHOT_HEIGHT = 144 ---Save/load screen registry -------------------------------------------------------------------------------------------------------------- @section registry -- Forward declarations local saveScreenConstructor = nil local loadScreenConstructor = nil ---Registers a save screen creation function. The save screen return by this function must implement the standard -- save screen methods (show, destroy) function registerSaveScreen(constructor) saveScreenConstructor = constructor end ---Registers a load screen creation function. The load screen return by this function must implement the standard -- load screen methods (show, destroy) function registerLoadScreen(constructor) loadScreenConstructor = constructor end ---Global accessor functions -------------------------------------------------------------------------------------------------------------- @section globals ---Shows the save or load screen. local function saveLoadScreen(isSave) local slot = nil local userData = nil callInContext(function() local function showScreen() local screen = nil if isSave then screen = saveScreenConstructor() else screen = loadScreenConstructor() end slot, userData = screen:show() screen:destroy() end showScreen() end) if slot > 0 then if isSave then -- Take a screenshot to add to the save file local ss = screenshot(getRootLayer(), -32768) ss:markTransient() local screenshotInfo = {screenshot=ss, width=SCREENSHOT_WIDTH, height=SCREENSHOT_HEIGHT} Save.save(slot, userData, screenshotInfo) setSharedGlobal(KEY_SAVE_LAST, slot) else Save.load(slot) end end return slot end ---Shows the save screen. -- @return The save slot that the user saved in, or nil if cancelled. function saveScreen() return saveLoadScreen(true) end ---Shows the load screen. function loadScreen() return saveLoadScreen(false) end ---Save slot component -------------------------------------------------------------------------------------------------------------- @section SaveSlot local SaveSlot = { slot=1, -- Save slot index isSave=false, -- True if we're in save mode, false if we're loading isNew=false, -- Was this save slot written recently isEmpty=true, -- True if the slot is unused compact=false, -- Toggles between full view and compact view backgroundImagePath="gui/savescreen#slotButton-", screenshot=nil, -- Thumbnail image (nil for empty slots) } function SaveSlot.new(self) self = extend(SaveSlot, self) local buttonImagePath = "gui/savescreen#slotButton-" if self.compact then buttonImagePath = "gui/savescreen#quicksave-" end local button = button(buttonImagePath) button:setToggle(true) button:setEnabled(self.isSave or not self.isEmpty) local label = textimg(self.label) label:setZ(button:getZ() - 10) local i = nil local newI = nil if not self.compact then if self.screenshot ~= nil then i = img(self.screenshot) else i = img("gui/savescreen#noImage") end i:setZ(button:getZ() - button:getWidth()/2) if self.isNew and not self.isEmpty then newI = img("gui/savescreen#newSave") newI:setZ(i:getZ() - 1) end end self.button = button self.label = label self.image = i self.newImage = newI return self end function SaveSlot:destroy() destroyValues{self.button, self.image, self.newImage, self.label} end function SaveSlot:getBounds() return self.button:getVisualBounds() end function SaveSlot:setBounds(x, y, w, h) local b = self.button local l = self.label local i = self.image local fontSize = math.ceil(w * .065) l:extendDefaultStyle(Text.createStyle{align="center", fontSize=fontSize}) if i ~= nil then local scale = math.min(w / 256, h / 192) b:setScale(scale) x = x + (w - b:getWidth()) / 2 y = y + (h - b:getHeight()) / 2 w = b:getWidth() h = b:getHeight() b:setPos(x, y) local iw = 224 * scale local ih = 126 * scale local ipad = (w - iw) / 2 i:setBounds(x + ipad, y + ipad, iw, ih) local lh = h - ih - ipad l:setSize(iw, lh) l:setPos(math.ceil(i:getX()), math.ceil(y + h - (lh + l:getTextHeight()) / 2)) else b:setBounds(x, y, w, h) l:setSize(w, h) l:setPos(math.ceil(x), math.ceil(y + (h - l:getTextHeight()) / 2)) end local newI = self.newImage if newI ~= nil then if i ~= nil then --Align with top-right of screenshot newI:setPos(i:getX() + i:getWidth() - newI:getWidth(), i:getY()) else --Align with top-right of button newI:setPos(b:getX() + b:getWidth() - newI:getWidth(), b:getY()) end end end ---Save/Load screen -------------------------------------------------------------------------------------------------------------- @section SaveLoadScreen local SaveLoadScreen = { isSave=false, page=nil, pages=10, selected=0, metaData=nil, --Meta data Lua table added to the save data. rows=2, cols=5, newSaveSlot=SaveSlot.new, x=0, y=0, w=screenWidth, h=screenHeight, pack=5, qcols=5, qh=screenHeight/14, qpack=6, pad=nil, screenshotWidth=nil, screenshotHeight=nil, --GUI Components pageButtons=nil, saves=nil, qsaves=nil, okButton=nil, cancelButton=nil, topFade=nil, bottomFade=nil } function SaveLoadScreen.new(self) self = extend(SaveLoadScreen, self) self.page = self.page or getSharedGlobal(KEY_SAVE_PAGE) or 1 self.pad = self.pad or math.min(self.w, self.h) / 100 self.screenshotWidth = self.screenshotWidth or prefs.saveScreenshotWidth self.screenshotHeight = self.screenshotHeight or prefs.saveScreenshotHeight self.saves = {} self.qsaves = {} local okText = "Load" if self.isSave then okText = "Save" end local cancelText = "Cancel" local okB = button("gui/savescreen#button-") okB:setText(okText) cancelB = button("gui/savescreen#button-") cancelB:setText(cancelText) self.pageButtons = {} for p=1,self.pages do local tb = button("gui/savescreen#pageButton-") tb:setText(p) tb:setToggle(true) self.pageButtons[p] = tb end self.okButton = okB self.cancelButton = cancelB local topFadeTex = tex("gui/savescreen#fade-top", true) if topFadeTex ~= nil then self.topFade = img(topFadeTex, {z=10}) end local bottomFadeTex = tex("gui/savescreen#fade-bottom", true) if bottomFadeTex ~= nil then self.bottomFade = img(bottomFadeTex, {z=10}) end self:setPage(self.page, true) self:initQSaves() return self end function SaveLoadScreen:destroy() destroyValues(self.pageButtons) destroyValues(self.saves) destroyValues(self.qsaves) destroyValues{self.okButton, self.cancelButton} destroyValues{self.topFade, self.bottomFade} end function SaveLoadScreen:layout() local x = self.x local y = self.y local w = self.w local h = self.h local qh = self.qh local ipad = self.pad local vpad = h / 7 local mainW = w - ipad*2 local mainH = h - vpad*2 - qh - ipad*3 -- Layout page buttons local pageButtonPanel = gridPanel() pageButtonPanel:setBounds(x, y, w, vpad) for _,pb in ipairs(self.pageButtons) do pageButtonPanel:add(pb) end pageButtonPanel:pack(5) local function gridLayout(bounds, cols, components) local x = bounds.x local y = bounds.y local rows = math.ceil(#components / cols) local cw = bounds.w / cols local ch = bounds.h / rows local col = 0 for _,component in ipairs(components) do component:setBounds(x + ipad, y + ipad, cw - ipad * 2, ch - ipad * 2) col = col + 1 if col >= cols then col = 0 x = bounds.x y = y + ch else x = x + cw end end end -- Layout save slot buttons gridLayout({x=x+ipad, y=y+vpad+ipad, w=mainW, h=mainH}, self.cols, self.saves) gridLayout({x=x+ipad, y=y+h-vpad-qh-ipad*2, w=mainW, h=qh}, self.qcols, self.qsaves) -- Bottom buttons local bottomButtonPanel = gridPanel() bottomButtonPanel:setBounds(x, y+h-vpad, w, vpad) for _,b in ipairs{self.okButton, self.cancelButton} do bottomButtonPanel:add(b) end bottomButtonPanel:pack(5) if self.topFade ~= nil then self.topFade:setBounds(x, y, w, vpad) end if self.bottomFade ~= nil then self.bottomFade:setBounds(x, y+math.ceil(h-vpad), w, vpad) end end function SaveLoadScreen:initQSaves() destroyValues(self.qsaves) self.qsaves = {} for pass=1,2 do local defaultLabel = "autosave" local startSlot = Save.getAutoSaveSlot(1) local maxSlots = getAutoSaveSlots() if self.isSave then -- Don't allow manual saving into autosave slots maxSlots = 0 end if pass == 2 then startSlot = Save.getQuickSaveSlot(1) maxSlots = 1 defaultLabel = "quicksave" end local saved = Save.getSaves(startSlot, maxSlots) for slot=startSlot,startSlot+maxSlots-1 do local i = slot - startSlot + 1 local label = "Empty\n" .. defaultLabel .. " " .. i local empty = true local si = saved[slot] if si ~= nil then label = defaultLabel .. " " .. i --.. "\n" .. si:getDateString() empty = false end local ss = self.newSaveSlot{slot=slot, label=label, isEmpty=empty, isSave=self.isSave, isNew=false, compact=true} table.insert(self.qsaves, ss) end end end function SaveLoadScreen:getPage() return self.page end function SaveLoadScreen:setPage(p, force) for i,pb in ipairs(self.pageButtons) do pb:setSelected(i == p) end if self.page ~= p or force then self.page = p --Destroy old slots destroyValues(self.saves); self.saves = {} --Create new slots local slotsPerPage = self.rows * self.cols local pageStart = 1 + (p - 1) * slotsPerPage local pageEnd = 1 + (p ) * slotsPerPage local saved = Save.getSaves(pageStart, pageEnd) local lastSaved = getSharedGlobal(KEY_SAVE_LAST) for i=pageStart,pageEnd-1 do local slot = i local screenshot = nil local label = "Empty " .. slot local empty = true local new = false local si = saved[i] if si ~= nil then slot = si:getSlot() screenshot = si:getThumbnail(self.screenshotWidth, self.screenshotHeight) label = "Save " .. slot -- .. "\n" .. si:getDateString() empty = false new = (lastSaved == i) end local ss = self.newSaveSlot{slot=slot, label=label, isEmpty=empty, screenshot=screenshot, isSave=self.isSave, isNew=new} table.insert(self.saves, ss) end if self.selected < pageStart or self.selected >= pageEnd then self.selected = 0 end self:setSelected(self.selected) self:layout() end end function SaveLoadScreen:setSelected(s) self.selected = s for _,save in ipairs(self.saves) do save.button:setSelected(save.slot == s) end for _,save in ipairs(self.qsaves) do save.button:setSelected(save.slot == s) end end function SaveLoadScreen:show() self:layout() while not Input.consume(VKeys.cancel) do for i,pb in ipairs(self.pageButtons) do if pb:consumePress() then self:setPage(i) end end for _,save in ipairs(self.saves) do if save.button:consumePress() then self:setSelected(save.slot) break end end for _,save in ipairs(self.qsaves) do if save.button:consumePress() then self:setSelected(save.slot) break end end self.okButton:setEnabled(self.selected ~= 0) if self.okButton:consumePress() then break elseif self.cancelButton:consumePress() then self.selected = 0 break end yield() end setSharedGlobal(KEY_SAVE_PAGE, self:getPage()) return self.selected, self.metaData end registerSaveScreen(function() return SaveLoadScreen.new{isSave=true} end) registerLoadScreen(function() return SaveLoadScreen.new{isSave=false} end) ← Previous Index Next →