-- For use with the USA version of Luminous Arc
-- Developed by Nietono
-- 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
saveStatesEnabled = false
saveSlot = 0
minimiseHold = false;
-- 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 GetOtherTransitionState()
return memory.readbyte(0x02136AEC)
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)
return 2 * (1 - spriteFacing) + spriteFlip
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 south
else
return north
end
else
if unitX > targetX then
return west
else
return east
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)
for i = 1, n do
HoldThroughLag(function() stylus.set{x = xIn, y = yIn, touch = true} end)
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)
if saveStatesEnabled and minimiseHold then
-- Assuming the min hold will be 2 frames
FindTwoFrameHoldStart(function() return GetTransitionState() == targetState end)
end
HoldForNFrames(xIn, yIn, minFrames)
while GetTransitionState() ~= targetState do
stylus.set{x = xIn, y = yIn, touch = true}
coroutine.yield()
end
end
function HoldUntilOtherState(xIn, yIn, minFrames, targetState)
if saveStatesEnabled and minimiseHold then
-- Assuming the min hold will be 2 frames
FindTwoFrameHoldStart(function() return GetOtherTransitionState() == targetState end)
end
HoldForNFrames(xIn, yIn, minFrames)
while GetOtherTransitionState() ~= targetState do
stylus.set{x = xIn, y = yIn, touch = true}
coroutine.yield()
end
end
function HoldOptionUntilStateOnSide(selectionPos, minFrames, targetState, switchMode, holdFunction, selectFunction)
switchMode = switchMode or false
local done = false
if GetInputMode() ~= buttonMode then
if saveStatesEnabled and minimiseHold then
local function StateReached()
return GetTransitionState() == targetState or switchMode and GetMenuScroll() == 0 and GetInputMode() == touchModeR
end
-- Assuming the min hold will be 2 frames
FindTwoFrameHoldStart(StateReached)
end
holdFunction(selectionPos, minFrames)
end
while GetTransitionState() ~= targetState and not done do
if GetInputMode() ~= buttonMode then
selectFunction(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
HoldOptionUntilStateOnSide(selectionPos, minFrames, targetState, switchMode, HoldNthOptionOnLeft, SelectNthOptionOnLeft)
else
HoldOptionUntilStateOnSide(selectionPos, minFrames, targetState, switchMode, HoldNthOptionOnRight, SelectNthOptionOnRight)
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
if saveStatesEnabled and minimiseHold then
-- Assuming the min hold will be 2 frames
FindTwoFrameHoldStart(function() return GetTransitionState() == targetState end)
end
HoldNthOtherOption(selectionPos, minFrames)
end
while GetTransitionState() ~= targetState do
if GetInputMode() ~= buttonMode then
SelectNthOtherOption(selectionPos)
end
coroutine.yield()
end
end
-- TODO: test whether my fixes broke this
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
if down then
HoldThroughLag(function() joypad.set{down = true} end)
nextPos = (nextPos + 1) % optionCount
else
HoldThroughLag(function() joypad.set{up = true} end)
nextPos = (nextPos - 1 + optionCount) % optionCount
end
currentPos = GetMenuSelectionPos()
if down then
HoldThroughLag(function() joypad.set{down = true} end)
else
HoldThroughLag(function() joypad.set{up = true} end)
end
if GetMenuSelectionPos() ~= optionPos then
WaitNFrames(1)
end
end
while (GetMenuType() == id) do
joypad.set{A = true}
WaitNFrames(1)
end
end
function SetTouchAndMode(storedX, storedY)
stylus.set{x = storedX, y = storedY, touch = true}
joypad.set{select = true}
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
if storeInput and mode == buttonMode and currentMode == touchModeL then
HoldThroughLag(function() stylus.set{x = storedX, y = storedY, touch = true} end)
HoldThroughLag(function() SetTouchAndMode(storedX, storedY) end)
storedInputExists = true
else
storedInputExists = false
HoldThroughLag(function() joypad.set{select = true} end)
end
if i ~= changes then
WaitNFrames(1)
end
end
end
-- Waiting
function WaitNFrames(n)
for i = 1, n do
coroutine.yield()
end
end
function HoldThroughLag(pressAction)
pressAction()
coroutine.yield()
while emu.lagged() do
pressAction()
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 WaitUntilOtherState(targetState)
while GetOtherTransitionState() ~= 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
-- Save states
function SetSaveState(useSaveStates, newSaveSlot)
saveSlot = newSaveSlot or saveSlot
saveStatesEnabled = useSaveStates
end
function FindTwoFrameHoldStart(condition)
local holdFrame, followingFrame = emu.framecount(), emu.framecount()
savestate.save(saveSlot)
while not condition() do
coroutine.yield()
if not emu.lagged() then
holdFrame = followingFrame
followingFrame = emu.framecount() - 1
end
end
savestate.load(saveSlot)
while emu.framecount() < holdFrame 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 SetMinimiseHold(minimise)
minimiseHold = minimise
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)
if direction > 0 then
HoldThroughLag(function() joypad.set{up = true} end)
else
HoldThroughLag(function() joypad.set{down = true} end)
end
end
function MoveCursorLeftOrRight(direction)
if direction > 0 then
HoldThroughLag(function() joypad.set{right = true} end)
else
HoldThroughLag(function() joypad.set{left = true} end)
end
end
function SelectConfirm()
if GetInputMode() ~= buttonMode then
if saveStatesEnabled and minimiseHold then
FindTwoFrameHoldStart(function() return GetMenuScroll() == 80 end)
end
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
if targetDirection == north then
HoldThroughLag(function() joypad.set{up = true} end)
elseif targetDirection == east then
HoldThroughLag(function() joypad.set{right = true} end)
elseif targetDirection == south then
HoldThroughLag(function() joypad.set{down = true} end)
elseif targetDirection == west then
HoldThroughLag(function() joypad.set{left = true} end)
end
else
HoldForNFrames(x, y, 2)
WaitNFrames(1)
end
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
WaitUntilOtherState(44)
else
HoldUntilOtherState(touchX, touchY, 2, 44)
end
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
-- SetSaveState(true, 8)
-- SetMinimiseHold(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)
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)
SetNextAttackDelay(fastDelay)
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)
SetNextAttackDelay(fastDelay)
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)
--]]
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, south)
MoveAndAttack(Alph, Eman, 13, 13, 3, 3)
MoveAndAttack(Lucia, Bruneed, 12, 13, 3, 3, -4, 0)
MoveAndAttack(Alph, Bruneed, 13, 14, 3, 3, -4, 0)
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()
--OldMap1Strat()
--Map1Strat()
--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)