A bot for executing strategies in the USA version of Luminous Arc.
-- Enumeration
magic, skill, flashDrive, synergy, item = 0, 1, 2, 3, 4
north, east, south, west = 0, 1, 3, 2
shiftLeft, shiftRight = -2, 2
touchModeR, touchModeL, buttonMode = 0, 1, 2
fastDelay, normalDelay, slowDelay = 0, 1, 2
-- States
storageEnabled = true
storedInputExists = false
targetWithButtons = false
resetAfterButtonTarget = true
toWait = normalDelay
toWaitForWait = normalDelay
toWaitForAttack = normalDelay
-- Memory addresses
function GetCurrentUnit()
return memory.readbyte(0x02159C68)
end
function GetUIMovement()
return memory.readbyte(0x02257DC8)
end
function GetMenuType()
return memory.readbyte(0x02258254)
end
function GetTransitionState()
return memory.readbyte(0x022578E4)
end
function GetMenuScroll()
return memory.readbyte(0x02257DC8)
end
function LostFrameOnConfirm()
return memory.readbyte(0x0225C754)
end
function GetDefaultFacing()
return memory.readbyte(0x022581F2)
end
function GetScrollX()
return memory.readword(0x0214F324)
end
function GetScrollY()
return memory.readword(0x0214F326)
end
function MenuVisible()
return memory.readbyte(0x0225C4FA) ~= 0
end
function MenuOnLeft()
return memory.readbyte(0x02257924) == 1
end
function GetInputMode()
return memory.readbyte(0x02257910)
end
function GetMenuSelectionPos()
return memory.readbyte(0x022581F2)
end
function GetCursorX()
return memory.readbyte(0x02257900)
end
function GetCursorY()
return memory.readbyte(0x02257904)
end
function GetTotalUnitCount()
return memory.readbyte(0x02257D9A)
end
function GetPlayerUnitCount()
return memory.readbyte(0x02257D9C)
end
function GetEnemyUnitCount()
return GetTotalUnitCount() - GetPlayerUnitCount()
end
function AllEnemiesKilled()
return GetTotalUnitCount() == GetPlayerUnitCount()
end
function GetUnitCoords(unit)
local nameAddress = 0x021591DC + 0xA6 * unit
local xAddress, yAddress = nameAddress + 0x9A, nameAddress + 0x9C
return memory.readbyte(xAddress), memory.readbyte(yAddress)
end
function GetUnitFacing(unit)
local nameAddress = 0x021591DC + 0xA6 * unit
return memory.readbyte(nameAddress + 0xA0)
end
-- This currently only works correctly for player units
function GetUnitSpriteFlip(unit)
local address = 0x0225DDAE + 0x1F8 * unit
return memory.readbyte(address)
end
-- This currently only works correctly for player units
function GetUnitSpriteDirection(unit)
local address = 0x0225DE22 + 0x1F8 * unit
return memory.readbyte(address)
end
-- This currently only works correctly for player units
function GetUnitInterimFacing(unit)
local spriteFlip, spriteFacing = GetUnitSpriteFlip(unit), GetUnitSpriteDirection(unit)
if spriteFlip == 0 then
if spriteFacing == 0 then
return west
else
return north
end
else
if spriteFacing == 0 then
return south
else
return east
end
end
end
-- This currently only works correctly for player units
function GetUnitOtherCount(unit, otherType)
if otherType == item then
-- TODO: find an address for this
return 255
else
local address = 0x0225DE2A + 0x1F8 * unit + 0x2 * otherType
return memory.readbyte(address)
end
end
-- Menu IDs
function GetWaitPosFromMenuID(id)
if id == 12 then
return 3
elseif id == 13 or id == 24 then
return 2
elseif id == 25 then
return 1
else
return -1
end
end
function GetActionPosFromMenuID(id)
if id == 12 then
return 2
elseif id == 13 then
return 1
else
return -1
end
end
function GetOtherPosFromActionMenuID(otherType, id)
if id == 23 then
if otherType == item then
return 2
else
return -1
end
elseif id == 22 then
if otherType == magic then
return 2
elseif otherType == item then
return 3
else
return -1
end
elseif id == 21 then
if otherType == skill then
return 2
elseif otherType == item then
return 3
else
return -1
end
elseif id == 20 then
if otherType == magic then
return 2
elseif otherType == skill then
return 3
elseif otherType == item then
return 4
else
return -1
end
elseif id == 19 then
if otherType == skill then
return 2
elseif otherType == flashDrive then
return 3
elseif otherType == synergy then
return 4
elseif otherType == item then
return 5
else
return -1
end
elseif id == 18 then
if otherType == magic then
return 2
elseif otherType == flashDrive then
return 3
elseif otherType == synergy then
return 4
elseif otherType == item then
return 5
else
return -1
end
elseif id == 17 then
if otherType == magic then
return 2
elseif otherType == skill then
return 3
elseif otherType == flashDrive then
return 4
elseif otherType == synergy then
return 5
elseif otherType == item then
return 6
else
return -1
end
elseif id == 16 then
if otherType == magic then
return 2
elseif otherType == flashDrive then
return 3
elseif otherType == item then
return 4
else
return -1
end
elseif id == 15 then
if otherType == skill then
return 2
elseif otherType == flashDrive then
return 3
elseif otherType == item then
return 4
else
return -1
end
elseif id == 14 then
if otherType == magic then
return 2
elseif otherType == skill then
return 3
elseif otherType == flashDrive then
return 4
elseif otherType == item then
return 5
else
return -1
end
else
return -1
end
end
function IsBaseMenu(id)
return id == 12 or id == 13 or id == 24 or id == 25
end
function IsActionMenu(id)
return id >= 14 and id <= 23
end
function IsOtherActionMenu(id)
return id == 65 or id == 69 or id == 74 or id == 78 or id == 83
end
function GetOtherMenuID(otherType)
if otherType == magic then
return 74
elseif otherType == skill then
return 69
elseif otherType == flashDrive then
return 78
elseif otherType == synergy then
return 83
elseif otherType == item then
return 65
else
return -1
end
end
function GetOtherTypeFromID(id)
if id == 65 then
return item
elseif id == 69 then
return skill
elseif id == 74 then
return magic
elseif id == 78 then
return flashDrive
elseif id == 83 then
return synergy
end
end
function GetOptionCountFromMenuID(id)
if id == 17 then
return 7
elseif id == 14 or id == 18 or id == 19 then
return 6
elseif id == 12 or id == 15 or id == 16 or id == 20 then
return 5
elseif id == 13 or id == 21 or id == 22 then
return 4
elseif id == 23 or id == 24 or id == 25 then
return 3
elseif IsOtherActionMenu(id) then
return GetUnitOtherCount(GetCurrentUnit(), GetOtherTypeFromID(id))
else
return 0
end
end
-- Conversion and useful functions
function MapToScreenCoords(mapX, mapY, height)
local scrollX, scrollY = GetScrollX(), GetScrollY()
local newX = 256 - scrollX + 16 * (mapX - mapY)
local newY = 632 - scrollY - 8 * (mapX + mapY + height)
return newX, newY
end
function GetRelativeFacing(unit, target)
if unit == target then
return GetUnitFacing(unit)
else
local unitX, unitY = GetUnitCoords(unit)
local targetX, targetY = GetUnitCoords(target)
return GetRelativeFacingFromPositions(unitX, unitY, targetX, targetY)
end
end
function GetRelativeFacingFromPositions(unitX, unitY, targetX, targetY)
local xDiff, yDiff = math.abs(unitX - targetX), math.abs(unitY - targetY)
if yDiff > xDiff then
if unitY > targetY then
return 3
else
return 0
end
else
if unitX > targetX then
return 2
else
return 1
end
end
end
function FindStepsToOption(optionPos, id)
local currentPos, maxOption = GetMenuSelectionPos() + 1, GetOptionCountFromMenuID(id)
local downSteps = (optionPos - currentPos + maxOption) % maxOption
local upSteps = maxOption - downSteps
if (upSteps < downSteps) then
return -1 * upSteps
end
return downSteps
end
-- Selection and holding
function SelectNthOptionOnLeft(n)
stylus.set{x = 15, y = 24 * n, touch = true}
end
function SelectNthOptionOnRight(n)
stylus.set{x = 240, y = 24 * n, touch = true}
end
function SelectNthOption(n)
if MenuOnLeft() then
SelectNthOptionOnLeft(n)
else
SelectNthOptionOnRight(n)
end
end
function SelectNthOtherOption(n)
stylus.set{x = 148, y = 8 + 16 * n, touch = true}
end
function HoldForNFrames(xIn, yIn, n)
WaitForLagEnd()
for i = 1, n do
stylus.set{x = xIn, y = yIn, touch = true}
coroutine.yield()
end
end
function HoldNthOptionOnLeft(n, frames)
HoldForNFrames(15, 24 * n, frames)
end
function HoldNthOptionOnRight(n, frames)
HoldForNFrames(240, 24 * n, frames)
end
function HoldNthOption(n, frames)
if MenuOnLeft() then
HoldNthOptionOnLeft(n, frames)
else
HoldNthOptionOnRight(n, frames)
end
end
function HoldNthOtherOption(n, frames)
HoldForNFrames(148, 8 + 16 * n, frames)
end
function HoldUntilState(xIn, yIn, minFrames, targetState)
HoldForNFrames(xIn, yIn, minFrames)
while GetTransitionState() ~= targetState do
stylus.set{x = xIn, y = yIn, touch = true}
coroutine.yield()
end
end
function HoldOptionUntilStateOnLeft(selectionPos, minFrames, targetState, switchMode)
switchMode = switchMode or false
local done = false
if GetInputMode() ~= buttonMode then
HoldNthOptionOnLeft(selectionPos, minFrames)
end
while GetTransitionState() ~= targetState and not done do
if GetInputMode() ~= buttonMode then
SelectNthOptionOnLeft(selectionPos)
if switchMode and GetMenuScroll() == 0 and GetInputMode() == touchModeR then
SetInputMode(touchModeL)
done = true
end
end
coroutine.yield()
end
end
function HoldOptionUntilStateOnRight(selectionPos, minFrames, targetState, switchMode)
switchMode = switchMode or false
local done = false
if GetInputMode() ~= buttonMode then
HoldNthOptionOnRight(selectionPos, minFrames)
end
while GetTransitionState() ~= targetState and not done do
if GetInputMode() ~= buttonMode then
SelectNthOptionOnRight(selectionPos)
if switchMode and GetMenuScroll() == 0 and GetInputMode() == touchModeR then
SetInputMode(touchModeL)
done = true
end
end
coroutine.yield()
end
end
function HoldOptionUntilState(selectionPos, minFrames, targetState, switchMode)
if MenuOnLeft() then
HoldOptionUntilStateOnLeft(selectionPos, minFrames, targetState, switchMode)
else
HoldOptionUntilStateOnRight(selectionPos, minFrames, targetState, switchMode)
end
end
function HandleOptionSelection(selectionPos, id, minFrames, targetState, switchMode)
if GetInputMode() == buttonMode then
WaitUntilState(targetState)
SelectOptionWithButtons(selectionPos, id)
else
HoldOptionUntilState(selectionPos, minFrames, targetState, switchMode)
end
end
function HoldOtherOptionUntilState(selectionPos, minFrames, targetState)
if GetInputMode() ~= buttonMode then
HoldNthOtherOption(selectionPos, minFrames)
end
while GetTransitionState() ~= targetState do
if GetInputMode() ~= buttonMode then
SelectNthOtherOption(selectionPos)
end
coroutine.yield()
end
end
function SelectOptionWithButtons(optionPos, id)
local steps = FindStepsToOption(optionPos, id)
local down = steps > 0
local optionCount = GetOptionCountFromMenuID(id)
if not down then
steps = steps * -1
end
optionPos = optionPos - 1
local currentPos = GetMenuSelectionPos()
local nextPos = currentPos
for i = 1, steps do
WaitForLagEnd()
if down then
joypad.set{down = true}
nextPos = (nextPos + 1) % optionCount
else
joypad.set{up = true}
nextPos = (nextPos - 1 + optionCount) % optionCount
end
WaitNFrames(1)
currentPos = GetMenuSelectionPos()
while currentPos ~= nextPos do
if down then
joypad.set{down = true}
else
joypad.set{up = true}
end
WaitForLagEnd()
currentPos = GetMenuSelectionPos()
end
if GetMenuSelectionPos() ~= optionPos then
WaitNFrames(1)
end
end
while (GetMenuType() == id) do
joypad.set{A = true}
WaitNFrames(1)
end
end
function SetInputMode(mode, storedX, storedY)
local currentMode = GetInputMode()
local storeInput = storedX and storedY
local changes = (mode - currentMode + 3) % 3
for i = 1, changes do
WaitForLagEnd()
if storeInput and mode == buttonMode and currentMode == touchModeL then
stylus.set{x = storedX, y = storedY, touch = true}
coroutine.yield()
WaitForLagEnd()
stylus.set{x = storedX, y = storedY, touch = true}
storedInputExists = true
else
storedInputExists = false
end
joypad.set{select = true}
coroutine.yield()
if i ~= changes then
WaitNFrames(1)
end
end
end
-- Waiting
function WaitForLagEnd()
while emu.lagged() do
coroutine.yield()
end
end
function WaitNFrames(n)
WaitForLagEnd()
for i = 1, n do
coroutine.yield()
end
end
function WaitForScrollStop()
local currentScrollX, currentScrollY = GetScrollX(), GetScrollY()
local oldScrollX, oldScrollY = currentScrollX, currentScrollY
local stopped = false
while not stopped do
coroutine.yield()
currentScrollX, currentScrollY = GetScrollX(), GetScrollY()
stopped = currentScrollX == oldScrollX and currentScrollY == oldScrollY
oldScrollX, oldScrollY = currentScrollX, currentScrollY
end
end
function WaitForVisibleMenu()
while not MenuVisible() do
coroutine.yield()
end
end
function WaitForUnitTurn(unit)
local currentUnit = GetCurrentUnit()
while currentUnit ~= unit do
coroutine.yield()
currentUnit = GetCurrentUnit()
end
end
function WaitForUnitTurnAndVisibleMenu(unit)
WaitForUnitTurn(unit)
WaitForVisibleMenu()
end
function WaitForPlayerTurn()
local currentUnit = GetCurrentUnit()
while currentUnit > 7 do
coroutine.yield()
currentUnit = GetCurrentUnit()
end
end
function WaitForBaseMenuID()
local id = GetMenuType()
while not IsBaseMenu(id) do
coroutine.yield()
id = GetMenuType()
end
return id
end
function WaitForActionMenuID()
local id = GetMenuType()
while not IsActionMenu(id) do
coroutine.yield()
id = GetMenuType()
end
return id
end
function WaitForMenuType(id)
while GetMenuType() ~= id do
coroutine.yield()
end
end
function WaitUntilState(targetState)
while GetTransitionState() ~= targetState do
coroutine.yield()
end
end
function WaitUntilMenuScroll(targetScroll)
while GetMenuScroll() ~= targetScroll do
coroutine.yield()
end
end
function WaitPastMenuScroll(oldState)
while GetMenuScroll() == oldState do
coroutine.yield()
end
end
function WaitForAllEnemiesKilled()
while not AllEnemiesKilled() do
coroutine.yield()
end
end
-- Actions
function SetButtonTargeting(useButtons)
targetWithButtons = useButtons
resetAfterButtonTarget = not useButtons
end
function UseButtonsForNextTarget()
targetWithButtons = true
end
function SetStorage(useStorage)
storageEnabled = useStorage
end
function SetNextWaitDelay(delay)
toWaitForWait = delay
end
function UpdateWaitConfirmDelay()
toWait = toWaitForWait
toWaitForWait = normalDelay
end
function SetNextAttackDelay(delay)
toWaitForAttack = delay
end
function UpdateAttackConfirmDelay()
toWait = toWaitForAttack
toWaitForAttack = normalDelay
end
function UpdateRemaining(remaining)
if (remaining > 0) then
remaining = remaining - 1
else
remaining = remaining + 1
end
return remaining
end
function SelectTileWithButtons(tileX, tileY)
local currentX, currentY = GetCursorX(), GetCursorY()
local remainingX, remainingY = tileX - currentX, tileY - currentY
local absX, absY = math.abs(remainingX), math.abs(remainingY)
local xFirst, steps = absX > absY, math.max(absX, absY)
for i = 1, steps do
if xFirst then
MoveCursorLeftOrRight(remainingX)
remainingX = UpdateRemaining(remainingX)
if (remainingY ~= 0) then
MoveCursorUpOrDown(remainingY)
remainingY = UpdateRemaining(remainingY)
elseif remainingX ~= 0 then
WaitNFrames(1)
end
else
MoveCursorUpOrDown(remainingY)
remainingY = UpdateRemaining(remainingY)
if (remainingX ~= 0) then
MoveCursorLeftOrRight(remainingX)
remainingX = UpdateRemaining(remainingX)
elseif remainingY ~= 0 then
WaitNFrames(1)
end
end
end
joypad.set{A = true}
WaitNFrames(1)
end
function MoveCursorUpOrDown(direction)
WaitForLagEnd()
if direction > 0 then
joypad.set{up = true}
else
joypad.set{down = true}
end
WaitNFrames(1)
end
function MoveCursorLeftOrRight(direction)
WaitForLagEnd()
if direction > 0 then
joypad.set{right = true}
else
joypad.set{left = true}
end
WaitNFrames(1)
end
function SelectConfirm()
if GetInputMode() ~= buttonMode then
HoldForNFrames(80, 168, 2)
end
while GetMenuScroll() ~= 80 do
if GetInputMode() ~= buttonMode then
stylus.set{x = 80, y = 168, touch = true}
end
coroutine.yield()
end
for i = 1, toWait do
if GetInputMode() ~= buttonMode then
stylus.set{x = 80, y = 168, touch = true}
end
coroutine.yield()
end
toWait = normalDelay
if GetInputMode() == buttonMode then
while GetTransitionState() == 29 do
joypad.set{A = true}
coroutine.yield()
end
end
WaitNFrames(1)
end
function WaitInDirection(targetDirection)
local x, y = 0, 0
local currentDirection = GetDefaultFacing()
if targetDirection == 0 then
x, y = 0, 0
elseif targetDirection == 1 then
x, y = 255, 0
elseif targetDirection == 2 then
x, y = 0, 192
elseif targetDirection == 3 then
x, y = 255, 192
end
if GetInputMode() == buttonMode or storedInputExists then
WaitForLagEnd()
if targetDirection == north then
joypad.set{up = true}
elseif targetDirection == east then
joypad.set{right = true}
elseif targetDirection == south then
joypad.set{down = true}
elseif targetDirection == west then
joypad.set{left = true}
end
else
HoldForNFrames(x, y, 2)
end
WaitNFrames(1)
if currentDirection ~= targetDirection then
if GetInputMode() == buttonMode then
joypad.set{A = true}
else
HoldForNFrames(x, y, 2)
end
end
WaitNFrames(1)
end
function SelectWait(targetDirection)
local id = WaitForBaseMenuID()
local targetState = id
local selectionPos = GetWaitPosFromMenuID(id)
local useButtons = storageEnabled and GetUnitInterimFacing(GetCurrentUnit()) ~= targetDirection
if GetTransitionState() ~= id then
WaitUntilState(11)
end
HandleOptionSelection(selectionPos, id, 2, targetState, useButtons)
WaitUntilMenuScroll(80)
WaitUntilMenuScroll(0)
if GetInputMode() == touchModeL and useButtons then
SetInputMode(buttonMode, 80, 168)
elseif GetInputMode() == buttonMode then
WaitNFrames(2)
end
WaitInDirection(targetDirection)
UpdateWaitConfirmDelay()
if GetInputMode() == buttonMode then
WaitUntilMenuScroll(80)
coroutine.yield()
if storedInputExists then
SetInputMode(touchModeR)
else
SelectConfirm()
end
else
SelectConfirm()
end
end
function SelectMove(moveX, moveY, height)
local id = WaitForBaseMenuID()
local targetState = id
if GetTransitionState() ~= id then
WaitUntilState(11)
end
HandleOptionSelection(1, id, 2, targetState)
local touchX, touchY = MapToScreenCoords(moveX, moveY, height)
WaitNFrames(1)
WaitUntilState(11)
if GetInputMode() == buttonMode then
WaitUntilState(44)
else
HoldUntilState(touchX, touchY, 2, 44)
stylus.set{x = touchX, y = touchY, touch = true}
end
coroutine.yield()
if GetInputMode() == buttonMode then
coroutine.yield()
SelectTileWithButtons(moveX, moveY)
else
WaitNFrames(1)
end
end
function SelectAction()
local id = WaitForBaseMenuID()
local targetState = id
local selectionPos = GetActionPosFromMenuID(id)
WaitUntilState(11)
HandleOptionSelection(selectionPos, id, 2, targetState)
WaitNFrames(1)
end
function SelectAttack(targetUnit, height, xShift, yShift)
xShift, yShift = xShift or 0, yShift or 0
SelectAction()
local id = WaitForActionMenuID()
local targetState = id
local targetX, targetY = GetUnitCoords(targetUnit)
local touchX, touchY = MapToScreenCoords(targetX, targetY, height)
touchX, touchY = touchX + xShift, touchY - yShift - 4
WaitUntilState(11)
HandleOptionSelection(1, id, 2, targetState, targetWithButtons)
WaitNFrames(1)
WaitUntilState(11)
if targetWithButtons then
WaitUntilMenuScroll(0)
elseif GetInputMode() == buttonMode then
WaitUntilState(64)
else
HoldUntilState(touchX, touchY, 2, 64)
end
if GetInputMode() == touchModeL and targetWithButtons then
if storageEnabled then
SetInputMode(buttonMode, 80, 168)
else
SetInputMode(buttonMode)
WaitNFrames(2)
end
WaitNFrames(2)
end
if GetInputMode() == buttonMode then
SelectTileWithButtons(targetX, targetY)
end
UpdateAttackConfirmDelay()
if GetInputMode() == buttonMode then
WaitUntilMenuScroll(80)
for i = 1, toWait do
coroutine.yield()
end
toWait = normalDelay
if storedInputExists then
SetInputMode(touchModeR)
if resetAfterButtonTarget then
targetWithButtons = false
end
else
if not storageEnabled then
SetInputMode(touchModeR)
end
SelectConfirm()
end
else
WaitNFrames(2)
SelectConfirm()
end
end
function SelectOtherAction(targetUnit, otherType, listPos, height, xShift, yShift)
xShift, yShift = xShift or 0, yShift or 0
local targetX, targetY = GetUnitCoords(targetUnit)
local touchX, touchY = MapToScreenCoords(targetX, targetY, height)
touchX, touchY = touchX + xShift, touchY - yShift - 4
SelectOtherActionAtPosition(touchX, touchY, otherType, listPos, height, targetX, targetY)
end
function SelectOtherActionAtPosition(touchX, touchY, otherType, listPos, height, targetX, targetY)
SelectAction()
local id = WaitForActionMenuID()
local otherPos = GetOtherPosFromActionMenuID(otherType, id)
local otherID = GetOtherMenuID(otherType)
local targetState = id
local othertargetState = otherID + 1
-- Verify if this can save time for other positions
local useButtons = storageEnabled and listPos == 1
WaitUntilState(11)
HoldOptionUntilState(otherPos, 2, targetState, useButtons or targetWithButtons)
if GetInputMode() == buttonMode then
SelectOptionWithButtons(otherPos, id)
end
WaitNFrames(1)
WaitForMenuType(otherID)
WaitUntilState(11)
if GetInputMode() == touchModeL and useButtons then
WaitUntilState(othertargetState - 1)
if targetWithButtons then
SetInputMode(buttonMode, 80, 168)
else
SetInputMode(buttonMode, touchX, touchY)
end
coroutine.yield()
else
HoldOtherOptionUntilState(listPos, 2, othertargetState)
WaitNFrames(2)
HoldNthOtherOption(listPos, 2)
end
if GetInputMode() == buttonMode then
SelectOptionWithButtons(listPos, otherID)
end
WaitNFrames(1)
othertargetState = othertargetState + 1
if GetInputMode() == buttonMode or useButtons or targetWithButtons then
WaitNFrames(2)
else
HoldForNFrames(touchX, touchY, 2)
end
WaitUntilState(11)
-- Flash drives sometimes use 82 instead of 81 (81 instead of 80 the frame before)
while GetTransitionState() ~= othertargetState and GetTransitionState() ~= 81 do
if GetInputMode() ~= buttonMode then
stylus.set{x = touchX, y = touchY, touch = true}
end
coroutine.yield()
end
if GetInputMode() == touchModeL and targetWithButtons then
if storageEnabled then
SetInputMode(buttonMode, 80, 168)
else
SetInputMode(buttonMode)
end
end
if GetInputMode() == buttonMode then
if storedInputExists and not targetWithButtons then
SetInputMode(touchModeR)
else
WaitNFrames(2)
SelectTileWithButtons(targetX, targetY)
end
else
stylus.set{x = touchX, y = touchY, touch = true}
coroutine.yield()
end
UpdateAttackConfirmDelay()
coroutine.yield()
if GetInputMode() == buttonMode then
WaitUntilMenuScroll(80)
if storedInputExists then
for i = 1, toWait do
coroutine.yield()
end
toWait = normalDelay
end
if storedInputExists then
SetInputMode(touchModeR)
if resetAfterButtonTarget then
targetWithButtons = false
end
else
if not storageEnabled then
SetInputMode(touchModeR)
end
SelectConfirm()
end
else
WaitNFrames(1)
SelectConfirm()
end
end
-- Combined actions
function JustWait(unit, targetDirection)
WaitForUnitTurnAndVisibleMenu(unit)
SelectWait(targetDirection)
end
function MoveAndWait(unit, moveX, moveY, height, targetDirection)
WaitForUnitTurnAndVisibleMenu(unit)
SelectMove(moveX, moveY, height)
WaitForVisibleMenu()
SelectWait(targetDirection)
end
function MoveAndAttack(unit, targetUnit, moveX, moveY, moveHeight, targetHeight, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectMove(moveX, moveY, moveHeight)
WaitForVisibleMenu()
SelectAttack(targetUnit, targetHeight, xShift, yShift)
end
function AttackAndWait(unit, targetUnit, height, targetDirection, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectAttack(targetUnit, height, xShift, yShift)
SelectWait(targetDirection)
end
function AttackAndMove(unit, targetUnit, moveX, moveY, targetHeight, moveHeight, targetDirection, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectAttack(targetUnit, targetHeight, xShift, yShift)
SelectMove(moveX, moveY, moveHeight)
SelectWait(targetDirection)
end
function AttackToWin(unit, targetUnit, height, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectAttack(targetUnit, height, xShift, yShift)
end
function MoveAndDoOther(unit, targetUnit, otherType, listPos, moveX, moveY, moveHeight, targetHeight, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectMove(moveX, moveY, moveHeight)
WaitForVisibleMenu()
SelectOtherAction(targetUnit, otherType, listPos, targetHeight, xShift, yShift)
end
function MoveAndDoOtherAtPosition(unit, targetX, targetY, otherType, listPos, moveX, moveY, moveHeight, targetHeight)
WaitForUnitTurnAndVisibleMenu(unit)
SelectMove(moveX, moveY, moveHeight)
WaitForVisibleMenu()
SelectOtherActionAtPosition(targetX, targetY, otherType, listPos, targetHeight)
end
function DoOtherAndWait(unit, targetUnit, otherType, listPos, height, targetDirection, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectOtherAction(targetUnit, otherType, listPos, height, xShift, yShift)
SelectWait(targetDirection)
end
function DoOtherAtPositionAndWait(unit, targetX, targetY, otherType, listPos, height, targetDirection)
WaitForUnitTurnAndVisibleMenu(unit)
SelectOtherActionAtPosition(targetX, targetY, otherType, listPos, height)
SelectWait(targetDirection)
end
function DoOtherAndMove(unit, targetUnit, otherType, listPos, moveX, moveY, targetHeight, moveHeight, targetDirection, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectOtherAction(targetUnit, otherType, listPos, targetHeight, xShift, yShift)
SelectMove(moveX, moveY, moveHeight)
SelectWait(targetDirection)
end
function DoOtherAtPositionAndMove(unit, targetX, targetY, otherType, listPos, moveX, moveY, targetHeight, moveHeight, targetDirection)
WaitForUnitTurnAndVisibleMenu(unit)
SelectOtherActionAtPosition(targetX, targetY, otherType, listPos, targetHeight)
SelectMove(moveX, moveY, moveHeight)
SelectWait(targetDirection)
end
function DoOtherToWin(unit, targetUnit, otherType, listPos, height, xShift, yShift)
WaitForUnitTurnAndVisibleMenu(unit)
SelectOtherActionAtPosition(targetUnit, otherType, listPos, height, xShift, yShift)
end
function DoOtherAtPositionToWin(unit, targetX, targetY, otherType, listPos, height)
WaitForUnitTurnAndVisibleMenu(unit)
SelectOtherAction(targetX, targetY, otherType, listPos, height)
end
-- GUI
function drawGUI()
local unit = GetCurrentUnit()
local x, y = GetUnitCoords(unit)
gui.text(1, 105, "Total lag: " .. emu.lagcount())
gui.text(1, 115, "Am I lagging? " .. tostring(emu.lagged()))
gui.text(1, 135, "Current unit: " .. unit)
gui.text(1, 145, "Current co-ords: " .. x .. ", " .. y)
gui.text(1, 155, "Current facing: " .. GetUnitFacing(unit))
gui.text(1, 165, "Default facing: " .. GetDefaultFacing())
end
gui.register(drawGUI)
-- Testing
released = true
function TestLoop()
while true do
menuType = GetMenuType()
state = GetTransitionState()
currentUnit = GetCurrentUnit()
if currentUnit > 7 then
xMid, yMid = MapToScreenCoords(16, 16, 2)
stylus.set{x = xMid, y = yMid, touch = true}
elseif menuType == 12 and (state ~= 12 or released) then
SelectNthOption(3)
released = false
elseif menuType == 44 and (state ~= 45 or released) then
SelectNthOption(1)
released = false
elseif menuType == 59 then
if state == 29 then
HoldForNFrames(80, 168, 2)
else
WaitInDirection(math.random(0,3))
end
released = false
else
released = true
end
coroutine.yield()
end
end
-- Strats
function OldMap1StratNoStoredMagic()
local Alph, Theo, Leon, Heath = 0, 1, 2, 3
local Argata, Zonar, Flurg, Laz = 8, 9, 10, 11
SetNextWaitDelay(slowDelay)
MoveAndWait(Heath, 11, 11, 1, east)
MoveAndWait(Theo, 9, 15, 1, north)
MoveAndWait(Alph, 12, 14, 1, west)
MoveAndWait(Leon, 10, 14, 1, north)
MoveAndAttack(Heath, Argata, 14, 11, 2, 2)
SetNextAttackDelay(fastDelay)
AttackAndWait(Theo, Laz, 1, east)
AttackAndWait(Alph, Laz, 1, east)
MoveAndAttack(Leon, Laz, 13, 15, 1, 1)
SetNextAttackDelay(fastDelay)
MoveAndAttack(Theo, Laz, 10, 15, 1, 1)
SetNextAttackDelay(fastDelay)
MoveAndAttack(Alph, Flurg, 13, 17, 2, 2)
AttackAndWait(Heath, Argata, 2, GetRelativeFacing(Heath, Argata))
AttackAndWait(Leon, Flurg, 2, south)
SetStorage(false)
MoveAndDoOther(Heath, Zonar, magic, 1, 14, 14, 2, 2)
SetStorage(true)
MoveAndAttack(Leon, Flurg, 16, 16, 2, 2)
SetNextAttackDelay(fastDelay)
AttackAndWait(Theo, Zonar, 2, east)
SetNextAttackDelay(fastDelay)
AttackToWin(Alph, Zonar, 2)
end
function OldMap1Strat()
local Alph, Theo, Leon, Heath = 0, 1, 2, 3
local Argata, Zonar, Flurg, Laz = 8, 9, 10, 11
SetNextWaitDelay(slowDelay)
MoveAndWait(Heath, 11, 11, 1, east)
MoveAndWait(Theo, 9, 15, 1, north)
MoveAndWait(Alph, 12, 14, 1, west)
MoveAndWait(Leon, 10, 14, 1, north)
MoveAndAttack(Heath, Argata, 14, 11, 2, 2)
SetNextAttackDelay(fastDelay)
AttackAndWait(Theo, Laz, 1, east)
AttackAndWait(Alph, Laz, 1, east)
MoveAndAttack(Leon, Laz, 13, 15, 1, 1)
SetNextAttackDelay(fastDelay)
MoveAndAttack(Theo, Laz, 10, 15, 1, 1)
SetNextAttackDelay(fastDelay)
MoveAndAttack(Alph, Flurg, 13, 17, 2, 2)
AttackAndWait(Heath, Argata, 2, GetRelativeFacing(Heath, Argata))
AttackAndWait(Leon, Flurg, 2, south)
MoveAndDoOther(Heath, Zonar, magic, 1, 14, 14, 2, 2)
MoveAndAttack(Leon, Flurg, 16, 16, 2, 2)
SetNextAttackDelay(fastDelay)
AttackAndWait(Theo, Zonar, 2, east)
AttackToWin(Alph, Zonar, 2)
end
function OldMap1StratWithButtons()
local Alph, Theo, Leon, Heath = 0, 1, 2, 3
local Argata, Zonar, Flurg, Laz = 8, 9, 10, 11
SetButtonTargeting(true)
SetNextWaitDelay(slowDelay)
MoveAndWait(Heath, 11, 11, 1, east)
MoveAndWait(Theo, 9, 15, 1, north)
MoveAndWait(Alph, 12, 14, 1, west)
MoveAndWait(Leon, 10, 14, 1, north)
SetNextAttackDelay(slowDelay)
MoveAndAttack(Heath, Argata, 14, 11, 2, 2)
SetNextAttackDelay(fastDelay)
AttackAndWait(Theo, Laz, 1, east)
SetNextAttackDelay(fastDelay)
AttackAndWait(Alph, Laz, 1, east)
MoveAndAttack(Leon, Laz, 13, 15, 1, 1)
SetNextAttackDelay(slowDelay)
MoveAndAttack(Theo, Laz, 10, 15, 1, 1)
MoveAndAttack(Alph, Flurg, 13, 17, 2, 2)
SetNextAttackDelay(4)
AttackAndWait(Heath, Argata, 2, GetRelativeFacing(Heath, Argata))
AttackAndWait(Leon, Flurg, 2, south)
MoveAndDoOther(Heath, Zonar, magic, 1, 14, 14, 2, 2)
MoveAndAttack(Leon, Flurg, 16, 16, 2, 2)
AttackAndWait(Theo, Zonar, 2, east)
AttackToWin(Alph, Zonar, 2)
end
function OldMap2Strat()
local Alph, Lucia = 0, 1
local Eman, Warloak, Bruneed = 8, 9, 11
MoveAndWait(Lucia, 13, 16, 2, east)
MoveAndWait(Alph, 12, 15, 2, west)
AttackAndWait(Lucia, Eman, 2, west)
AttackAndWait(Alph, Eman, 2, south)
DoOtherAndWait(Lucia, Warloak, magic, 1, 1, west)
AttackAndWait(Alph, Warloak, 1, west)
AttackAndWait(Lucia, Bruneed, 3, east)
MoveAndAttack(Alph, Bruneed, 15, 15, 3, 3)
end
function Map1Strat()
local Alph, Theo, Leon, Heath = 0, 1, 2, 3
local Argata, Zonar, Flurg, Laz = 8, 9, 10, 11
--[
MoveAndWait(Heath, 11, 11, 1, north)
MoveAndAttack(Theo, Leon, 12, 11, 2, 1)
MoveAndWait(Alph, 13, 12, 2, east)
MoveAndWait(Leon, 13, 10, 2, east)
MoveAndWait(Heath, 12, 12, 2, east)
MoveAndAttack(Alph, Argata, 16, 12, 2, 2)
MoveAndAttack(Leon, Argata, 16, 10, 2, 2)
AttackAndWait(Theo, Flurg, 2, north, 0, 16)
MoveAndAttack(Heath, Flurg, 14, 13, 2, 2)
MoveAndAttack(Alph, Zonar, 19, 13, 2, 2)
MoveAndAttack(Leon, Zonar, 18, 12, 2, 2)
JustWait(Theo, north)
AttackToWin(Heath, Laz, 2)
end
function Map2Strat()
local Alph, Lucia = 0, 1
local Eman, Warloak, Bruneed = 8, 9, 11
--[[
SetNextWaitDelay(slowDelay)
JustWait(Lucia, south)
JustWait(Alph, north)
MoveAndAttack(Lucia, Warloak, 12, 12, 2, 1)
MoveAndAttack(Alph, Warloak, 11, 12, 2, 1)
AttackAndWait(Lucia, Eman, 3, east)
MoveAndAttack(Alph, Eman, 13, 13, 3, 3)
MoveAndAttack(Lucia, Bruneed, 12, 14, 3, 3)
AttackToWin(Alph, Bruneed, 3)
--]]
--[ New strat, saves 8f by latest optimizations; Bruneed 2W 1S is faster but is it possible to not get 3S after it which makes it slower?
SetNextWaitDelay(slowDelay)
JustWait(Lucia, south)
JustWait(Alph, north)
MoveAndAttack(Lucia, Warloak, 13, 11, 3, 2)
MoveAndAttack(Alph, Warloak, 12, 11, 2, 2)
AttackAndWait(Lucia, Eman, 3, south, -8, 8)
MoveAndAttack(Alph, Eman, 12, 12, 2, 3, -14, 18)
MoveAndAttack(Lucia, Bruneed, 13, 12, 3, 3, -8, 18)
UseButtonsForNextTarget()
SetNextAttackDelay(fastDelay)
MoveAndAttack(Alph, Bruneed, 12, 13, 3, 3)
--]]
end
function Map5Strat()
local Alph, Heath, Leon, Theo = 0, 1, 2, 3
local Vanessa, Youknix, Pucelle, Beestah, Gul, Gal = 8, 9, 10, 11, 12, 13
--[
MoveAndWait(Heath, 13, 12, 2, north)
MoveAndWait(Alph, 13, 13, 3, north)
MoveAndWait(Theo, 12, 11, 2, north)
MoveAndWait(Leon, 12, 12, 2, north)
MoveAndAttack(Heath, Beestah, 13, 16, 4, 4)
MoveAndAttack(Alph, Pucelle, 15, 13, 10, 10)
MoveAndAttack(Theo, Pucelle, 13, 14, 4, 10, 0, 20)
MoveAndWait(Leon, 13, 15, 4, north)
MoveAndAttack(Leon, Gal, 14, 17, 4, 4)
AttackAndWait(Heath, Gal, 4, north)
MoveAndWait(Theo, 13, 15, 4, north)
JustWait(Alph, east)
JustWait(Alph, east)
AttackAndWait(Theo, Vanessa, 4, north)
MoveAndAttack(Heath, Vanessa, 14, 17, 4, 4)
MoveAndAttack(Alph, Youknix, 16, 11, 9, 7)
MoveAndAttack(Theo, Youknix, 13, 11, 2, 7)
MoveAndAttack(Heath, Vanessa, 15, 20, 5, 5)
--]]
end
function DoStrat()
--emu.speedmode("turbo")
--OldMap1Strat()
--OldMap2Strat()
Map2Strat()
--Map5Strat()
WaitForAllEnemiesKilled()
emu.pause()
end
function DoPrinting()
confirmCheck = LostFrameOnConfirm()
currentFrame = emu.framecount()
if confirmCheck == 1 then
print("Possible confirm frame loss at frame " .. currentFrame)
end
end
function ContinueStrat()
DoPrinting()
coroutine.resume(strat)
end
strat = coroutine.create(DoStrat)
emu.registerbefore(ContinueStrat)