User File #26173542235309721

Upload All User Files

#26173542235309721 - Bugs Bunny - Lost in Time Lua PSXHawk v5

Bugs Bunny - Lost in Time.lua
1005 downloads
Uploaded 10/14/2015 5:09 PM by Warepire (see all 18)
Added camera angle, and a conversion for Bugs' angle to the same notation. Fixed the hud updating to work properly and not leave garbage data when Bugs isn't moving.
Please excuse the hud-function, adding the internal camera angle support made quite a mess.
-- TODO:
-- Extend waypoints to use a queue-style list.
-- Make the script move Bugs using camera angle, Bugs angle, and waypoints.
-- 0x0069408 / 2 bytes s = Camera turning speed when manually turning camera
-- Changes angle by 11.25 degrees, in BAMS notation (32)
-- 0x0069416 / 2 bytes s = ??? Seems to also be camera, but sloppy?
-- 0x00693D4 / 4 bytes h = Bitfield, Bugs state

--position, speed, direction
Xpos = 0
XposOld = 0
Ypos = 0
YposOld = 0
Zpos = 0
ZposOld = 0
Xspeed = 0
Yspeed = 0
Zspeed = 0
--not really a need to look at Y-speed as it seems to be independent of XZ speed,
--might still be useful for jumping.
XZspeed = 0
XYZspeed = 0
XZdirection = 0

--timer related things
local inlvlObjTimer

--hud update logic things
local update = true
local counter30FPS = 0
local counter30FPSOld = 0

--waypoint things
local waypointX = nil
local waypointZ = nil
local displayWaypoint = false

-- Conversion as per: http://electronicstechnician.tpub.com/14091/css/14091_316.htm
function convertDegreesToBAMS(deg)
	-- no conversion necessary
	if deg == 360 or deg == 0 then
		return 0
	end
	-- need to convert
	local returnValue = 0
	if deg >= 180 then
		returnValue = returnValue + 0x800
		deg = deg - 180
	end
	if deg >= 90 then
		returnValue = returnValue + 0x400
		deg = deg - 90
	end
	if deg >= 45 then
		returnValue = returnValue + 0x200
		deg = deg - 45
	end
	if deg >= 22.5 then
		returnValue = returnValue + 0x100
		deg = deg - 22.5
	end
	if deg >= 11.25 then
		returnValue = returnValue + 0x80
		deg = deg - 11.25
	end
	if deg >= 5.625 then
		returnValue = returnValue + 0x40
		deg = deg - 5.625
	end
	if deg >= 2.81 then
		returnValue = returnValue + 0x20
		deg = deg - 2.81
	end
	if deg >= 1.4 then
		returnValue = returnValue + 0x10
		deg = deg - 1.4
	end
	if deg >= 0.703 then
		returnValue = returnValue + 0x8
		deg = deg - 0.703
	end
	if deg >= 0.351 then
		returnValue = returnValue + 0x4
		deg = deg - 0.351
	end
	if deg >= 0.175 then
		returnValue = returnValue + 0x2
		deg = deg - 0.175
	end
	if deg >= 0.088 then
		returnValue = returnValue + 0x1
		deg = deg - 0.088
	end
	-- Any remaining fragments of 'deg' are too small to count,
	-- accept the accuracy loss.
	return returnValue
end

