User File #74799479026510261

Upload All User Files

#74799479026510261 - SNES Ghoul Patrol - DTC10 T8's script

DTC10T8_GP_Basic.lua
Game: Ghoul Patrol ( SNES, see all files )
175 downloads
Uploaded 10/12/2021 2:22 PM by FatRatKnight (see all 245)
This was the script we developed in the DTC. Has hitboxes, position, and speed. And a few random things. This was my version, without the side-radar.
--SNES Ghoul Patrol

assert(memory.usememorydomain("WRAM"))

local R1u , R1s = memory.read_u8     , memory.read_s8
local R2u , R2s = memory.read_u16_le , memory.read_s16_le
local R4u , R4s = memory.read_u32_le , memory.read_s32_le

local function R3u(a) return R1u(a) + R2u(a+1)*0x100 end

local Tx= gui.pixelText
local Box= gui.drawRectangle

local R2x = function(v) return string.format("%04X",R2u(v)) end

-------------------------------------------------------------------------------
local FauxRAMWatch= {
-------------------------------------------------------------------------------
    {addr=0x0A6A,f=R1u,clr=0xFFFFFFFF},   --Run timer
    {addr=0x0A72,f=R1u,clr=0xFFFFFFFF},   --Dash timer
    {addr=0x0A56,f=R1u,clr=0xFFFFFF00},   --Slide timer
    {addr=0x0A22,f=R2s,clr=0xFFFFFFFF},   --Height
    {addr=0x0028,f=R2x,clr=0xFF00FFFF},   --RNG
    {addr=0x0024,f=R2u,clr=0xFFFFFFFF},   --Frame count
    {addr=0x0A4E,f=R2s,clr=0xFF0040FF},   --Invincibility
    {addr=0x7BFA,f=R1u,clr=0xFFFF8000},   --Boss(?) (temp)
    {addr=0x1F49,f=R2s,clr=0xFFFF8000},   --Boss HP
    {addr=0x1F45,f=R2x,clr=0xFFFF8000},   --Boss ?
    {addr=0x715C,f=R1s,clr=0xFFFF8000},   --Boss ?
}

--*****************************************************************************
local function HexVal(v)
--*****************************************************************************
--Just so we see negative -1 in hex as -1 and not FFFFFFFF.

    local s= "+"
    if v < 0 then s= "-"; v= -v end
    return s .. string.format("%X",v)
end

--*****************************************************************************
local function FetchCam() return  R2s(0x1DDB), R2s(0x1DDD)  end
--Returns two values. Best catch it with  CamX,CamY=FetchCam()  or something.


--*****************************************************************************
local function SynthesizedPos(v)
--*****************************************************************************
-- ###.#.## format.
-- ###.-.-- Tile position
-- ---.#.-- Pixel position within that tile
-- ---.-.## Subpixel position

    local s= string.format("%3d",math.floor(v / 0x80000))
    v= v%0x80000
    s= s .. "." .. math.floor(v/0x10000) .. "."
    v= (v%0x10000) * 100 / 0x10000
    return s .. string.format("%02d",math.floor(v))
end

--*****************************************************************************
local function PxTxRight(x,y,t,c1,c2,f,sn)
--*****************************************************************************
--Right-justifies gui.pixelText based on a constant width.
    x= x - string.len(t)*4
    gui.pixelText(x,y,t,c1,c2,f,sn)
end

--*****************************************************************************
local function SplitColor(x,y,s,c1,c2)
--*****************************************************************************
--Shades the last four characters in grey, by default.
--This is bugged, as I forgot gui.text is left-justified. I'll fix later.
    Tx(x   ,y,string.sub(s,1,-5),c1 or 0xFFFFFFFF)
    Tx(x+16,y,string.sub(s,-4)  ,c2 or 0xFFC0C0C0)
end

--*****************************************************************************
local function PlayerProjectiles(CamX, CamY)
--*****************************************************************************
--Tries to get at the five projectiles somewhere in memory.

    for i= 0, 9 do
--Someday, an if block to check if the projectile exists...
        local o= i*2 --offset
        local X, Y= R2u(0x0072F2+o)-CamX, R2u(0x007392+o)-CamY
        Box(X-4,Y-8,9,9,0xFFFFFFFF)
        --Tx(2,50+8*i,R2u(0x0072F2+o) .. " " .. R2u(0x007392+o) .. " " .. R2u(0x007342+o))
        local H= R2u(0x007342+o)
        PxTxRight(X-4,Y-10,H+5)
        PxTxRight(X-4,Y+ 2,H-5)
    end
    Box(4,4,5,5,0xFFFFFFFF)
