File: prog_save.lua

package info (click to toggle)
fillets-ng-data 1.0.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 180,924 kB
  • sloc: makefile: 2
file content (188 lines) | stat: -rw-r--r-- 5,041 bytes parent folder | download | duplicates (5)
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