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
|
-- -----------------------------------------------------------------
-- These function are called when save or load is made.
-- script_save() ... calls level_save(serialized_level)
-- script_load() ... calls level_load(saved_moves)
-- with global variable saved_moves
-- script_loadState() ... uses global variable saved_models
-- to restore model states
-- -----------------------------------------------------------------
file_include("script/share/Pickle.lua")
function script_save()
local serialized = pickle(getModelsTable())
level_save(serialized)
end
function script_load()
if not saved_moves then
error("global variable 'saved_moves' is not set")
end
level_load(saved_moves)
end
local function assignModelAttributes(saved_table)
local models = getModelsTable()
--NOTE: don't save objects with cross references
--NOTE: objects addresses will be different after load
for model_key, model in pairs(saved_table) do
for param_key, param in pairs(model) do
models[model_key][param_key] = param
end
end
end
function script_loadState()
undo.seen_restarts = nil
if not saved_models then
error("global variable 'saved_models' is not set")
end
local saved_table = unpickle_table(saved_models)
assignModelAttributes(saved_table)
end
--
-- Functions to enable undo
--
local UNDO_MAX_OVERWRITES = 10
if not undo then
undo = {}
undo.stack = {}
undo.seen_restarts = getRestartCount()
undo.num_overwrites = -1
-- The undo.index points where to save the next undo.
undo.index = 0
undo.hold = -1
end
local function applyUndoState(state)
level_load(state.moves)
game_changeBg(state.bg)
local saved_table = unpickle_string(state.serialized)
assignModelAttributes(saved_table)
for index, model in pairs(getModelsTable()) do
model_change_setLocation(model.index, model.X, model.Y)
model_change_setExtraParams(model.index, model.__extra_params)
if model:isLeft() ~= model.lookLeft then
model:change_turnSide()
end
end
end
local function collectUndoState(moves)
-- Saving the actual model position (after room.prepareRound())
for index, model in pairs(getModelsTable()) do
model.X, model.Y = model:getLoc()
model.lookLeft = model:isLeft()
model.__extra_params = model_getExtraParams(model.index)
end
local serialized = pickle(getModelsTable())
local bg = game_getBg()
return {moves=moves, serialized=serialized, bg=bg}
end
local function limitUndoLength()
-- Any level restart or load will clear the undo stack.
-- That limits the memory usage.
if undo.seen_restarts ~= getRestartCount() then
undo.seen_restarts = getRestartCount()
undo.num_overwrites = -1
undo.index = 0
undo.stack = {}
end
end
local function setupOverwrites(keepLast)
-- Sets undo.index to do an overwrite or not.
if keepLast then
undo.num_overwrites = 0
end
if undo.index == 0 then
-- The first known state should be preserved.
undo.num_overwrites = -1
end
if undo.num_overwrites > 0 then
undo.index = undo.index - 1
undo.num_overwrites = undo.num_overwrites - 1
else
if undo.num_overwrites == 0 then
undo.num_overwrites = UNDO_MAX_OVERWRITES
else
undo.num_overwrites = undo.num_overwrites + 1
end
end
end
local function preventRedo()
undo.stack[undo.index] = nil
undo.stack[undo.index + 1] = nil
end
local function isTopState(moves)
local prev = undo.stack[undo.index - 1]
return prev and prev.moves == moves
end
local function checkPossiblyMistyped()
-- Returns true when the keypress is mistyped.
if undo.hold < 0 then
undo.hold = 2
return false
elseif undo.hold == 0 then
return false
else
undo.hold = undo.hold - 1
return true
end
end
function script_saveUndo(moves, keepLast)
limitUndoLength()
if isTopState(moves) then
return
end
setupOverwrites(keepLast)
undo.stack[undo.index] = collectUndoState(moves)
undo.index = undo.index + 1
preventRedo()
end
function script_loadFinalUndo()
-- Reloads the state from the last saved undo.
undo.hold = -1
local saved = undo.stack[undo.index - 1]
if not saved then
return
end
applyUndoState(saved)
end
function script_loadUndo(lastMoves, steps)
-- Undo has steps == 1
-- Redo has steps == -1
if checkPossiblyMistyped() then
return
end
local pos = undo.index - steps
if isTopState(lastMoves) then
pos = pos - 1
end
local saved = undo.stack[pos]
if not saved then
return
end
-- The currently loaded state will be preserved.
undo.index = pos + 1
undo.num_overwrites = 0
undo.seen_restarts = getRestartCount()
applyUndoState(saved)
end
|