end

local Scale  = {
    Factor = 1.00,
    Adj    = 0.25,
    KeyUp  = "PageUp",
    KeyDown= "PageDown",
 
    Left   = 0,
    Right  = 256,
    Top    = 0,
    Bottom = 224
}

--*****************************************************************************
local function AdjustScaling(Keys)
--*****************************************************************************

--Adjust stuff.
    if Keys[Scale.KeyUp] then
        Scale.Factor= Scale.Factor + Scale.Adj
    elseif Keys[Scale.KeyDown] then
        Scale.Factor= math.max(Scale.Factor - Scale.Adj, 1)
    else --Sneak in an abort to the calcs this way.
        return
    end

--Adjust where the perimeter is
    Scale.Left  = math.floor(128 - 128/Scale.Factor)
    Scale.Right = 256-Scale.Left
    Scale.Top   = math.floor(112 - 112/Scale.Factor)
    Scale.Bottom= 224-Scale.Top

--Report the new scaling
    Tx(128,112,string.format("%5.2f",Scale.Factor))
end

--*****************************************************************************
local function Hitboxes(CamX, CamY)
--*****************************************************************************
    local Count= R2u(0x0000A8)
    if Count > 80 then return end --Panic

    if Scale.Factor > 1 then
        gui.drawBox(Scale.Left,Scale.Top,Scale.Right,Scale.Bottom,0x80FFFFFF,0)
    end

    for i= Count, 0, -2 do
        local ptr= R2u(0x1497+i)

        local v0E = R2u(ptr+0x0E)
        if (v0E ~= 0) then
            local Left   = R2u(ptr+0x02) - R2u(ptr+0x14)
            local Right  = R2u(ptr+0x02) + R2u(ptr+0x14)
            local Up     = R2u(ptr+0x06) - R2u(ptr+0x18)
            local Down   = R2u(ptr+0x06)
            local H_Plus = R2u(ptr+0x04) + R2u(ptr+0x16)
            local H_Minus= R2u(ptr+0x04)
            local ID     = R2u(ptr+0x0C)

            local X1= math.floor((Left-CamX) /Scale.Factor + Scale.Left)
            local X2= math.floor((Right-CamX)/Scale.Factor + Scale.Left)
            local Y1= math.floor((Up-CamY)   /Scale.Factor + Scale.Top)
            local Y2= math.floor((Down-CamY) /Scale.Factor + Scale.Top)
            gui.drawBox(X1,Y1,X2,Y2,0xFFFF4000,0x20FF4000)

            if ID < 0x2C then
                local DataPtr= R2u(0x808381+ID,"System Bus")
--                PxTxRight(X1,Y1-6,string.format("%04X",DataPtr))
--                PxTxRight(X1,Y1-6,R2s(DataPtr+0x2A))
--                PxTxRight(X1,Y2  ,R4s(DataPtr+0x38))
                PxTxRight(X1,Y1-6,H_Plus)
                PxTxRight(X1,Y2  ,H_Minus)
            else
                PxTxRight(X1,Y1-6,string.format("%02X",ID),0xFFFFFF00)
                PxTxRight(X1,Y2  ,Down,0xFFFFFF00)
            end
        end
    end
end

--Constants
local size= 0x10000

--0x79D4 - Diagonal cap
--0x8000 - Straight cap
--0x9998 - Straight acceleration
--0x6C10 - Diagonal acceleration

local Orange= 0xFFFF8000
local White = 0xFFFFFFFF
local Blue  = 0xFF0060FF
local Grey  = 0xFFC0C0C0

local DashSpeedTable= {
    {v=0x0000, s=Orange, d=Blue  }, --Hand-calculated the thresholds.
    {v=0x0DC4, s=Orange, d=Orange}, --Identifies good speeds to try Dash Mode
    {v=0x6668, s=Blue  , d=Orange}, --trickery with.
    {v=0x8001, s=Grey  , d=Orange},
    {v=0x93F0, s=Grey  , d=Blue  },
    {v=0xA1B4, s=Grey  , d=Grey  },
    {v=0xCCD0, s=Blue  , d=Grey  },
    {v=0xE668, s=Orange, d=Grey  },
    {v=0xFA58, s=Orange, d=Blue  }
}

