-
Notifications
You must be signed in to change notification settings - Fork 1
/
sonic-hud.lua
324 lines (299 loc) · 10 KB
/
sonic-hud.lua
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
--------------------------------------------------------------------------------
-- This file is part of the Lua HUD for TASing Sega Genesis Sonic games.
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser 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 Lesser General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------
if base_path == nil then
base_path = (string.gsub(debug.getinfo(1).source, "sonic%-hud", "?", 1)):sub(2)
package.path = base_path .. ";" .. package.path
end
--------------------------------------------------------------------------------
-- Script configuration options.
--------------------------------------------------------------------------------
gens.persistglobalvariables({
-- Enabled HUDs
game_hud = true,
active_char_huds = {[1]=true, [2]=true, [3] = true},
stat_hud = true,
boss_hud_active = true,
menu_active = true, -- ignores disable_lua_hud
-- Special features
enable_predictor = true,
-- Configuration options
disable_original_huds = false,
disable_lua_hud = false,
skip_boring_stuff = false,
skip_score_tally = true, -- requires skip_boring_stuff to be true to do anything
disable_hyperflash = true,
disable_s3k_super_music = false,
})
--------------------------------------------------------------------------------
-- Include all of the script subfiles.
--------------------------------------------------------------------------------
require("sonic/common/rom-check")
require("headers/register")
require("headers/widgets")
require("sonic/common/config-menu")
require("sonic/common/game-info")
require("sonic/common/char-info")
require("sonic/common/portraits")
require("sonic/common/jump-predictor")
require("sonic/common/status-widget")
require("sonic/common/character-hud")
require("sonic/common/boss-hud")
require("sonic/fast-forward")
require("sonic/kill-hyperflash")
require("sonic/kill-super-music")
require("sonic/kill-original")
--------------------------------------------------------------------------------
-- HUD components: status icons, character HUDs, boss HUDs.
--------------------------------------------------------------------------------
local status_huds = Status_widget:new(72, 0, stat_hud)
local char_huds = nil
local levelbounds = nil
local boss_hud = Boss_widget:new(0, 0, boss_hud_active)
--------------------------------------------------------------------------------
-- Main game HUD
--------------------------------------------------------------------------------
local function create_main_hud(ly, w, h)
local main_hud = Frame_widget:new(0, 0, w, h)
--main_hud:add_status_icon(1, 2, "score" , bind(game.get_score , game))
main_hud:add_status_icon(1, 2, "camera" , bind(game.get_camera , game))
main_hud:add_status_icon(1, ly + 2, "clock" , bind(game.get_time , game))
main_hud:add_status_icon(1, 2 * ly + 2, "ring" , bind(game.get_rings , game))
main_hud:add_status_icon(1, 3 * ly + 2, "emeralds-chaos", bind(game.get_chaos_emeralds, game))
if game:has_super_emeralds() then
main_hud:add_status_icon(36, 3 * ly + 2, "emeralds-super", bind(game.get_super_emeralds, game))
elseif rom:is_sonic_cd() then
main_hud:add(Icon_widget:new(0, 0, bind(game.get_timewarp_icon, game)), 36, 3 * ly + 2)
end
return main_hud
end
local main_hud = Container_widget:new(0, 0, game_hud)
main_hud:add(create_main_hud(14, 65, 58), 3, 0)
main_hud:add_toggle(make_toggle(58, false, Container_widget.toggled, main_hud, game_hud), 0, 0)
--------------------------------------------------------------------------------
-- Main workhorse function
--------------------------------------------------------------------------------
local flash_nomovie = false
local char_hud_xy = {
[1] = {
[1] = {3, 172}
},
[2] = {
[1] = {3, 172},
[2] = {214, 172},
},
[3] = {
[1] = {3, 172},
[2] = {214, 172},
[3] = {214, 120},
},
}
function table_eq(table1, table2)
local avoid_loops = {}
local function recurse(t1, t2)
-- compare value types
if type(t1) ~= type(t2) then
return false
end
-- Base case: compare simple values
if type(t1) ~= "table" then
return t1 == t2
end
-- Base case: tables of different sizes
if #table1 ~= #table2 then
return false
end
-- Now, on to tables.
-- First, let's avoid looping forever.
if avoid_loops[t1] then
return avoid_loops[t1] == t2
end
avoid_loops[t1] = t2
-- Copy keys from t2
local t2keys = {}
local t2tablekeys = {}
for k, _ in pairs(t2) do
if type(k) == "table" then
table.insert(t2tablekeys, k)
end
t2keys[k] = true
end
-- Let's iterate keys from t1
for k1, v1 in pairs(t1) do
local v2 = t2[k1]
if type(k1) == "table" then
-- if key is a table, we need to find an equivalent one.
local ok = false
for i, tk in ipairs(t2tablekeys) do
if table_eq(k1, tk) and recurse(v1, t2[tk]) then
table.remove(t2tablekeys, i)
t2keys[tk] = nil
ok = true
break
end
end
if not ok then
return false
end
else
-- t1 has a key which t2 doesn't have, fail.
if v2 == nil then
return false
end
t2keys[k1] = nil
if not recurse(v1, v2) then
return false
end
end
end
-- if t2 has a key which t1 doesn't have, fail.
if next(t2keys) then
return false
end
return true
end
return recurse(table1, table2)
end
local last_mode = nil
-- Reads mem values, emulates a couple of frames, displays everything
draw_hud = function ()
-- Selected character(s)
local selchar = game:get_char()
local new_mode = game:get_mode()
if (characters == nil) or (new_mode ~= last_mode) or not table_eq(game.curr_char, selchar) then
set_chardata(selchar)
char_huds = {}
levelbounds = {}
local pos_tables = char_hud_xy[#characters]
for i, char in ipairs(characters) do
local xy = pos_tables[i]
table.insert(char_huds, Character_hud:new(char, xy[1], xy[2], active_char_huds[i]))
table.insert(levelbounds, Level_bounds:new(char, (char.is_p1 and {255, 255, 0, 255}) or {255, 0, 255, 255}))
end
end
last_mode = new_mode
-- look 2 frames into the future, pretending the B button is held,
-- and get what the X and Y velocity of the player will be
if want_prediction() then
predict_jumps()
end
-- Display big red translucent box all over screen if not playing or recording a movie.
if flash_nomovie and not movie.recording() and not movie.playing() then
gui.box (0, 0, 319, 223, {255, 0, 0, 128}, {255, 0, 0, 255})
end
-- Camera bounds:
for i, hud in pairs(levelbounds) do
if hud.character:in_game() and active_char_huds[i] then
hud:draw()
end
end
-- Basic game HUD (rings, time, score, emeralds)
game_hud = main_hud:draw()
-- The character huds:
for i, hud in pairs(char_huds) do
if hud.character:in_game() then
active_char_huds[i] = hud:draw()
end
end
boss_hud_active = boss_hud:draw()
-- General timers: invincibility, speed shoes, super status, etc.
stat_hud = status_huds:draw()
end
local function do_huds()
if not game:disable_hud() then
draw_hud()
else
game.curr_char = nil
end
end
local function do_huds_load()
for _, item in pairs(status_huds.children) do
item.active = false
end
status_huds.children = {}
do_huds()
end
local function toggle_lua_hud(enable)
if enable then
callbacks.gens.registerafter:add(do_huds)
callbacks.savestate.registerload:add(do_huds_load)
else
callbacks.gens.registerafter:remove(do_huds)
callbacks.savestate.registerload:remove(do_huds_load)
end
end
--------------------------------------------------------------------------------
-- Starting options.
--------------------------------------------------------------------------------
local function apply_options()
if char_huds then
for n = 1,#char_huds do
char_huds[n]:set_state(active_char_huds[n])
end
end
main_hud:set_state(game_hud)
boss_hud:set_state(boss_hud_active)
if boss_hud_active then
boss_hud:register()
else
boss_hud:unregister()
end
status_huds:set_state(stat_hud)
toggle_lua_hud(not disable_lua_hud)
toggle_disable_original_huds(disable_original_huds)
toggle_fast_forward(skip_boring_stuff)
toggle_hyperflash(disable_hyperflash)
toggle_disable_s3k_super_music(disable_s3k_super_music)
end
local function reset_config()
game_hud = true
active_char_huds = {[1]=true, [2]=true, [3] = true}
stat_hud = true
boss_hud_active = true
menu_active = true
enable_predictor = true
disable_original_huds = false
disable_lua_hud = false
skip_boring_stuff = false
skip_score_tally = true
disable_hyperflash = true
disable_s3k_super_music = false
end
--------------------------------------------------------------------------------
-- Configuration menu.
--------------------------------------------------------------------------------
local menubtn = Container_widget:new(273, 0, menu_active)
local menu = Config_menu:new(40, 30, 240, 160, function ()
apply_options()
if not disable_lua_hud and not game:disable_hud() then
do_huds()
end
menu_active = menubtn:draw()
end, menu_active)
menubtn:add_toggle(make_toggle(8, false, Container_widget.toggled, menubtn, not disable_lua_hud), 43, 0)
menubtn:add(make_button(Config_menu.menu_loop, menu, "Options", 42, 8, nil, {0, 0, 0, 192}), 0, 0)
callbacks.gui.register:add(function()
menu_active = menubtn:draw()
end)
--------------------------------------------------------------------------------
-- Apply the options and do initial draw.
--------------------------------------------------------------------------------
apply_options()
update_input()
if not disable_lua_hud and not game:disable_hud() then
do_huds()
end
menubtn:draw()