User File #32501723906487738

Upload All User Files

#32501723906487738 - Bugs Bunny Lost in Time Lua

bblit.lua
947 downloads
Uploaded 7/25/2016 4:57 PM by Warepire (see all 18)
Now with Waypoint lists, J adds waypoint, K drops it, L autowalks. The list works like a stack.
Also handles controller dead zones.
--local module definitions first
local bblitGUI = {}
do
	local areaX, areaY
	
	function bblitGUI.SetArea(x, y)
		areaX = x
		areaY = y
	end

	function bblitGUI.DrawBool(token, value)
		gui.text(areaX, areaY, string.format("%s: %s", token, tostring(value)))
		areaY = areaY + 20
	end		

	function bblitGUI.DrawValue(token, value)
		gui.text(areaX, areaY, string.format("%s: %d", token, value))
		areaY = areaY + 20
	end
	
	function bblitGUI.DrawVector2(token, value1, value2)
		gui.text(areaX, areaY, string.format("%s: (%3d,%3d)", token, value1, value2))
		areaY = areaY + 20
	end

	function bblitGUI.DrawVector3(token, value1, value2, value3)
		gui.text(areaX, areaY, string.format("%s: (%6d,%6d,%6d)", token, value1, value2, value3))
		areaY = areaY + 20
	end

	function bblitGUI.DrawLStick(x, y)
		gui.drawEllipse(areaX, areaY, 256, 256, "white")
		gui.drawLine(areaX + 128, areaY + 128, areaX + x, areaY + y, "red")
		gui.drawEllipse(areaX + x - 2, areaY + y - 2, 4, 4, "red", "red")
		areaY = areaY + 260
	end
end

local bblitAngle = {}
do
	-- Conversions as per: http://electronicstechnician.tpub.com/14091/css/14091_316.htm
	function bblitAngle.DegreesToBAMS(degrees)
		-- no conversion necessary
		if degrees == 360 or degrees == 0 then
			return 0
		end
		-- need to convert
		local returnValue = 0
		
		local BAMSbits = 0x800
		local angularDegrees = 180
		
		while BAMSbits > 0 do
			if degrees >= angularDegrees then
				returnValue = returnValue + BAMSbits
				degrees = degrees - angularDegrees
			end
			
			BAMSbits = math.floor(BAMSbits / 2)
			angularDegrees = angularDegrees / 2
		end
		
		return returnValue
	end

	function bblitAngle.BAMStoDegrees(bams)
		if bams == 4096 or bams == 0 then
			return 0
		end

		local returnValue = 0

		local BAMSbits = 0x800
		local angularDegrees = 180

		while bams > 0 do
			if bams >= BAMSbits then
				returnValue = returnValue + angularDegrees
				bams = bams - BAMSbits
			end

			BAMSbits = math.floor(BAMSbits / 2)
			angularDegrees = angularDegrees / 2

		end

		return returnValue
	end
	
	function bblitAngle.ClampBAMS(bams)
		if bams < 0 then
			bams = bams + 4096
		end
		if bams >= 4096 then
			bams = bams - 4096
		end
		return bams
	end
	
	function bblitAngle.Vector2ToDegrees(x, y)
		local angle = math.atan2(x, y)
		if angle < 0 then
			angle = angle + (math.pi * 2)
		end
		angle = math.deg(angle)
		return angle
	end
end

local bblitKeyboard = {}
do
	local keysToCheck = {"J", "K", "L"}
	local keys = {}
	local keysPrev = {}
	local keysDown = {}

	function bblitKeyboard.GetInput()
		keys = input.get()
		
		for i, v in ipairs(keysToCheck) do
			
			if keys[v] == true and keysPrev[v] ~= true then
				keysDown[v] = true
			else
				keysDown[v] = false
			end
			
		end
		
		keysPrev = keys
	end
	
	function bblitKeyboard.GetKey(token)
		return keys[token] == true
	end
	
	function bblitKeyboard.GetKeyDown(token)
		return keysDown[token] == true
	end
end

local bblitJoypad = {}
do
	function bblitJoypad.SetAnalog(x, y)
		local analogInput = {}
		analogInput["LStick X"] = x
		analogInput["LStick Y"] = y
		joypad.setanalog(analogInput, 1)
	end
end

