local isNyma = false
local isNymaCheck = joypad.get()
if isNymaCheck["P1 Left Stick Left / Right"] ~= nil then
isNyma = true
console.log("Nyma core detected")
end
-- 6AAD3 control variable for in-stage state
-- 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)
local drawXScale = 1
local drawYScale = 1
if isNyma then
drawXScale = 0.75
drawYScale = 0.50
end
gui.drawEllipse(areaX * drawXScale, areaY * drawYScale, 256 * drawXScale, 256 * drawYScale, "white")
gui.drawLine((areaX + 128) * drawXScale, (areaY + 128) * drawYScale, (areaX + x) * drawXScale, (areaY + y) * drawYScale, "red")
gui.drawEllipse((areaX + x - 2) * drawXScale, (areaY + y - 2) * drawYScale, 4 * drawXScale, 4 * drawYScale, "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 = {}
if isNyma then
analogInput["Left Stick Left / Right"] = x * 256
analogInput["Left Stick Up / Down"] = y * 256
else
analogInput["LStick X"] = x
analogInput["LStick Y"] = y
end
joypad.setanalog(analogInput, 1)
end
end
local bblitAutowalk = {}
do
local active = false
local waypoints = {}
local waypointAngle, diffAngle = 0, 0
local input = { X = 128, Y = 128 }
local gui
local angle
local joypad
local Xdistance
local Zdistance
function bblitAutowalk.CopyWaypoint(wp)
copy = {}
for key, value in pairs(wp) do
copy[key] = value
end
return copy
end
function bblitAutowalk.CountWaypoints()
return table.getn(waypoints)
end
function bblitAutowalk.GetFirstWaypoint()
return waypoints[1]
end
function bblitAutowalk.GetDifferenceAngle()
return diffAngle
end
function bblitAutowalk.GetWaypointAngle()
return waypointAngle
end
function bblitAutowalk.Init(modGui, modAngle, modJoypad)
gui = modGui
angle = modAngle
joypad = modJoypad
end
function bblitAutowalk.Enable(yes)
if yes and bblitAutowalk.CountWaypoints() == 0 then
return
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, bblitAutowalk.CopyWaypoint(wp))
end
function bblitAutowalk.PopWaypoint()
if bblitAutowalk.CountWaypoints() > 0 then
table.remove(waypoints)
end
end
function bblitAutowalk.Autowalk(pos, camAngle)
if not active or bblitAutowalk.CountWaypoints() < 1 then
return
end
Xdistance = pos.X - waypoints[1].X
Zdistance = pos.Z - waypoints[1].Z
waypointAngle = angle.ClampBAMS(angle.DegreesToBAMS(angle.Vector2ToDegrees(Xdistance, Zdistance)))
diffAngle = angle.ClampBAMS(camAngle - waypointAngle)
local diffAngleRadians = math.rad(angle.BAMStoDegrees(diffAngle))
--dead zone
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 bblitAutowalk.CountWaypoints() > 0 then
-- Special, remove from front!
--table.remove(waypoints, 1)
end
if bblitAutowalk.CountWaypoints() == 0 then
--bblitAutowalk.Enable(false)
input.X = 128
input.Y = 128
return
end
end
joypad.SetAnalog(input.X, input.Y)
waypointAngle = (waypointAngle + 2048) % 4096
end
-- HACK: Should not need pos as a parameter?
function bblitAutowalk.DrawHUD(pos)
gui.SetArea(0, 200)
gui.DrawLStick(input.X, input.Y)
gui.DrawVector2("L Stick", input.X, input.Y)
gui.DrawBool("Autowalk", bblitAutowalk.Enabled())
if bblitAutowalk.CountWaypoints() > 0 then
for i, wp in ipairs(waypoints) do
gui.DrawVector3(string.format("Waypoint %d", i), wp.X, wp.Y, wp.Z)
end
end
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
NORMAL_CARROTS = memory.read_u8 (0x010043)
CLOCKS = memory.read_u8 (0x010045)
GC1 = memory.read_u8 (0x010047)
GC2 = memory.read_u8 (0x01013C)
GOLDEN_CARROTS = GC1 + GC2 *256
HP = memory.read_u8 (0x010041)
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)
-- Kork: What is the purpose of this? leaving space for text in the right side?
-- client.SetGameExtraPadding(200, 0, 500, 0)
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(0, 150)
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)
gui.DrawValue( "HP: ", HP)
gui.DrawValue( "Carrots: ", NORMAL_CARROTS)
gui.DrawValue( "Golden Carrots: ", GOLDEN_CARROTS)
gui.DrawValue( "Clocks: ", CLOCKS)
if autowalk.Enabled() then
if autowalk.CountWaypoints() > 0 then
gui.DrawValue( "Waypoint BAM angle", autowalk.GetWaypointAngle())
gui.DrawValue( "Diff BAM angle", autowalk.GetDifferenceAngle())
if autowalk.Enabled() then
local Xdistance = pos.X - autowalk.GetFirstWaypoint().X
local Zdistance = pos.Z - autowalk.GetFirstWaypoint().Z
gui.DrawValue( "Autowalk distance", math.sqrt(Xdistance*Xdistance + Zdistance*Zdistance))
end
end
end
autowalk.DrawHUD(pos)
emu.frameadvance()
end
end
main()