diff --git a/sonic-hud.lua b/sonic-hud.lua index a1ba796..34b6b16 100644 --- a/sonic-hud.lua +++ b/sonic-hud.lua @@ -26,7 +26,7 @@ end gens.persistglobalvariables({ -- Enabled HUDs game_hud = true, - active_char_huds = {[1]=true, [2]=true}, + active_char_huds = {[1]=true, [2]=true, [3] = true}, stat_hud = true, boss_hud_active = true, menu_active = true, -- ignores disable_lua_hud @@ -94,6 +94,20 @@ main_hud:add_toggle(make_toggle(58, false, Container_widget.toggled, main_hud, g -- 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}, + }, +} -- Reads mem values, emulates a couple of frames, displays everything draw_hud = function () @@ -103,9 +117,11 @@ draw_hud = function () set_chardata(selchar) char_huds = {} levelbounds = {} - for i, char in pairs(characters) do - table.insert(char_huds, Character_hud:new(char, i, (i == 2 and 214) or 0, 169, active_char_huds[i])) - table.insert(levelbounds, Level_bounds:new(char, i, (char.is_p1 and {255, 255, 0, 255}) or {255, 0, 255, 255})) + 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 @@ -193,7 +209,7 @@ end local function reset_config() game_hud = true - active_char_huds = {[1]=true, [2]=true} + active_char_huds = {[1]=true, [2]=true, [3] = true} stat_hud = true boss_hud_active = true menu_active = true diff --git a/sonic/common/char-info.lua b/sonic/common/char-info.lua index 7611ba2..277fd1b 100644 --- a/sonic/common/char-info.lua +++ b/sonic/common/char-info.lua @@ -412,7 +412,6 @@ if rom:is_sonic_cd() then return string.format("%s%3d%%", (val >= max and "Y") or "N", math.floor((100 * absinertia) / maxcharge)) end - -- Standard spindash. function Character:is_peelout() return not self:is_rolling() end @@ -434,7 +433,6 @@ else return memory.readbytesigned(self.offset + curr_data.spindash_flag) ~= 0 end - -- Standard spindash. function Character:is_peelout() return memory.readbytesigned(self.offset + curr_data.spindash_flag) < 0 end @@ -452,6 +450,29 @@ else end end +if curr_data.dropdash_flag ~= nil then + function Character:dropdash_active() + return self.charid == charids.sonic and memory.readbyte(self.offset + curr_data.dropdash_flag) > 0 + end + + function Character:dropdash_timer() + local timer = memory.readbytesigned(self.offset + curr_data.dropdash_delay) + if timer < 0 then + return "Ready" + else + return string.format("%5d", memory.readbytesigned(self.offset + curr_data.dropdash_delay) + 1) + end + end +else + function Character:dropdash_active() + return false + end + + function Character:dropdash_timer() + return string.format("%5d", 0) + end +end + function Character:spindash_icon() return (self:is_peelout() and "sonic-peelout") or self.curr_set.spindash end @@ -576,12 +597,12 @@ else end function Character:cputime_active() - local cputime = self.cputime_time_left() + local cputime = self:cputime_time_left() return cputime ~= 0 and cputime < 599 end function Character:cputime_timer() - return string.format("%3d", self.cputime_time_left()) + return string.format("%3d", self:cputime_time_left()) end if curr_data.respawn_counter == nil then @@ -607,7 +628,7 @@ function Character:despawn_timer() end function Character:respawn_time_left() - return (64 - AND(self:get_level_frames(), 0x3f)) % 64 + return (64 - AND(game:get_level_frames(), 0x3f)) % 64 end if curr_data.CPU_routine ~= nil and curr_data.obj_control ~= nil then @@ -711,6 +732,7 @@ function Character:init(id, index, port) -- the common icons. To add new ones, just copy and modify accordingly. self.status_huds = { Create_HUD(self, self.spindash_active , self.spindash_charge , self.spindash_icon), + Create_HUD(self, self.dropdash_active , self.dropdash_timer , self.spindash_icon), Create_HUD(self, self.hit_active , self.hit_timer , self.wounded_icon ), } @@ -757,48 +779,21 @@ end characters = nil -- Set character data +local portrait_data = { + [charids.sonic ] = portraits.sonic, + [charids.tails ] = portraits.tails, + [charids.knuckles] = portraits.knuckles, + [charids.amy_rose] = portraits.amy_rose, + [charids.charmy ] = portraits.charmy, + [charids.bunnie ] = portraits.bunnie, + [charids.espio ] = portraits.sonic, -- TODO: Fix this + [charids.vector ] = portraits.knuckles, -- TODO: Fix this +} + function set_chardata(selchar) game.curr_char = selchar - if selchar == charids.sonic_tails then -- Sonic + Tails - characters = { - Character:new(charids.sonic , 0, portraits.sonic), - Character:new(charids.tails , 1, portraits.tails) - } - elseif selchar == charids.sonic then -- Sonic solo - characters = { - Character:new(charids.sonic , 0, portraits.sonic) - } - elseif selchar == charids.tails then -- Tails - characters = { - Character:new(charids.tails , 0, portraits.tails) - } - elseif selchar == charids.knuckles then -- Knuckles - characters = { - Character:new(charids.knuckles, 0, portraits.knuckles) - } - elseif selchar == charids.amy_tails then -- Amy + Tails - characters = { - Character:new(charids.amy_rose, 0, portraits.amy_rose), - Character:new(charids.tails , 1, portraits.tails) - } - elseif selchar == charids.amy_rose then -- Amy - characters = { - Character:new(charids.amy_rose, 0, portraits.amy_rose) - } - --[[ - elseif selchar == charids.cream then -- Cream - characters = { - Character:new(charids.cream , 0, portraits.cream ) - } - --]] - elseif selchar == charids.charmy then -- charmy - characters = { - Character:new(charids.charmy , 0, portraits.charmy ) - } - elseif selchar == charids.bunnie then -- Bunnie - characters = { - Character:new(charids.bunnie , 0, portraits.bunnie ) - } + characters = {} + for i, char in ipairs(selchar) do + table.insert(characters, Character:new(char, i - 1, portrait_data[char])) end end - diff --git a/sonic/common/character-hud.lua b/sonic/common/character-hud.lua index a6d7901..d900d52 100644 --- a/sonic/common/character-hud.lua +++ b/sonic/common/character-hud.lua @@ -39,14 +39,18 @@ local shield_icons = {[shieldids.no_shield] = "blank", [shieldids.lightning_shield] = "shield-lightning", [shieldids.bubble_shield] = "shield-bubble"} -function Character_hud:construct(char, index, x, y, active) +function Character_hud:construct(char, x, y, active) self:super(x, y, active) self.character = char local pad = (char.is_p1 and 15) or 1 - local char_hud = Frame_widget:new(0, 0, 99, 51) + local char_hud = Frame_widget:new(0, 0, (char.is_p1 and 99) or 101, 51) - self:add_toggle(make_toggle(99, true, Container_widget.toggled, self, active), 0, 52) + if char.is_p1 then + self:add_toggle(make_toggle(51, false, Container_widget.toggled, self, active), -3, 0) + else + self:add_toggle(make_toggle(51, false, Container_widget.toggled, self, active), 102, 0) + end char_hud:add(Icon_widget:new(0, 0, bind(char.get_face, char)), 2, 2) if char.is_p1 then local lives_delta = (game.get_continues == nil and 4) or 0 diff --git a/sonic/common/game-data/sch-data.lua b/sonic/common/game-data/sch-data.lua index 4e12955..13ed34b 100644 --- a/sonic/common/game-data/sch-data.lua +++ b/sonic/common/game-data/sch-data.lua @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------- sch_rom_data = { - Revision = 0x2a0, + Revision = 0x2a4, Leader_ptr = 0xfffe2e, Sidekick1_ptr = 0xfffe30, Sidekick2_ptr = 0xfffe32, @@ -38,6 +38,7 @@ sch_rom_data = { control_counter = 0x56, respawn_counter = 0x58, CPU_routine = 0x5a, + Player_set = 0xffffb1, code = 0x4, x_pos = 0x10, x_sub = 0x12, @@ -66,6 +67,8 @@ sch_rom_data = { shield = 0x7c, bubbles = 0x82, carry_delay = 0x8f, + dropdash_flag = 0x96, + dropdash_delay = 0x97, air_frames = 0x40, GameModeID_SegaScreen = 0x0, GameModeID_TitleScreen = 0x4, diff --git a/sonic/common/game-data/sonic1-data.lua b/sonic/common/game-data/sonic1-data.lua index c0de82f..289dc0e 100644 --- a/sonic/common/game-data/sonic1-data.lua +++ b/sonic/common/game-data/sonic1-data.lua @@ -41,7 +41,7 @@ sonic1_rom_data = { Shield_active = 0xfffe2c, top_speed = 0xfff760, air_left = 0x28, - id = 0xfffe15, + id = 0x0, x_pos = 0x8, x_sub = 0xa, y_pos = 0xc, diff --git a/sonic/common/game-data/sonic2-data.lua b/sonic/common/game-data/sonic2-data.lua index 5094725..09651db 100644 --- a/sonic/common/game-data/sonic2-data.lua +++ b/sonic/common/game-data/sonic2-data.lua @@ -71,6 +71,7 @@ sonic2_rom_data = { control_counter = 0xfff702, respawn_counter = 0xfff704, CPU_routine = 0xfff708, + Player_option = 0xffff72, GameModeID_Demo = 0x8, GameModeID_Level = 0xc, } diff --git a/sonic/common/game-data/sonic3k-data.lua b/sonic/common/game-data/sonic3k-data.lua index 53e9ea1..1038b08 100644 --- a/sonic/common/game-data/sonic3k-data.lua +++ b/sonic/common/game-data/sonic3k-data.lua @@ -78,6 +78,7 @@ sonic3_rom_data = { control_counter = 0xfff702, respawn_counter = 0xfff704, CPU_routine = 0xfff708, + Player_option = 0xffff08, GameModeID_Demo = 0x8, GameModeID_Level = 0xc, } diff --git a/sonic/common/jump-predictor.lua b/sonic/common/jump-predictor.lua index 68e206b..a4df36d 100644 --- a/sonic/common/jump-predictor.lua +++ b/sonic/common/jump-predictor.lua @@ -64,12 +64,18 @@ function want_prediction() return enable_predictor and prediction_wanted() and (movie.recording() or not movie.playing()) end +local joypad_table = { + [1] = 1, + [2] = 2, + [3] = '1B', + [4] = '1C' +} function predict_jumps() savestate.save(state) for n=1, 2 do repeat for i, _ in pairs(characters) do - joypad.set(i, buttons) + joypad.set(joypad_table[i], buttons) end gens.emulateframeinvisible() until not gens.lagged() diff --git a/sonic/common/rom-check.lua b/sonic/common/rom-check.lua index 8e111dc..762ea56 100644 --- a/sonic/common/rom-check.lua +++ b/sonic/common/rom-check.lua @@ -248,7 +248,7 @@ function rom_info:construct(checksum, engine, air_cap, tails_flies, cream_flies, elseif AND(get_char, 0xff0000) ~= 0 then -- RAM address self.get_char = function() return memory.readword(get_char) end else - self.get_char = function() return get_char end + self.get_char = function() return {get_char} end end self.boss_array = boss_array or {} self.ring_offset = ring_offset or 0xfffe20 @@ -292,18 +292,59 @@ local function scheroes_check(self, val) return true end +-- We remap the value read from memory in S2/S3/S&K/S3&K to the internal IDs +-- defined in the enums file. +local s23k_char_table = { + [0] = {charids.sonic, charids.tails}, + [1] = {charids.sonic}, + [2] = {charids.tails}, + [3] = {charids.knuckles}, +} + +local function s2_char() + return s23k_char_table[memory.readword(sonic2_rom_data.Player_option)] +end + +local function s3k_char() + return s23k_char_table[memory.readword(sonic3_rom_data.Player_option)] +end + +-- We remap the value read from memory in S2Amy/S3Amy to the internal IDs +-- defined in the enums file. +local s23kamy_char_table = { + [0] = {charids.amy_rose, charids.tails}, + [1] = {charids.amy_rose}, + [2] = {charids.tails}, + [3] = {charids.knuckles}, +} + -- We remap the value read from memory in Amy in S2 to the internal IDs -- defined in the enums file. local function s2amy_char() - local char = memory.readword(0xffff72) - return (char == 2 and charids.tails) or (char + charids.amy_tails) + return s23kamy_char_table[memory.readword(sonic2_rom_data.Player_option)] end --- We remap the value read from memory in S3Amy to the internal IDs --- defined in the enums file. local function s3kamy_char() - local char = memory.readword(0xffff08) - return (char >= 2 and char) or (char + charids.amy_tails) + return s23kamy_char_table[memory.readword(sonic3_rom_data.Player_option)] +end + +-- We remap the value read from memory in Sonic Classic Heroes to the internal IDs +-- defined in the enums file. +local scheroes_char_table = { + [0] = {}, + [1] = {charids.sonic}, + [2] = {charids.tails}, + [3] = {charids.sonic, charids.tails}, + [4] = {charids.knuckles}, + [5] = {charids.sonic, charids.knuckles}, + [6] = {charids.tails, charids.knuckles}, + [7] = {charids.sonic, charids.tails, charids.knuckles}, +} + +-- We remap the value read from memory in Amy in S2 to the internal IDs +-- defined in the enums file. +local function scheroes_char() + return scheroes_char_table[memory.readbyte(sch_rom_data.Player_set)] end -- Check for plain S&K. @@ -338,23 +379,23 @@ local supported_games = { s1tnl = rom_info:new(sums.s1tnl , eng.s1 , true , false, false, charids.sonic , bosses.s1tnl , 0xfffe20 , nil , nil , nil , sonic1_rom_data ), scd = rom_info:new(sums.scd , eng.scd, true , false, false, charids.sonic , bosses.scd , 0xff1512 , nil , nil , nil , soniccd_rom_data), scdjp = rom_info:new(sums.scdjp , eng.scd, true , false, false, charids.sonic , bosses.scd , 0xff1512 , nil , nil , nil , soniccd_rom_data), - s2 = rom_info:new(sums.s2 , eng.s2 , true , false, false, 0xffff72 , bosses.s2 , 0xfffe20 , 0xffeed0 , huds.s2 , nil , sonic2_rom_data ), + s2 = rom_info:new(sums.s2 , eng.s2 , true , false, false, s2_char , bosses.s2 , 0xfffe20 , 0xffeed0 , huds.s2 , nil , sonic2_rom_data ), s2knux = rom_info:new(sums.sk , eng.s2 , false, false, false, charids.knuckles, bosses.s2knux , 0xfffe20 , 0xffeed0 , huds.s2knux, sklockon_check(sums.s2), sonic2_rom_data ), s2amy = rom_info:new(sums.s2amy , eng.s2 , false, false, false, s2amy_char , bosses.s2amy , 0xfffe20 , 0xffeed0 , huds.s2amy , nil , sonic2_rom_data ), s2boom = rom_info:new(sums.s2boom , eng.s2 , false, false, false, charids.sonic , bosses.s2boom , 0xfffe02 , 0xffeed0 , huds.s2boom, nil , sonic2_rom_data ), - s2rob = rom_info:new(sums.s2rob , eng.s2 , true , false, false, 0xffff72 , bosses.s2rob , 0xfffe20 , 0xffeed0 , huds.s2rob , nil , sonic2_rom_data ), + s2rob = rom_info:new(sums.s2rob , eng.s2 , true , false, false, s2_char , bosses.s2rob , 0xfffe20 , 0xffeed0 , huds.s2rob , nil , sonic2_rom_data ), s2vr = rom_info:new(sums.s2vr , eng.s2 , true , false, false, charids.sonic , bosses.s2 , 0xfffe02 , 0xffeed0 , huds.s2 , nil , sonic2_rom_data ), - s2hrtw = rom_info:new(sums.s2hrtw , eng.s2 , true , false, false, 0xffff72 , bosses.s2 , 0xfffe20 , 0xffeed0 , huds.s2 , nil , sonic2_rom_data ), + s2hrtw = rom_info:new(sums.s2hrtw , eng.s2 , true , false, false, s2_char , bosses.s2 , 0xfffe20 , 0xffeed0 , huds.s2 , nil , sonic2_rom_data ), s2keh = rom_info:new(sums.s2keh , eng.keh, false, false, false, charids.knuckles, bosses.s2keh , 0xfffefc , 0xfff47e , huds.s2keh , nil , keh_rom_data ), - s1and2 = rom_info:new(sums.s1and2 , eng.s2 , true , false, false, 0xffff72 , bosses.s1and2 , 0xfffe20 , 0xffeed0 , huds.s1and2, nil , sonic2_rom_data ), - s1and2b = rom_info:new(sums.s1and2b , eng.s2 , true , false, false, 0xffff72 , bosses.s1and2 , 0xfffe20 , 0xffeed0 , huds.s1and2, nil , sonic2_rom_data ), - s3 = rom_info:new(sums.s3 , eng.s3 , false, true , false, 0xffff08 , bosses.s3 , 0xfffe20 , 0xffee24 , huds.s3 , nil , sonic3_rom_data ), - sk = rom_info:new(sums.sk , eng.sk , false, true , false, 0xffff08 , bosses.sk , 0xfffe20 , 0xffee24 , huds.sk , sknolockon_check , sonic3_rom_data ), - s3k = rom_info:new(sums.sk , eng.s3k, false, true , false, 0xffff08 , bosses.sk , 0xfffe20 , 0xffee24 , huds.sk , sklockon_check(sums.s3), sonic3_rom_data ), - s3kmaster= rom_info:new(sums.s3kmaster, eng.s3k, false, true , false, 0xffff08 , bosses.sk , 0xfffe20 , 0xffee24 , huds.sk , nil , sonic3_rom_data ), + s1and2 = rom_info:new(sums.s1and2 , eng.s2 , true , false, false, s2_char , bosses.s1and2 , 0xfffe20 , 0xffeed0 , huds.s1and2, nil , sonic2_rom_data ), + s1and2b = rom_info:new(sums.s1and2b , eng.s2 , true , false, false, s2_char , bosses.s1and2 , 0xfffe20 , 0xffeed0 , huds.s1and2, nil , sonic2_rom_data ), + s3 = rom_info:new(sums.s3 , eng.s3 , false, true , false, s3k_char , bosses.s3 , 0xfffe20 , 0xffee24 , huds.s3 , nil , sonic3_rom_data ), + sk = rom_info:new(sums.sk , eng.sk , false, true , false, s3k_char , bosses.sk , 0xfffe20 , 0xffee24 , huds.sk , sknolockon_check , sonic3_rom_data ), + s3k = rom_info:new(sums.sk , eng.s3k, false, true , false, s3k_char , bosses.sk , 0xfffe20 , 0xffee24 , huds.sk , sklockon_check(sums.s3), sonic3_rom_data ), + s3kmaster= rom_info:new(sums.s3kmaster, eng.s3k, false, true , false, s3k_char , bosses.sk , 0xfffe20 , 0xffee24 , huds.sk , nil , sonic3_rom_data ), s3kamy = rom_info:new(sums.s3kamy , eng.s3k, false, true , false, s3kamy_char , bosses.sk , 0xfffe20 , 0xffee24 , huds.s3kamy, nil , sonic3_rom_data ), s4cyb = rom_info:new(sums.s4cyb , eng.s3k, false, true , false, charids.sonic , bosses.s4cyb , 0xfffe20 , 0xffee24 , nil , nil , sonic3_rom_data ), - scheroes = rom_info:new(sums.scheroes , eng.sch, false, true , false, charids.sonic , bosses.scheroes, sch_rom_data.Ring_count, sch_rom_data.Camera_delay_ptr, nil , scheroes_check , sch_rom_data ), + scheroes = rom_info:new(sums.scheroes , eng.sch, false, true , false, scheroes_char , bosses.scheroes, sch_rom_data.Ring_count, sch_rom_data.Camera_delay_ptr, nil , scheroes_check , sch_rom_data ), } -- These two variables will hold info on the currently loaded ROM.