-- Configuration
local config = {
display_sprite_hitbox = true,
display_sprite_info = false,
}
local colors = {
"red", -- Donkey Kong
"magenta" -- Diddy
}
-- Some utility functions
local u8 = function(address) return memory.readbyte("WRAM", address) end
local s8 = function(address) return memory.readsbyte("WRAM", address) end
local u16 = function(address) return memory.readword("WRAM", address) end
local s16 = function(address) return memory.readsword("WRAM", address) end
local function rectangle(x, y, w, h, ...)
-- Draw from top-left to bottom-right
if w < 0 then
x, w = x + w, -w
end
if h < 0 then
y, h = y + h, -h
end
x, y, w, h = 2*x, 2*y, 2*w + 2, 2*h + 2
gui.rectangle(x, y, w, h, 2, ...)
end
-- Definitions
local WRAM = {
game_mode_ptr = 0x1c,
camera_vertical_offset = 0x4a,
-- 0xbe --> camera distance from level origin
-- 0xc0 --> camera y mirror
xcam_effective = 0x1a62,
sprite_type = 0x0d45,
xspeed = 0xe8b,
xpos_camera = 0x88b,
ypos_camera = 0x895,
left_border_pos = 0x1b23,
xpos_offset = 0x0b19,
ypos_offset = 0x0bc1,
}
-- This table'll be used differently in the future
-- some sprites have completely different hitboxes depending on context
local odd_hitbox = {
-- [sprite type] = offset,
[0x15] = 0x08,
[0x16] = 0x08,
[0x23] = false, -- 0x0d,
}
local game = {}
local sprite = {}
-- Game functions
local function load_game_info()
game.game_mode_ptr = u16(WRAM.game_mode_ptr)
game.is_level_mode = game.game_mode_ptr == 0x80a9 or game.game_mode_ptr == 0xa98e
game.camera_vertical_offset = u16(WRAM.camera_vertical_offset)
game.xpos_camera = u16(WRAM.xpos_camera)
game.ypos_camera = u16(WRAM.ypos_camera)
end
-- Debugging...
--[==[
-- get clipping
local function regexec(address, fn) memory.registerexec("BUS", address, fn) end
---[[
memory.registerexec("BUS", 0xbba533, function()
print(string.format("Clipping(%d) x: %d -> %d / y: %d -> %d",
memory.getregister("x")//2, u16(0xa6), u16(0xaa), u16(0xa8), u16(0xac) ))
end)
--]]
regexec(0xbfcf14, function() print"bfcf14" end)
regexec(0xbba4d5, function() print"bba4d5" end)
regexec(0xbba4c8, function() print"bba4c8" end)
---[[
memory.registerexec("BUS", 0xbba600, function()
print(string.format("%d: left: %d %d / right: %d %d / top: %d %d / down: %d %d",
u16(0xb6), u16(0xb2), u16(0xa6), u16(0xaa), u16(0xae), u16(0xb0), u16(0xac), u16(0xa8), u16(0xb4) ))
end)
--]]
--]==]
-- end of debugging
local function draw_hitbox(xscreen, yscreen, slot, hitbox_offset, color)
-- flip
local do_flip = u16(0x0c69 + 2*slot)
local xflip = bit.test(do_flip, 14) and -1 or 1
local yflip = bit.test(do_flip, 15) and -1 or 1 -- not sure about that
local xoff = xflip * memory.readsword("BUS", hitbox_offset + 0)
local yoff = yflip * memory.readsword("BUS", hitbox_offset + 2)
local width = xflip * memory.readsword("BUS", hitbox_offset + 4)
local height = yflip * memory.readsword("BUS", hitbox_offset + 6)
-- draw
rectangle(xscreen + xoff, yscreen + yoff, width, height, color)
end
-- bba4d5 , bba4c8: get sprite clipping values (normal / special)
-- bba594: check for collision
-- b2 a6/ aa ae / b0 ac / a8 b4
local function sprite_hitbox(slot)
local spr_type = u16(WRAM.sprite_type + 2*slot)
local index = math.floor(u16(0xd11 + 2*slot)/2)
-- position
local xpos = u16(WRAM.xpos_offset + 2*slot)
local ypos = u16(WRAM.ypos_offset + 2*slot)
local xscreen = xpos - game.xpos_camera
local yscreen = game.camera_vertical_offset - ypos - game.ypos_camera
local color
if slot <= 2 then
color = colors[slot]
elseif odd_hitbox[spr_type] then
color = 0xff00
else
color = "yellow"
end
local hitbox_offset
if odd_hitbox[spr_type] then
hitbox_offset = 8*odd_hitbox[spr_type] + 0xa428
else
hitbox_offset = memory.readword("BUS", 0xbb8000 + index)
end
hitbox_offset = hitbox_offset + 0xbb0000
draw_hitbox(xscreen, yscreen, slot, hitbox_offset, color)
end
local function draw_sprite_info()
local xpos_camera = game.xpos_camera
local ypos_camera = game.ypos_camera
local camera_vertical_offset = game.camera_vertical_offset
-- gui.text(0, 0, string.format("Cam: %d, %d", xpos_camera, ypos_camera))
for slot = 1, 25 do
local spr_type = u16(WRAM.sprite_type + 2*slot)
local is_normal_sprite = slot <= 14
if spr_type ~= 0 then
local xpos = u16(WRAM.xpos_offset + 2*slot)
local ypos = u16(WRAM.ypos_offset + 2*slot)
local xscreen = xpos - xpos_camera
local yscreen = camera_vertical_offset - ypos - ypos_camera
local xspeed = s8(WRAM.xspeed + slot) -- todo
-- draw position pixel
if config.display_sprite_info then
gui.solidrectangle(2*xscreen, 2*yscreen, 2, 2, 0x40ffffff)
gui.text(2*xscreen, 2*yscreen, "#" .. slot, 0x80ffffff, -1, 0x80000000)
local mytext = string.format("#%d[%.2x] (%d, %d)", slot, spr_type, xpos, ypos)
gui.text(512 - 8 * #mytext, 16*slot, mytext, is_normal_sprite and 0xffffff or 0x808080, -1, 0)
end
-- draw hitbox
if is_normal_sprite then
sprite_hitbox(slot)
end
end
end
end
-- Callbacks and startup
function on_paint()
load_game_info()
if game.is_level_mode then
draw_sprite_info()
else
gui.text(0, 0, "Outside level", 0xffffff, -1, 0)
end
end
gui.repaint()
print("DKC Lua script has started.")