local bblitAutowalk = {}
do
	local active = false
	local waypoints = {}
	local currentWaypoint
	local waypointAngle, diffAngle = 0, 0
	local input = { X = 128, Y = 128 }
	local gui
	local angle
	local joypad

	local function CopyWaypoint(wp)
		copy = {}
		for key, value in pairs(wp) do
			copy[key] = value
		end
		return copy
	end

	local function CountWaypoints()
		return table.getn(waypoints)
	end

	function bblitAutowalk.Init(modGui, modAngle, modJoypad)
		gui = modGui
		angle = modAngle
		joypad = modJoypad
	end

	function bblitAutowalk.Enable(yes)
		if yes and CountWaypoints() == 0 then
			return
		end
		if active == false and yes then
			currentWaypoint = 1
		end
		active = yes
		if active == false then
			input.X = 128
			input.Y = 128
			joypad.SetAnalog(input.X, input.Y)
		end
	end

	function bblitAutowalk.Enabled()
		return active
	end

	function bblitAutowalk.PushWaypoint(wp)
		table.insert(waypoints, CopyWaypoint(wp))
		if CountWaypoints() == 1 then
			currentWaypoint = 1
		end
	end

	function bblitAutowalk.PopWaypoint()
		if CountWaypoints() > 0 then
			table.remove(waypoints)
		end
		if currentWaypoint > CountWaypoints() then
			currentWaypoint = CountWaypoints()
		end
	end

	function bblitAutowalk.Autowalk(pos, camAngle)
		if not active or CountWaypoints() < 1 then
			return
		end

		local Xdistance = pos.X - waypoints[currentWaypoint].X
		local Zdistance = pos.Z - waypoints[currentWaypoint].Z

		waypointAngle = angle.ClampBAMS(angle.DegreesToBAMS(angle.Vector2ToDegrees(Xdistance, Zdistance)))
		diffAngle = angle.ClampBAMS(camAngle - waypointAngle)
		local diffAngleRadians = math.rad(angle.BAMStoDegrees(diffAngle))
		-- Account for controller dead zones
		if diffAngleRadians < 0.406 and diffAngleRadians > 0.1 then
			diffAngleRadians = 0.406
		elseif diffAngleRadians > 5.870 and diffAngleRadians < 6.18 then
			diffAngleRadians = 5.870
		elseif diffAngleRadians < 0.1 or diffAngleRadians > 6.18 then
			diffAngleRadians = 0
		end
		-- Add 0.5 to round the float correctly as we go to integers
		-- Interpolate the X/Y from the angle as per: http://mathforum.org/sarah/hamilton/ham.1side.1angle.html
		-- X/Y are inverted (X = Y and Y = X) compared to the angle, they're also inverted along their own axis (X 255 = X 0)
		input.Y = math.abs(math.max(0, math.min(math.floor(((math.cos(diffAngleRadians) * 128) + 128) + 0.5), 255)) - 255)
		input.X = math.abs(math.max(0, math.min(math.floor(((math.sin(diffAngleRadians) * 128) + 128) + 0.5), 255)) - 255)
	
		if math.sqrt(Xdistance*Xdistance + Zdistance*Zdistance) < 150 then
			if CountWaypoints() > 0 then
				currentWaypoint = currentWaypoint + 1
			end
			if currentWaypoint > CountWaypoints() then
				currentWaypoint = CountWaypoints()
				input.X = 128
				input.Y = 128
			end
		end

		joypad.SetAnalog(input.X, input.Y)
	end

	-- HACK: Should not need pos as a parameter?
	function bblitAutowalk.DrawHUD(pos)
		if CountWaypoints() > 0 then
			for i, wp in ipairs(waypoints) do
				local template = "Waypoint %d"
				if i == currentWaypoint then
					template = "* " .. template
				end
				gui.DrawVector3(string.format(template, i), wp.X, wp.Y, wp.Z)
			end
			gui.DrawValue(  "Waypoint BAM angle", waypointAngle)
			gui.DrawValue(  "Diff BAM angle", diffAngle)

			if bblitAutowalk.Enabled() then
				local Xdistance = pos.X - waypoints[currentWaypoint].X
				local Zdistance = pos.Z - waypoints[currentWaypoint].Z
				gui.DrawValue(  "Autowalk distance", math.sqrt(Xdistance*Xdistance + Zdistance*Zdistance))
			end
		end
		gui.SetArea(400, 20)
		gui.DrawLStick(input.X, input.Y)
		gui.DrawVector2("L Stick", input.X, input.Y)
		gui.DrawBool("Autowalk", bblitAutowalk.Enabled())
	end
