mirror of
https://github.com/notwa/mm
synced 2024-11-04 20:09:03 -08:00
complete rewrite of cheat menu
This commit is contained in:
parent
4c363dae6f
commit
9dabf504d4
5 changed files with 308 additions and 162 deletions
|
@ -1,5 +1,6 @@
|
|||
require "boilerplate"
|
||||
require "addrs.init"
|
||||
require "classes"
|
||||
|
||||
-- check for errors in the actor linked lists
|
||||
local validate = false
|
||||
|
@ -177,26 +178,6 @@ function collect_actors()
|
|||
return any > 0, actors_by_type, new_counts
|
||||
end
|
||||
|
||||
InputHandler = Class()
|
||||
function InputHandler:init(binds)
|
||||
self.binds = binds
|
||||
self.old_ctrl = {}
|
||||
end
|
||||
|
||||
function InputHandler:update()
|
||||
local ctrl = {}
|
||||
local pressed = {}
|
||||
local j = joypad.getimmediate()
|
||||
for k, v in pairs(self.binds) do
|
||||
ctrl[k] = j[v]
|
||||
end
|
||||
for k, v in pairs(ctrl) do
|
||||
pressed[k] = ctrl[k] and not self.old_ctrl[k]
|
||||
end
|
||||
self.old_ctrl = ctrl
|
||||
return ctrl, pressed
|
||||
end
|
||||
|
||||
ActorLister = Class()
|
||||
function ActorLister:init()
|
||||
self.before = 0
|
||||
|
|
|
@ -235,8 +235,7 @@ return {
|
|||
kegs = AL(0xAC, 1),
|
||||
},
|
||||
|
||||
buttons_1 = AG(0x1A, 1),
|
||||
buttons_2 = AG(0x1B, 1),
|
||||
buttons = AG(0x1A, 2),
|
||||
framerate_limiter = AG(0xA2, 1),
|
||||
scene_number = AG(0xA4, 2),
|
||||
screen_T = AG(0xC0, 4),
|
||||
|
|
|
@ -1,158 +1,101 @@
|
|||
require "boilerplate"
|
||||
local addrs = require "addrs.init"
|
||||
require "addrs.init"
|
||||
require "classes"
|
||||
require "menu classes"
|
||||
|
||||
local close = {text="close", type="close"}
|
||||
function T(x, y, color, pos, s, ...)
|
||||
if #{...} > 0 then
|
||||
s = s:format(...)
|
||||
end
|
||||
gui.text(10*x + 2, 16*y + 4, s, nil, color or "white", pos or "bottomright")
|
||||
end
|
||||
|
||||
local menu = {
|
||||
{
|
||||
close,
|
||||
{text="hey"},
|
||||
{text="L to levitate", type="toggle", id="levitate"},
|
||||
{text="levitate", type="hold", call_id="levitate"},
|
||||
{text="everything", type="oneshot", call_id="everything"},
|
||||
active = 1,
|
||||
function T_BR(x, y, color, ...) T(x, y, color, "bottomright", ...) end
|
||||
function T_BL(x, y, color, ...) T(x, y, color, "bottomleft", ...) end
|
||||
function T_TL(x, y, color, ...) T(x, y, color, "topleft", ...) end
|
||||
function T_TR(x, y, color, ...) T(x, y, color, "topright", ...) end
|
||||
|
||||
local passives = {}
|
||||
|
||||
Passive = Class(Callbacks)
|
||||
function Passive:init(...)
|
||||
Callbacks.init(self, ...)
|
||||
table.insert(passives, self)
|
||||
end
|
||||
function Passive:tick()
|
||||
end
|
||||
|
||||
local levitate = Passive()
|
||||
function levitate:tick()
|
||||
if self.state then
|
||||
if bit.band(addrs.buttons(), 0x20) > 0 then
|
||||
self:hold()
|
||||
end
|
||||
end
|
||||
end
|
||||
function levitate:hold()
|
||||
addrs.link_actor.y_vel(10)
|
||||
end
|
||||
|
||||
local everything = Callbacks()
|
||||
function everything:on()
|
||||
dofile("oneshot.lua")
|
||||
end
|
||||
|
||||
local self_destruct = Callbacks()
|
||||
function self_destruct:on()
|
||||
addrs.hearts(0)
|
||||
end
|
||||
|
||||
local main_menu = Menu{
|
||||
Screen{
|
||||
Text("hey"),
|
||||
Toggle("L to Levitate", levitate),
|
||||
Hold("Levitate", levitate),
|
||||
Oneshot("100%", everything),
|
||||
Back(),
|
||||
},
|
||||
{
|
||||
close,
|
||||
{text="kill link", type="oneshot", call_id="kill"},
|
||||
{text="some flags", type="control"}, -- how to handle this?
|
||||
-- i guess with a function like update_text
|
||||
-- and self_render and handle_input basically
|
||||
-- or call_id="submenu" options={yada yada}
|
||||
{text="k"},
|
||||
active = 1,
|
||||
Screen{
|
||||
Oneshot("Kill Link", self_destruct),
|
||||
Flags("some flags"),
|
||||
Text("k"),
|
||||
Back(),
|
||||
},
|
||||
active = 1,
|
||||
}
|
||||
|
||||
local active_menu = nil
|
||||
|
||||
local menu_state = {
|
||||
levitate = false,
|
||||
local input = InputHandler{
|
||||
enter = "P1 L",
|
||||
up = "P1 DPad U",
|
||||
down = "P1 DPad D",
|
||||
left = "P1 DPad L",
|
||||
right = "P1 DPad R",
|
||||
}
|
||||
|
||||
local menu_calls = {
|
||||
levitate = function(item, state)
|
||||
addrs.z_vel(16)
|
||||
end,
|
||||
kill = function(item, state)
|
||||
addrs.hearts(0)
|
||||
end,
|
||||
set = function(item, state)
|
||||
A(item.addr, item.type)(item.value)
|
||||
end,
|
||||
everything = function(item, state)
|
||||
dofile("oneshot.lua")
|
||||
end,
|
||||
}
|
||||
local menu = nil
|
||||
|
||||
function T(x, y, s, color)
|
||||
color = color or "white"
|
||||
gui.text(10*x + 2, 16*y + 4, s, nil, color, "bottomright")
|
||||
end
|
||||
while mm or oot do
|
||||
local ctrl, pressed = input:update()
|
||||
|
||||
function draw_row(row, row_number, is_active)
|
||||
local color = is_active and "cyan" or "white"
|
||||
if row.type == "toggle" then
|
||||
T(4, row_number, row.text, color)
|
||||
T(0, row_number, "[ ]", "yellow")
|
||||
if menu_state[row.id] then
|
||||
T(1, row_number, "x", "cyan")
|
||||
end
|
||||
else
|
||||
T(0, row_number, row.text, color)
|
||||
local delay = false
|
||||
if not menu and pressed.enter then
|
||||
delay = true
|
||||
menu = main_menu
|
||||
menu:focus()
|
||||
end
|
||||
end
|
||||
|
||||
function run_row(row, hold)
|
||||
local rt = row.type
|
||||
if rt == "hold" then
|
||||
if row.call_id then
|
||||
menu_calls[row.call_id](row, menu_state)
|
||||
end
|
||||
if row.id then
|
||||
menu_state[row.id] = true -- TODO: set to false later
|
||||
if menu and not delay then
|
||||
local old = menu
|
||||
menu = menu:navigate(ctrl, pressed)
|
||||
if menu ~= old then
|
||||
old:unfocus()
|
||||
if menu then menu:focus() end
|
||||
end
|
||||
end
|
||||
if menu then menu:draw(T_TL, 0) end
|
||||
|
||||
if hold then return end
|
||||
|
||||
if rt == "toggle" then
|
||||
menu_state[row.id] = not menu_state[row.id]
|
||||
if row.call_id then
|
||||
menu_calls[row.call_id](row, menu_state)
|
||||
end
|
||||
elseif rt == "close" then
|
||||
active_menu = nil
|
||||
elseif rt == "oneshot" then
|
||||
menu_calls[row.call_id](row, menu_state)
|
||||
for i, passive in ipairs(passives) do
|
||||
passive:tick()
|
||||
end
|
||||
end
|
||||
|
||||
function run_mainmenu(ctrl, pressed)
|
||||
local active_submenu = menu.active
|
||||
if pressed.left then
|
||||
active_submenu = active_submenu - 1
|
||||
end
|
||||
if pressed.right then
|
||||
active_submenu = active_submenu + 1
|
||||
end
|
||||
active_submenu = (active_submenu - 1) % #menu + 1
|
||||
menu.active = active_submenu
|
||||
|
||||
local submenu = menu[active_submenu]
|
||||
|
||||
local active_row = submenu.active
|
||||
if pressed.down then
|
||||
active_row = active_row - 1
|
||||
end
|
||||
if pressed.up then
|
||||
active_row = active_row + 1
|
||||
end
|
||||
active_row = (active_row - 1) % #submenu + 1
|
||||
submenu.active = active_row
|
||||
|
||||
local row = submenu[active_row]
|
||||
|
||||
if pressed.enter then
|
||||
run_row(row)
|
||||
elseif ctrl.enter then
|
||||
run_row(row, true)
|
||||
end
|
||||
|
||||
T(0, 0, ("menu %02i/%02i"):format(active_submenu, #menu), "yellow")
|
||||
for i, row in ipairs(submenu) do
|
||||
draw_row(row, i, i == active_row)
|
||||
end
|
||||
end
|
||||
|
||||
local pressed = {}
|
||||
local old_ctrl = {}
|
||||
while true do
|
||||
local j = joypad.getimmediate()
|
||||
local ctrl = {
|
||||
enter = j["P1 L"],
|
||||
up = j["P1 DPad U"],
|
||||
down = j["P1 DPad D"],
|
||||
left = j["P1 DPad L"],
|
||||
right = j["P1 DPad R"],
|
||||
}
|
||||
|
||||
for k, v in pairs(ctrl) do
|
||||
pressed[k] = ctrl[k] and not old_ctrl[k]
|
||||
end
|
||||
|
||||
gui.clearGraphics()
|
||||
|
||||
if pressed.enter and not active_menu then
|
||||
active_menu = menu
|
||||
pressed.enter = false
|
||||
end
|
||||
|
||||
if active_menu == menu then
|
||||
run_mainmenu(ctrl, pressed)
|
||||
end
|
||||
|
||||
old_ctrl = ctrl
|
||||
emu.yield()
|
||||
|
||||
emu.frameadvance()
|
||||
end
|
||||
|
|
|
@ -41,3 +41,23 @@ function Monitor:save(fn)
|
|||
self.dirty = false
|
||||
end
|
||||
end
|
||||
|
||||
InputHandler = Class()
|
||||
function InputHandler:init(binds)
|
||||
self.binds = binds
|
||||
self.old_ctrl = {}
|
||||
end
|
||||
|
||||
function InputHandler:update()
|
||||
local ctrl = {}
|
||||
local pressed = {}
|
||||
local j = joypad.getimmediate()
|
||||
for k, v in pairs(self.binds) do
|
||||
ctrl[k] = j[v]
|
||||
end
|
||||
for k, v in pairs(ctrl) do
|
||||
pressed[k] = ctrl[k] and not self.old_ctrl[k]
|
||||
end
|
||||
self.old_ctrl = ctrl
|
||||
return ctrl, pressed
|
||||
end
|
||||
|
|
203
Lua/menu classes.lua
Normal file
203
Lua/menu classes.lua
Normal file
|
@ -0,0 +1,203 @@
|
|||
function wrap(x, around)
|
||||
return (x - 1) % around + 1
|
||||
end
|
||||
|
||||
MenuItem = Class()
|
||||
Text = Class(MenuItem)
|
||||
Back = Class(Text)
|
||||
Close = Back -- FIXME
|
||||
|
||||
Active = Class(Text)
|
||||
Toggle = Class(Active)
|
||||
Hold = Class(Active)
|
||||
Oneshot = Class(Active)
|
||||
Flags = Class(Active)
|
||||
|
||||
Screen = Class()
|
||||
Menu = Class()
|
||||
|
||||
Callbacks = Class()
|
||||
|
||||
function Callbacks:init()
|
||||
self.state = false
|
||||
end
|
||||
function Callbacks:on()
|
||||
self.state = true
|
||||
end
|
||||
function Callbacks:off()
|
||||
self.state = false
|
||||
end
|
||||
function Callbacks:hold()
|
||||
end
|
||||
function Callbacks:release()
|
||||
end
|
||||
|
||||
function MenuItem:init()
|
||||
self.focused = false
|
||||
end
|
||||
function MenuItem:run()
|
||||
return self
|
||||
end
|
||||
function MenuItem:hold()
|
||||
self:release()
|
||||
end
|
||||
function MenuItem:focus()
|
||||
self.focused = true
|
||||
end
|
||||
function MenuItem:unfocus()
|
||||
self.focused = false
|
||||
end
|
||||
function MenuItem:release()
|
||||
end
|
||||
function MenuItem:draw(brush, y)
|
||||
end
|
||||
|
||||
function Text:init(text)
|
||||
MenuItem.init(self)
|
||||
self.text = text
|
||||
end
|
||||
function Text:draw(brush, y)
|
||||
local color = self.focused and 'yellow' or 'white'
|
||||
brush(0, y, color, self.text)
|
||||
end
|
||||
|
||||
function Back:init()
|
||||
Text.init(self, 'back')
|
||||
end
|
||||
function Back:run()
|
||||
return nil -- FIXME
|
||||
end
|
||||
|
||||
function Active:init(text, callbacks)
|
||||
self.text = text
|
||||
self.callbacks = callbacks or {}
|
||||
end
|
||||
|
||||
function Toggle:init(text, callbacks)
|
||||
Active.init(self, text, callbacks)
|
||||
self.state = false
|
||||
end
|
||||
function Toggle:run()
|
||||
self.state = not self.state
|
||||
if self.state then
|
||||
self.callbacks:on(true)
|
||||
else
|
||||
self.callbacks:off(false)
|
||||
end
|
||||
return self
|
||||
end
|
||||
function Toggle:draw(brush, y)
|
||||
local color = self.focused and 'yellow' or 'white'
|
||||
brush(0, y, 'cyan', '[ ]')
|
||||
if self.state then
|
||||
brush(1, y, color, 'x')
|
||||
end
|
||||
brush(4, y, color, self.text)
|
||||
end
|
||||
|
||||
function Oneshot:run()
|
||||
self.callbacks:on()
|
||||
return self
|
||||
end
|
||||
|
||||
function Hold:run()
|
||||
self:hold()
|
||||
return self
|
||||
end
|
||||
function Hold:hold()
|
||||
self.callbacks:hold()
|
||||
end
|
||||
function Hold:release()
|
||||
self.callbacks:release()
|
||||
end
|
||||
|
||||
--function Flags:init
|
||||
|
||||
function Screen:init(items)
|
||||
self.items = items
|
||||
self.item_sel = 1
|
||||
end
|
||||
|
||||
function Screen:focus()
|
||||
self.items[self.item_sel]:focus()
|
||||
end
|
||||
|
||||
function Screen:unfocus()
|
||||
self.items[self.item_sel]:unfocus()
|
||||
end
|
||||
|
||||
function Screen:navigate(ctrl, pressed)
|
||||
local i = self.item_sel
|
||||
local old = self.items[i]
|
||||
|
||||
if pressed.down then i = i + 1 end
|
||||
if pressed.up then i = i - 1 end
|
||||
i = wrap(i, #self.items)
|
||||
self.item_sel = i
|
||||
|
||||
local item = self.items[i]
|
||||
|
||||
if item ~= old then
|
||||
old:unfocus()
|
||||
old:release()
|
||||
item:focus()
|
||||
end
|
||||
|
||||
local focus = self
|
||||
if pressed.enter then
|
||||
focus = item:run()
|
||||
elseif ctrl.enter then
|
||||
item:hold()
|
||||
else
|
||||
item:release()
|
||||
end
|
||||
|
||||
if focus == item then
|
||||
focus = self
|
||||
end
|
||||
|
||||
return focus
|
||||
end
|
||||
|
||||
function Screen:draw(brush, y)
|
||||
for i, item in ipairs(self.items) do
|
||||
item:draw(brush, y + i - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function Menu:init(screens)
|
||||
self.screens = screens
|
||||
self.screen_sel = 1
|
||||
end
|
||||
|
||||
function Menu:focus()
|
||||
self.screens[self.screen_sel]:focus()
|
||||
end
|
||||
|
||||
function Menu:unfocus()
|
||||
self.screens[self.screen_sel]:unfocus()
|
||||
end
|
||||
|
||||
function Menu:navigate(ctrl, pressed)
|
||||
local s = self.screen_sel
|
||||
local old = self.screens[s]
|
||||
if pressed.left then s = s - 1 end
|
||||
if pressed.right then s = s + 1 end
|
||||
s = wrap(s, #self.screens)
|
||||
self.screen_sel = s
|
||||
|
||||
local screen = self.screens[s]
|
||||
if screen ~= old then
|
||||
old:unfocus()
|
||||
screen:focus()
|
||||
end
|
||||
|
||||
local focus = screen:navigate(ctrl, pressed)
|
||||
if focus == screen then focus = self end
|
||||
|
||||
return focus
|
||||
end
|
||||
|
||||
function Menu:draw(brush, y)
|
||||
self.screens[self.screen_sel]:draw(brush, y)
|
||||
end
|
Loading…
Reference in a new issue