-- Addresses from https://tasvideos.org/GameResources/DS/MarioKartDS
-- Script v1.1 by lexikiq
-- v1.1: Re-added 'realSpeed' variable
-- v1.0: Initial port
local verUSA = true -- set to false for EUR
local xPrev = 0
local zPrev = 0
local function padNum(num, digits, sign)
local str = tostring(math.abs(num))
while #str < digits do
str = "0" .. str
end
if sign then
if num > 0 then
str = "+" .. str
elseif num < 0 then
str = "-" .. str
else
str = " " .. str
end
end
return str
end
local function time(secs)
local sec = padNum(math.floor(secs) % 60, 2)
local min = math.floor(secs / 60)
local milli = padNum(math.floor(secs * 1000) % 1000, 3)
return min .. ":" .. sec .. ":" .. milli
end
local function text(x, y, str)
gui.text(x, y, str)
return y + 14
end
local function formatFloat(num)
local fmt = string.format("%.4f", num)
if num > 0 then
fmt = "+" .. fmt
elseif num == 0 then
fmt = " " .. fmt
end
return fmt
end
while true do
-- Set memory domain
memory.usememorydomain("ARM9 System Bus")
-- Set starting pointers
local playerDataPointer = 0x217ACF8
local checkpointDataPointer = 0x21661B0
if not verUSA then playerDataPointer = 0x217D028 end
playerData = memory.read_u32_le(playerDataPointer)
checkpointData = memory.read_u32_le(checkpointDataPointer)
-- Read values
local x = memory.read_s32_le(playerData + 0x80)
local y = memory.read_s32_le(playerData + 0x84)
local z = memory.read_s32_le(playerData + 0x88)
local velocity = memory.read_s32_le(playerData + 0x2A8) -- this is velocity, not speed, because it's signed
local xSpeed = math.abs(xPrev - x)
local zSpeed = math.abs(zPrev - z)
local realSpeed = math.sqrt(xSpeed * xSpeed + zSpeed * zSpeed)
realSpeed = math.floor(realSpeed * 10) / 10
xPrev = x
zPrev = z
local maxVelocity = memory.read_s32_le(playerData + 0xD0)
local boostTimer = memory.read_s32_le(playerData + 0x238) -- measured in frames
local mtBoostTimer = memory.read_s32_le(playerData + 0x23C) -- measured in frames
local mtChargeTimer = memory.read_s32_le(playerData + 0x30C) -- measured in frames
local facingAngle = memory.read_s16_le(playerData + 0x236)
local driftAngle = memory.read_s16_le(playerData + 0x388)
local verticalAngle = memory.read_s16_le(playerData + 0x234)
local directionSine = memory.read_s32_le(playerData + 0x68)
local directionCosine = memory.read_s32_le(playerData + 0x70)
local direction = math.sqrt((directionSine / 4096) ^ 2 + (directionCosine / 4096) ^ 2)
local targetSine = memory.read_s32_le(playerData + 0x50)
local targetCosine = memory.read_s32_le(playerData + 0x58)
local target = math.sqrt((targetSine / 4096) ^ 2 + (targetCosine / 4096) ^ 2)
local turningLoss = memory.read_s32_le(playerData + 0x2D4)
local grip = memory.read_s32_le(playerData + 0x240)
local grounded = "In Air"
if memory.read_u8(playerData + 0x3DD) == 0 then grounded = "On Ground" end
local spawnpoint = memory.read_u8(playerData + 0x3C4)
if spawnpoint == 255 then spawnpoint = "N/A" end
local lapTime = memory.read_u32_le(checkpointData + 0xD80) * 1.0 / 60 - 0.05 -- weird
local checkpoint = memory.read_u8(checkpointData + 0xDAE)
local keyCheckpoint = memory.read_u8(checkpointData + 0xDB0)
local ghostCheckpoint = memory.read_u8(checkpointData + 0xE3A)
local ghostKeyCheckpoint = memory.read_u8(checkpointData + 0xE3C)
-- Prepare to draw values
local sx, sy = 2, 2
local layout = nds.getscreenlayout()
if layout == "Vertical" then
sy = sy + ((192 + nds.getscreengap()) * client.getwindowsize())
elseif layout == "Horizontal" then
sx = sx + (256 * client.getwindowsize())
end
-- Draw values
gui.text(sx + (256 * client.getwindowsize()) - 196, sy + (20 * client.getwindowsize()), "Lap Time: " .. time(lapTime))
sy = text(sx, sy, "X: " .. padNum(x, 8, true))
sy = text(sx, sy, "Z: " .. padNum(z, 8, true))
sy = text(sx, sy, "Y: " .. padNum(y, 8, true))
sy = text(sx, sy, "Velocity: " .. padNum(velocity, 5, true)) -- TODO: /32767??
sy = text(sx, sy, "Real Spd: " .. padNum(realSpeed, 7, true))
sy = text(sx, sy, "Max Vel.: " .. padNum(maxVelocity, 5, true)) -- TODO: /32767??
sy = text(sx, sy, "Boost Timer: " .. tostring(boostTimer) .. " (MT: " .. tostring(mtBoostTimer) .. ")")
sy = text(sx, sy, "MT Timer: " .. tostring(mtChargeTimer))
sy = text(sx, sy, "Turning Loss: " .. string.format("%.4f", math.abs(turningLoss/4096)))
sy = text(sx, sy, "Facing Angle: " .. tostring(facingAngle)) -- TODO: /32767??
sy = text(sx, sy, "Drift Angle: " .. tostring(driftAngle)) -- TODO: /11000?
--sy = text(sx, sy, "Total Angle:" .. tostring(facingAngle + driftAngle)) -- nonsense
sy = text(sx, sy, "Vertical Angle: " .. tostring(verticalAngle))
--sy = text(sx, sy, "Direction: " .. formatFloat(direction)) -- this makes no sense
--sy = text(sx, sy, "TargetDir: " .. formatFloat(target)) -- this one neither
sy = text(sx, sy, "Grip: " .. string.format("%.4f", grip/4096))
sy = text(sx, sy, grounded)
sy = text(sx, sy, "Checkpoint: " .. tostring(checkpoint) .. " (Key: " .. tostring(keyCheckpoint) .. ")")
sy = text(sx, sy, "Spawnpoint: " .. tostring(spawnpoint))
--sy = text(sx, sy, "Ghost Checkpoint: " .. tostring(ghostCheckpoint) .. " (Key: " .. tostring(ghostKeyCheckpoint) .. ")") -- meh
-- Finish
emu.frameadvance()
end