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()