diff --git a/Lua/actor lister.lua b/Lua/actor lister.lua index 6bea5a3..d99f1bf 100755 --- a/Lua/actor lister.lua +++ b/Lua/actor lister.lua @@ -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 diff --git a/Lua/addrs/M common.lua b/Lua/addrs/M common.lua index 8aafa77..5580b77 100755 --- a/Lua/addrs/M common.lua +++ b/Lua/addrs/M common.lua @@ -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), diff --git a/Lua/cheat menu.lua b/Lua/cheat menu.lua index f3c377f..fcc4c9a 100755 --- a/Lua/cheat menu.lua +++ b/Lua/cheat menu.lua @@ -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 diff --git a/Lua/classes.lua b/Lua/classes.lua index 7dcc0df..ce3f69d 100644 --- a/Lua/classes.lua +++ b/Lua/classes.lua @@ -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 diff --git a/Lua/menu classes.lua b/Lua/menu classes.lua new file mode 100644 index 0000000..c51952e --- /dev/null +++ b/Lua/menu classes.lua @@ -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