local function hud()
	--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
	Xpos = memory.read_s32_le(0x00069DC0)
	Ypos = memory.read_s32_le(0x00069DC4)
	Zpos = memory.read_s32_le(0x00069DC8)

	--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.
	--The updated flag solves an issue caused by using emu.yield()
	counter30FPS = memory.read_u32_le(0x00076010)
	local tempXspeed = Xpos - XposOld
	local tempYspeed = Ypos - YposOld
	local tempZspeed = Zpos - ZposOld
	-- Make sure the direction is in 0-360 degree range
	local tempXZdirection = math.atan2(tempXspeed, tempZspeed)
	if tempXZdirection < 0 then
		tempXZdirection = tempXZdirection + (math.pi * 2)
	end
	tempXZdirection = math.deg(tempXZdirection)
	if counter30FPS ~= counter30FPSOld and update == true then
		Xspeed = math.abs(tempXspeed)
		Yspeed = math.abs(tempYspeed)
		Zspeed = math.abs(tempZspeed)
		XZdirection = tempXZdirection
		XposOld = Xpos
		YposOld = Ypos
		ZposOld = Zpos
		update = false
	elseif counter30FPS == counter30FPSOld then
		update = true
	end
	counter30FPSOld = counter30FPS

	-- Read out the camera angle, and clamp it to 12-bit BAMS
	local cameraAngleBAMS = memory.read_s16_le(0x00069402)
	if cameraAngleBAMS < 0 then
		cameraAngleBAMS = cameraAngleBAMS + 4096
	end
	if cameraAngleBAMS == 4096 then
		cameraAngleBAMS = 0
	end
	local xzAngleBAMS = convertDegreesToBAMS(XZdirection)

	XZspeed = math.sqrt(Xspeed*Xspeed + Zspeed*Zspeed)
	XYZspeed = math.sqrt(Xspeed*Xspeed + Yspeed*Yspeed + Zspeed*Zspeed)

	inlvlObjTimer = memory.read_u16_le(0x00069526)

	if displayWaypoint == true and (waypointX ~= nil and waypointZ ~= nil) then
		local Xdistance = waypointX - Xpos
		local Zdistance = waypointZ - Zpos
		local waypointAngle = math.atan2(Xdistance, Zdistance)
		if waypointAngle < 0 then
			waypointAngle = waypointAngle + (math.pi * 2)
		end
		waypointAngle = math.deg(waypointAngle)
		gui.text(20, 200, string.format("%3d", waypointAngle))
	end

	gui.text(20,60, string.format("Pos: (%6d,%6d,%6d)", Xpos, Ypos, Zpos))
	gui.text(20,80, string.format("Speed XZ: %3d    Angle: %3d\n",XZspeed,XZdirection))
	gui.text(20,100, string.format("Speed 3D: %3d", XYZspeed))
	gui.text(20,120, string.format("Speed Y:  %3d", Yspeed))
	gui.text(20,140, string.format("Obj timer: %3d", inlvlObjTimer))

	gui.text(20,300, string.format("Camera BAMS: %4d", cameraAngleBAMS))
	gui.text(20,320, string.format("Bugs BAMS:   %4d", xzAngleBAMS))
end

-- Hack to not trigger on every frame the key is down
local jKeyDown
local middleMouseDown

local function toggle_waypoints()
	local keyboardInput = input.get()
	local mouseInput = input.getmouse()
	-- jKeyDown is nil when not set, middleMouseDown is false... All for the sake of consistency...?
	if (keyboardInput["J"] == true or mouseInput["Middle"] == true) and (jKeyDown == nil and middleMouseDown == false) then
		if displayWaypoint == true then
			displayWaypoint = false
		else
			displayWaypoint = true
		end
	end
	jKeyDown = keyboardInput["J"]
	middleMouseDown = mouseInput["Middle"]
end

function tablelength(T)
	local count = 0
	for _ in pairs(T) do count = count + 1 end
	return count
end

local form = nil
local textlabel = nil
local textbox = nil
local poslabel = nil
local posbox = nil
local okButton = nil
local copyButton = nil

local function OK_callback()
	local text = forms.gettext(textbox)
	if text ~= nil and string.len(text) ~= 0 then
		local splitText = {}
		local i = 0
		for v in string.gmatch(text, "[+-]?%d+") do
			splitText[i] = v
			i = i + 1
		end
		if tablelength(splitText) == 2 then
			waypointX = tonumber(splitText[0], 10)
			waypointZ = tonumber(splitText[1], 10)
		end
	else
		waypointX = nil
		waypointZ = nil
	end
end

local function Copy_callback()
	forms.settext(textbox, string.format("%d/%d", Xpos, Zpos))
	OK_callback()
end

form = forms.newform(200, 130, "Set X/Z waypoint")
textlabel = forms.label(form, "Wayp X/Z:", 0, 10, 60, 15)
textbox = forms.textbox(form, nil, 120, 20, nil, 60, 5)
okButton = forms.button(form, "Set", OK_callback, 10, 40, 170, 25)
copyButton = forms.button(form, "Copy Pos && Set", Copy_callback, 10, 70, 170, 25)

local function destroy_waypoint_window()
	forms.destroyall()
end

-- Not supported in PSXHawk yet:
--event.onexit(destroy_waypoint_window, "Exit handler")

while true do
	hud();
	toggle_waypoints();
	emu.yield();
end