Skip to content

Commit

Permalink
Adding support for 3 players
Browse files Browse the repository at this point in the history
Also for SCH drop dash. Fixed a few bugs from the previous refactors.
Missing support for SCH Chaotix, bosses, and super/hyper transformations.
  • Loading branch information
flamewing committed Sep 6, 2020
1 parent 62c30a7 commit ab0f48e
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 74 deletions.
26 changes: 21 additions & 5 deletions sonic-hud.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ()
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
87 changes: 41 additions & 46 deletions sonic/common/char-info.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 ),
}

Expand Down Expand Up @@ -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

10 changes: 7 additions & 3 deletions sonic/common/character-hud.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion sonic/common/game-data/sch-data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
-------------------------------------------------------------------------------

sch_rom_data = {
Revision = 0x2a0,
Revision = 0x2a4,
Leader_ptr = 0xfffe2e,
Sidekick1_ptr = 0xfffe30,
Sidekick2_ptr = 0xfffe32,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion sonic/common/game-data/sonic1-data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions sonic/common/game-data/sonic2-data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
1 change: 1 addition & 0 deletions sonic/common/game-data/sonic3k-data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
8 changes: 7 additions & 1 deletion sonic/common/jump-predictor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
75 changes: 58 additions & 17 deletions sonic/common/rom-check.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit ab0f48e

Please sign in to comment.