--*****************************************************************************
local function SpeedDashColors(spd) --Expects full speed value
--*****************************************************************************
    if spd < 0 then spd= -spd end
    spd= spd%0x10000

    for i= #DashSpeedTable, 1, -1 do
        local SpeedCheck= DashSpeedTable[i]
        if spd >= SpeedCheck.v then
            return SpeedCheck.s, SpeedCheck.d
        end
    end

    return 0xFFFF00FF, 0xFFFF00FF --This should not happen. Purple's my error.
end

--*****************************************************************************
local function SpeedColors(spd)
--*****************************************************************************
--Just trying to pack Mittenz' colors in a nice function.

    spd= spd/size

    --BackSlide ready!
    if (spd <  -2) or  (spd >= 3) then return 0xFF00FF00, 0xFF00CF00 end

    --Basically no speed.
    if (spd >  -1) and (spd <  1) then return 0xFFFF0000, 0xFFC00000 end

    --Somewhere in between
    return nil, nil --Use default colors
end

-------------------------------------------------------------------------------


--*****************************************************************************
local function RNG_SpawnPos(A)    return bit.band(A,0x0F)    end
local function RNG_Sign(A)    if A >= 0x7F then return "-" end; return "+"  end
--*****************************************************************************

local CallerIDs= {
    [0x83851B]= {name="Spwn_Hpos" , fn=RNG_SpawnPos },
    [0x838523]= {name="Spwn_Vpos" , fn=RNG_SpawnPos },
    [0x83852B]= {name="Spwn_Hsign", fn=RNG_Sign },
    [0x83853C]= {name="Spwn_Vsign", fn=RNG_Sign },
}

local RNG_Calls= {}
local RequestPause= false

--*****************************************************************************
local function RNG_Call()
--*****************************************************************************
--Table info:
--  caller = Address of the detected JSL
--  A = Accumulator when RNG function ends
--  clr = color to display line
--  name = Just a name to show
--  val = Value that the caller wanted from the RNG

    local T= {}

    local Stack= emu.getregister("S")
    T.caller= R3u(Stack+1) - 3

    T.A= emu.getregister("A")

    local CallerInfo= CallerIDs[T.caller]

    if not CallerInfo then --We don't know who it is.
        T.name= ""
        T.clr= 0xFFFFFFFF --White
        T.val= ""
    else
        T.name= CallerInfo.name or ""
        T.clr= 0xFFFFFF00 --Yellow
        T.val= CallerInfo.fn(T.A)
    end --Yellow if we know.

    table.insert(RNG_Calls,T)
end
event.onmemoryexecute(RNG_Call,0x80C4E4) --RTL, so we can report the A.



--*****************************************************************************
local function TerrainRadar(x,y , px,py)
--*****************************************************************************
--Just a silly little experiment. No screen edge check, though.
--Screen location for drawing, then pixel location for radar to read.

    local X_tile= math.floor(px/8)
    local Y_tile= math.floor(py/8)

    for yT = -6, 5 do
        local RowPtr= R2u(0x42C8 + (Y_tile+yT)*2)
        for xT = -6, 6 do
            local Tile= R2u(0x010000 + RowPtr + (X_tile+xT)*2)
            local TileProp= R2u(0x58C8 + (Tile%0x400)*2)
            if TileProp%2 == 1 then --It's a wall!
                local Height= bit.band(TileProp,0x7000)
                if Height == 0 then
                    Tx(x+xT*6,y+yT*6,"X")
                else
                    Tx(x+xT*6,y+yT*6,string.format("%X",Height/0x1000))
                end
            elseif bit.band(TileProp,0x0008) ~= 0 then --Pits of DOOM!
                Tx(x+xT*6,y+yT*6,"P",0xFFFF8000)
            elseif bit.band(TileProp,0x0100) ~= 0 then --Splashy splash
                Tx(x+xT*6,y+yT*6,"W",0xFF00FFFF)
            end
        end
    end
    Box(x-6,y-6,16,12,0xC0FFFFFF,0x40FF4000)
end