end

local gui = bblitGUI
local angle = bblitAngle
local keyboard = bblitKeyboard
local joypad = bblitJoypad
local autowalk = bblitAutowalk

-----------------------
-----------------------
------modules end------
-----------------------
-----------------------

local function CheckModule(name,mod)
	if mod ~= nil and mod ~= true then
		console.log("Module " .. name .. " successfully loaded.")
	else
		console.log("Module " .. name .. " failed to load! (value = " .. mod .. ")")
	end
end

do
	--Due to that the game internally runs at 30 fps despite rendering at 60 fps
	--the speed values cannot be updated each frame.
	--Apply some simpler logic to only update this once per 2 frames by using an
	--address that increments at 30 fps.

	local Counter30FPS = 0
	local Counter30FPSOld = 0
	
	function CheckDoUpdate()
		Counter30FPS = memory.read_u32_le(0x00076010)
		if Counter30FPS == Counter30FPSOld then
			return false
		end
		Counter30FPSOld = Counter30FPS
		return true
	end
end

local pos = { X = 0, Y = 0, Z = 0 }
local posOld = { X = 0, Y = 0, Z = 0 }
local speed = { X = 0, Y = 0, Z = 0, XZ = 0, XYZ = 0 }
local camAngle, bugsAngle = 0, 0

local function Update()

	if not CheckDoUpdate() then return end
	
	--Actual speed vector addresses:
	--addr 00069D30 X Speed [signed dword]
	--addr 00069D34 Y Speed(?)  [signed dword]
	--addr 00069D38 Z Speed [signed dword]
	--This is not reliable as it shows the speed Bugs want to travel at,
	--not the actual speed that he moves at.
	--Use position as a source for speed instead

	pos.X = memory.read_s32_le(0x00069DC0)
	pos.Y = memory.read_s32_le(0x00069DC4)
	pos.Z = memory.read_s32_le(0x00069DC8)
	
	speed.X = pos.X - posOld.X
	speed.Y = pos.Y - posOld.Y
	speed.Z = pos.Z - posOld.Z
	
	speed.XZ = math.sqrt(speed.X*speed.X + speed.Z*speed.Z)
	speed.XYZ = math.sqrt(speed.X*speed.X + speed.Y*speed.Y + speed.Z*speed.Z)
	
	camAngle = angle.ClampBAMS(memory.read_s16_le(0x00069402))
	--Seems to change every stage?
	--bugsAngle = angle.ClampBAMS(memory.read_s16_le(0x000B8AAA))
	--HACK:
	if speed.XZ > 10 then
		bugsAngle = angle.DegreesToBAMS(angle.Vector2ToDegrees(speed.X, speed.Z))
	end

	autowalk.Autowalk(pos, camAngle)

	posOld.X = pos.X
	posOld.Y = pos.Y
	posOld.Z = pos.Z
end

local function main()

	CheckModule("GUI", gui)
	CheckModule("Angle", angle)
	CheckModule("Keyboard", keyboard)
	CheckModule("Joypad", joypad)
	CheckModule("Autowalk", autowalk)
	
	autowalk.Init(gui, angle, joypad)

	client.SetGameExtraPadding(600, 0, 0, 200)

	joypad.SetAnalog(128, 128)

	while true do
		keyboard.GetInput()
		Update()
		
		if keyboard.GetKeyDown("J") == true then
			autowalk.PushWaypoint(pos)
		end
		if keyboard.GetKeyDown("K") == true then
			autowalk.PopWaypoint()
		end
		if keyboard.GetKeyDown("L") == true then
			if autowalk.Enabled() then
				autowalk.Enable(false)
			else
				autowalk.Enable(true)
			end
		end
	
		gui.SetArea(20, 56)
		gui.DrawVector3("Position ", pos.X, pos.Y, pos.Z)
		gui.DrawVector3("Speed    ", speed.X, speed.Y, speed.Z)
		gui.DrawValue(  "XZ Speed ", speed.XZ)
		gui.DrawValue(  "XYZ Speed", speed.XYZ)
		gui.DrawValue(  "Camera BAM Angle", camAngle)
		gui.DrawValue(  "Bugs BAM Angle  ", bugsAngle)

		autowalk.DrawHUD(pos)

		emu.frameadvance()
	end
end

main()