--Soul Blazer / Soul Blader script. By FatRatKnight
local Region= "J" -- Try "U" or "J" - Perhaps I should code an auto-identify
local R1u,R1s= memory.readbyte ,memory.readsbyte
local R2u,R2s= memory.readword ,memory.readsword
local R4u,R4s= memory.readdword,memory.readsdword
local function NullFn() end
local obj_list_cr= {}
--*****************************************************************************
local function Roll(rTbl)
--*****************************************************************************
local c= 0
for i= 14, 0, -1 do
local v= rTbl[i]+rTbl[i+1]+c
c= math.floor(v/0x100)
rTbl[i]= v%0x100
end
rTbl[15]= (rTbl[15]+1)%0x100
end
local R= {}
local Rlist= {0} -- Have a default to display without an error.
local Thresholds= {
[0]= {L= 1, M= 4},
{L= 3, M= 8},
{L= 5, M=12},
{L= 7, M=16},
{L= 9, M=20},
{L=11, M=24},
{L=51, M=100},
}
--*****************************************************************************
local function GetColor(v,zone)
--*****************************************************************************
if zone > 7 then return 0x808080 end -- I'm confused.
if v < Thresholds[zone].L then return 0x00FFFF end -- Large. Awesome.
if v < Thresholds[zone].M then return 0xFFFF00 end -- Medium. Nice.
return 0x808080 -- Small. Nuts.
end
local OldR= -1
--*****************************************************************************
local function UpdateRNG(count)
--*****************************************************************************
local TestR= R4u(0x7E0302)
if TestR == OldR then return end -- Assume we're good, don't process.
OldR= TestR
for i= 0,15 do R[i]= R1u(0x7E0302+i) end
for i= 1, count do
Roll(R)
Rlist[i]= R[0]
end
end
local DirTbl= {[0]="^","v",">","<"}
--*****************************************************************************
local function RNGHandler(x,top,count)
--*****************************************************************************
UpdateRNG(count)
for i= 1, count do
local c= GetColor(Rlist[i]%100,R1u(0x7E03BA))
gui.text(x ,top + 15*(i-1), string.format("%3d",Rlist[i]), c)
gui.text(x+24,top + 15*(i-1), DirTbl[Rlist[i]%4])
end
end
local DisplayBoxes= {}
--*****************************************************************************
local function ShowObjectBoxes(SX,SY)
--*****************************************************************************
if #DisplayBoxes <= 0 then return end
for i= 1, #DisplayBoxes do
local bx= DisplayBoxes[i]
gui.rectangle(
bx.L *SX , bx.T *SY ,
-- bx.R *SX , bx.B *SY ,
(bx.R+1)*SX-1,(bx.B+1)*SY-1,
1,0x4000FF00,0xE000FF00
)
end
end
local CamRegion= {U= 0x7E0052, J= 0x7E0050}
local CamAddr= CamRegion[Region]
--*****************************************************************************
local function MakeObjectBox_cr(obj) -- Coroutine function
--*****************************************************************************
--Grabs box data for later painting.
local BoxesToPaint= 0
local CamX, CamY= R2s(CamAddr)+16,R2s(CamAddr+2)+16
while obj do
if R2u(obj+0x16) < 0x8000 then -- Luckily the top bit says visibility
BoxesToPaint= BoxesToPaint+1
local b= DisplayBoxes[i] or {} -- I'm silly, reusing tables
-- Get every side of our box!
local X,Y= R2s(obj+0x00)-CamX, R2s(obj+0x02)-CamY
b.L,b.T= X-R2s(obj+0x08) , Y-R2s(obj+0x0A)
b.R,b.B= X+R2s(obj+0x0C)-b.L , Y+R2s(obj+0x0E)-b.T
X= b.L
Y= b.T
b.L= math.max(0,math.min(255 ,b.L))
b.T= math.max(0,math.min(223 ,b.T))
X= b.L-X -- I want to know whether my range restriction
Y= b.T-Y -- actually caused a change I need to know.
b.R= math.max(0,math.min(255-b.L,b.R-X))
b.B= math.max(0,math.min(223-b.T,b.B-Y))
--Color. I don't know how to identify objects, but once I do, I'll love colors!
b.c= 0x00FF00
--Well, store the information.
DisplayBoxes[BoxesToPaint]=b
end
obj= coroutine.yield()
end
--Shrink down our list if what we have now is less than before.
while DisplayBoxes[BoxesToPaint+1] ~= nil do
BoxesToPaint= BoxesToPaint+1
DisplayBoxes[BoxesToPaint]= nil
end
end
table.insert(obj_list_cr,MakeObjectBox_cr)
local LairData= {n=0,t=0}
local LairColors= {}
--*****************************************************************************
local function ShowLairs(x,top)
--*****************************************************************************
local lairs= LairData.n
gui.text(x ,top+lairs*15,string.format("%3d",lairs ),0x00FFFF)
gui.text(x+40,top+lairs*15,string.format("%3d",LairData.t),0xFFFF00)
if LairData.n <= 0 then return end
for i= 1, LairData.n do
gui.text(x,top+(i-1)*15,LairData[i],LairColors[i])
end
end
--*****************************************************************************
local function LairList_cr(obj) -- coroutine function
--*****************************************************************************
-- Attempts to identify and store a list of lairs for later painting
local lairs= 0
local total= 0
while obj do
total= total+1 -- Total object count. Just in case.
--Identify if this is a lair.
--Method gives several false positives, minimal false negatives. Need better.
local count= R1u(obj+0x2E) -- Count of enemies left
local Assoc= R2u(obj+0x32) -- Associated lair
if (count ~= 0) and (Assoc == 0) then
lairs= lairs+1 -- Lairs counted.
-- In sight, or not?
local color= 0x00FF00
if R2u(obj+0x16) >= 0x8000 then color= 0x7F7F7F end
LairColors[lairs]= color
-- Stick the text in place
LairData[lairs]= string.format("%3X %3X %2d %3d",
R2u(obj),R2u(obj+2),count,R2u(obj+0x14)
)
end
obj= coroutine.yield()
end
LairData.n= lairs -- Our display routine will use this n.
LairData.t= total -- Would like to see how many objects total.
end
table.insert(obj_list_cr,LairList_cr)
--*****************************************************************************
local function Finalize(FnTbl) for i= 1, #FnTbl do FnTbl[i]() end end
--*****************************************************************************
local ObjectHeaderPointer= {U= 0x7E06A6, J= 0x7E06A3}
local ObjPtr= ObjectHeaderPointer[Region]
--*****************************************************************************
local function IterateObjects()
--*****************************************************************************
if #obj_list_cr <= 0 then return end -- Uh, nothing to do. Defensive code
local co= {}
for i= 1, #obj_list_cr do co[i]= coroutine.wrap(obj_list_cr[i]) end
local Panic= 0
local Next= R2u(ObjPtr)
while Next ~= 0 do
Panic= Panic+1
if Panic > 70 then -- There shouldn't be more than 64 objects anyway.
gui.status("SB_obj","PANIC")
Finalize(co)
return
end
local Addr= 0x7E0000 + Next
for i= 1, #co do co[i](Addr) end -- Feed our functions objects!
Next= R2u(Addr + 0x3E)
end
gui.status("SB_obj",Panic) -- A number indicates normal function.
Finalize(co)
end
local OldEmulated= on_frame_emulated or NullFn
on_frame_emulated= function() OldEmulated(); IterateObjects() end
local OldLoad= on_post_load or NullFn
on_post_load= function(s,b) OldLoad(s,b); IterateObjects() end
local RNG_count,RNG_prev= 0, 0
--*****************************************************************************
local function BasicHUD(x,top,height)
--*****************************************************************************
gui.text(0, 0,string.format("%4X",R2u(0x7E0800)),0xFFFF80,0x40000000)
gui.text(0,15,string.format("%4X",R2u(0x7E0802)),0x80FFFF,0x40000000)
local bottom= top+height
gui.text(x,bottom-16,string.format("%4X",R2u(0x7E0312)))
local RNG= R1u(0x7E0311)
local RNG_diff= (RNG - RNG_prev) % 256
RNG_count= RNG_count + RNG_diff
RNG_prev= RNG
gui.text(x,bottom-32,string.format("%5d:%2X",RNG_count,RNG_prev))
end
local OldPaint= on_paint or NullFn -- Script chaining!
--*****************************************************************************
function on_paint(non_synth)
--*****************************************************************************
OldPaint(non_synth) -- Decent way to chain scripts together.
local x,height= gui.resolution()
local ScaleX, ScaleY= x/256, height/224
x= x+gui.delta_right_gap(152) -- Place ourselves to the right of that gunk
local y= gui.delta_top_gap(0) -- Incorperate spare space provided by the
height= height+y+gui.delta_bottom_gap(0) -- chained scripts we have.
local count= math.floor(height/15)
RNGHandler(x,-y,count)
ShowLairs(x+40,-y)
ShowObjectBoxes(ScaleX,ScaleY)
BasicHUD(x+40,-y,height)
end
--[[
Address 0x7E0800, object size=64
+0x00,2s X-pos
+0x02,2s Y-pos
+0x04,2s X-spd
+0x06,2s Y-spd
+0x08,2s Hitbox Left (subtract from X-pos)
+0x0A,2s Hitbox Top (subtract from Y-pos)
+0x0C,2s Hitbox Right (add to X-pos)
+0x0E,2s Hitbox Bottom (add to Y-pos)
+0x10,
+0x12,
+0x14,2u Useful timer (such as lair spawn rate)
+0x16,2x Flags (0x8000 indicates offscreen)
+0x18,2x Some kind of ID number?
+0x1A,
+0x1C,
+0x1E,
+0x20,
+0x22,
+0x24,
+0x26,2s Invincibility timer
+0x28,
+0x2A,
+0x2C,
+0x2E,2x Looks suspicious... Count in lair?
+0x30,
+0x32,2x Link to associated lair
+0x34,
+0x36,
+0x38,
+0x3A,
+0x3C,2x Link (prev)
+0x3E,2x Link (next)
]]--