--*****************************************************************************
local function PlayerHUD()
--*****************************************************************************
--All sorts of basic data here.

    --X axis
    local pos= R2u(0x0A2E)*size+R2u(0x0A2A)
    SplitColor(  6,  2,string.format("%8X",pos))
    Tx( 40,  2,SynthesizedPos(pos))

    local spd= R4s(0x0A32)
    local c1, c2= SpeedColors(spd)
    SplitColor(  6,  8,string.format("%8s",HexVal(spd)),c1,c2)
    Tx( 40,  8,string.format("%+8.2f",spd/size))

    local ClrS, ClrD= SpeedDashColors(spd)
    Tx( 72,  2,"S",ClrS)
    Tx( 72,  8,"D",ClrD)

    --Y axis
    pos= R2u(0x0A3A)*size+R2u(0x0A36)
    SplitColor( 81,  2,string.format("%8X",pos))
    Tx(115,  2,SynthesizedPos(pos))

    spd= R4s(0x0A3E)
    c1, c2= SpeedColors(spd)
    SplitColor( 81,  8,string.format("%8s",HexVal(spd)),c1,c2)
    Tx(115,  8,string.format("%+8.2f",spd/size))

    ClrS, ClrD= SpeedDashColors(spd)
    Tx(148,  2,"S",ClrS)
    Tx(148,  8,"D",ClrD)

    --Height
    pos= R2u(0x0A22)*size+R2u(0x0A20)
    SplitColor(156,  2,string.format("%8X",pos))
    Tx(190,  2,SynthesizedPos(pos))

    spd= R4s(0x0A26)
    SplitColor(156,  8,string.format("%8s",HexVal(spd)))
    Tx(190,  8,string.format("%+8.2f",spd/size))

    TerrainRadar(200,180 , R2u(0x0A2E),R2u(0x0A3A))

    --Inventory
    Tx( 2, 200, string.format("%03X %03X %03X %03X", R2u(0x1D21), R2u(0x1D1F), R2u(0x1D23), R2u(0x1D29)))
    Tx( 2, 208, string.format("K:%X +:%X R:%X B:%X G:%X ?:%X F:%X", R2u(0x1D5D), R2u(0x1D71), R2u(0x1D61), R2u(0x1D63), R2u(0x1D65), R2u(0x1D69), R2u(0x1D67)))
end

--*****************************************************************************
local function RAMScan()
--*****************************************************************************
--While it's nice having RAM Watch, I like my scripty ideas.
--Decimal only. The list should be at the top of this script.

    for i= 1, #FauxRAMWatch do
        local Watch= FauxRAMWatch[i]
        local Value= Watch.f(Watch.addr)
        PxTxRight(255,8*(i-1),Value,Watch.clr)
    end
end

--*****************************************************************************
local function HandleRNG(x,y)
--*****************************************************************************
    while #RNG_Calls ~= 0 do
        local C= table.remove(RNG_Calls,1)
        Tx(x,y,string.format("%06X:%02X %s %s",C.caller, C.A, C.val, C.name),C.clr)
        y= y+7
    end
end

--*****************************************************************************
local function BasicHUD()
--*****************************************************************************

    local CamX, CamY= FetchCam()

    --Screen stuff
    PlayerProjectiles(CamX,CamY)
    Hitboxes(CamX, CamY)

    --Have this on the bottom so boxes don't mess with our HUD.
    PlayerHUD()
    RAMScan()
    HandleRNG(  2, 30)

    --Boss HP quickie
    Tx(64,200,math.ceil(R2u(0x1F49)/17),0xFFFF8000)
end

--emu.setislagged
--tastudio.setlag
--emu.framecount

local OldEmuFrame, OldGameFrame= -2, -2
--*****************************************************************************
local function LagTag()
--*****************************************************************************
    local EmuFrame, GameFrame= emu.framecount(), R2u(0x0024)

    if EmuFrame == OldEmuFrame+1 then
        if GameFrame ~= OldGameFrame+1 then
            emu.setislagged()
            tastudio.setlag(EmuFrame,true)
        end
    end

    OldEmuFrame, OldGameFrame= EmuFrame, GameFrame
end

--*****************************************************************************
while true do
--*****************************************************************************
    local GimmeDaKeys= input.get()
    AdjustScaling(GimmeDaKeys)
    BasicHUD()
    LagTag()

	emu.frameadvance();
end