User File #638489656119329905

Upload All User Files

#638489656119329905 - BizHawk RAM Watch v1.1

Mario Kart DS.lua
91 downloads
Uploaded 4/17/2024 3:46 PM by lexikiq (see all 15)
Update to UserFiles/Info/638275741882298253 which re-adds the "real speed" variable, which is a simple arithmetic operation displaying your effective speed across the previous frame. It relies on data from the previous frame and thus will not display correctly after loading a save state.
-- 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