Advertisement
Guest User

FF6 BizHawk LUA Info Overlay

a guest
Jan 26th, 2020
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.35 KB | None | 0 0
  1. -- Used with "BizHawk" emulator - load ROM -> Tools -> Lua Console -> Script -> Open Script -> double-click until green play sign
  2.  
  3. -- Enable or disable the overlay with [R+SELECT]. Haven't played with this yet, so
  4. -- you may end up seeing overlay popups during the normal game at odd times.
  5.  
  6. -- mod type for offsets, works off of the 'rom name' (what it says in title bar/saves games as)
  7. -- if your rom isn't detected just add it in this format (including the , and any " marks):
  8. -- ["ROM NAME"] = romtype,
  9. -- where romtype is one of these: nil, "ROTDS", "BNW"
  10. -- any other romtype will use nil (vanilla) settings
  11.  
  12. local MODS = {
  13.  
  14. ["Final Fantasy III (USA)"]     = nil,     -- vanilla, default. -- means comments
  15. ["Return of the Dark Sorcerer"] = "ROTDS", -- uses rotds extended item/spell names
  16. ["Brave New World 2.0"]         = "BNW",   -- mod'd dance chance
  17. ["FFVI T Edition"]              = "japan", -- nothing yet, the version is stripped further down
  18.  
  19. }
  20.  
  21. --- =======================
  22. --- all settings are above this
  23. --- =======================
  24.  
  25.  
  26.  
  27. local romname = gameinfo.getromname()
  28.  
  29. -- t-edition has ever-changing patch number on the end
  30. if (romname:sub(1,14) == "FFVI T Edition") then romname = "FFVI T Edition" end
  31. local MOD_TYPE = MODS[romname]
  32. function checkIfHelpButton()
  33.     -- X is at 0x0A as the 7th bit (0x40), Y is at 0x0B same
  34.     local x = bit.band(memory.readbyte(0x0A,"WRAM"),0x40)
  35.     if (x > 0) then return true end
  36.     local y = bit.band(memory.readbyte(0x0B,"WRAM"),0x40)
  37.     if (y > 0) then return true end
  38.     return false
  39. end
  40. function initialize()
  41.      gui.defaultPixelFont("fceux")
  42.     -- vanilla
  43. -- offset start, text length, index start
  44.     offNames = {
  45.         spells = { 0x26F567, 7, 0 },
  46.         espers = { 0x26F6E1, 8, 54 },
  47.        attacks = { 0x26F7B9, 10, 81 },
  48.     espattacks = { 0x26FE8F, 10, 256 },
  49.         dances = { 0x26FF9D, 12, 283 },
  50. }
  51.     offMonsterData = 0xF0000
  52.     lenMonsterData = 32
  53.     offItemNames = 0x12B300
  54.     lenItemNames = 13
  55.     if (MOD_TYPE == "ROTDS") then
  56.         offNames.spells = { 0x316100, 9, 0 }
  57.         offNames.espers = { 0x3162E6, 12, 54 }
  58.         offNames.attacks = { 0x31645E, 16, 81 }
  59.         offNames.espattacks = { 0x316DDE, 16, 256 }
  60.         offNames.dances = { 0x3170FE, 13, 283 }
  61.         lenItemNames = 13
  62.     end
  63. end
  64. initialize()
  65.  
  66. -- maps each font byte to the appropriate character, FF should break loop, space for anything else
  67. local fontMap = { [0x80] = "A", [0x81] = "B", [0x82] = "C", [0x83] = "D", [0x84] = "E", [0x85] = "F", [0x86] = "G", [0x87] = "H", [0x88] = "I", [0x89] = "J", [0x8A] = "K", [0x8B] = "L", [0x8C] = "M", [0x8D] = "N", [0x8E] = "O", [0x8F] = "P", [0x90] = "Q", [0x91] = "R", [0x92] = "S", [0x93] = "T", [0x94] = "U", [0x95] = "V", [0x96] = "W", [0x97] = "X", [0x98] = "Y", [0x99] = "Z", [0x9A] = "a", [0x9B] = "b", [0x9C] = "c", [0x9D] = "d", [0x9E] = "e", [0x9F] = "f", [0xA0] = "g", [0xA1] = "h", [0xA2] = "i", [0xA3] = "j", [0xA4] = "k", [0xA5] = "l", [0xA6] = "m", [0xA7] = "n", [0xA8] = "o", [0xA9] = "p", [0xAA] = "q", [0xAB] = "r", [0xAC] = "s", [0xAD] = "t", [0xAE] = "u", [0xAF] = "v", [0xB0] = "w", [0xB1] = "x", [0xB2] = "y", [0xB3] = "z", [0xB4] = "0", [0xB5] = "1", [0xB6] = "2", [0xB7] = "3", [0xB8] = "4", [0xB9] = "5", [0xBA] = "6", [0xBB] = "7", [0xBC] = "8", [0xBD] = "9", [0xBE] = "!", [0xBF] = "?", [0xC3] = "'", [0xC4] = "-", [0xC5] = ".", }
  68.  
  69. local monSpecials = {
  70. [0x00]="+Darkness", [0x01]="+Zombie", [0x02]="+Poison", [0x03]="+Magitek", [0x04]="+Invis", [0x05]="+Imp", [0x06]="+Stone", [0x07]="+Death", [0x08]="+Doom", [0x09]="+Critical", [0x0A]="+Blink", [0x0B]="+Silence", [0x0C]="+Berserk", [0x0D]="+Confuse", [0x0E]="+Sap", [0x0F]="+Sleep", [0x10]="+Dance", [0x11]="+Regen", [0x12]="+Slow", [0x13]="+Haste", [0x14]="+Stop", [0x15]="+Shell", [0x16]="+Protect", [0x17]="+Reflect", [0x18]="+Rage", [0x19]="+Frozen", [0x1A]="+Reraise", [0x1B]="+Morph", [0x1C]="+Spellcast", [0x1D]="+Flee", [0x1E]="+Interceptor", [0x1F]="+Float", [0x20]=" Damage + 50%", [0x21]=" Damage +100%", [0x22]=" Damage +150%", [0x23]=" Damage +200%", [0x24]=" Damage +250%", [0x25]=" Damage +300%", [0x26]=" Damage +350%", [0x27]=" Damage +400%", [0x28]=" Damage +450%", [0x29]=" Damage +500%", [0x2A]=" Damage +550%", [0x2B]=" Damage +600%", [0x2C]=" Damage +650%", [0x2D]=" Damage +700%", [0x2E]=" Damage +750%", [0x2F]=" Damage +800%",
  71. }
  72. function hexformat(input) return string.format("%02x", input):upper() end
  73. function trim(out) return (out:gsub("^%s*(.-)%s*$", "%1")) end
  74. -- skipFF to anything to prevent breaking on FF
  75. function translate(byteTable,skipFF)
  76.     local out = ""
  77.     local start = 0
  78.     if (not byteTable[start]) then start = 1 end
  79.     for i=start,#byteTable do
  80.         local hex = byteTable[i]
  81.         if ((hex == 0xFF or not hex) and not skipFF) then break end
  82.         if (fontMap[hex]) then
  83.             out = out .. fontMap[hex]
  84.         else
  85.             out = out .. " "
  86.         end
  87.     end
  88.     out = trim(out)
  89.     return out
  90. end
  91. function getStatuses(byteA,byteB,byteC)
  92.     local statuses = { {"Blind", "Zombi", "Poisn", "M-Tek", "Invis", "Imp  ", "Stone", "Death"},
  93.                        {"Doom ", "Hurt ", "Image", "Mute ", "Brsrk", "Muddl", "Sap  ", "Sleep"},
  94.                        {"Float", "Regen", "Slow ", "Haste", "Stop ", "Shell", "Safe ", "Rflct"} }
  95.     local outStatuses = {}
  96.     for _,byte in pairs({ byteA, byteB, byteC }) do
  97.         for i=1,#statuses[_] do
  98.             if (bit.band(byte,bit.lshift(1,(i-1))) > 0) then
  99.                 outStatuses[#outStatuses+1] = statuses[_][i]
  100.             end
  101.         end
  102.     end
  103.     local out = ""
  104.     for _,v in pairs(outStatuses) do
  105.         out = out .. v .. " "
  106.         if (_ % 6 == 0) then out = out .. "\n      " end
  107.     end
  108.     return trim(out)
  109. end
  110. function getElements(byte)
  111.     local elements = { "FIR", "ICE", "THN", "POI", "WND", "HLY", "ERT", "WTR" }
  112.     local outElements = {}
  113.     for i=1,#elements do
  114.         if (bit.band(byte,bit.lshift(1,(i-1))) > 0) then
  115.             outElements[#outElements+1] = elements[i]
  116.         end
  117.     end
  118.     local out = ""
  119.     for _,v in pairs(outElements) do
  120.         out = out .. v .. " "
  121.         --if (_ % 5 == 0) then out = out .. "\n" end
  122.     end
  123.     return trim(out)
  124. end
  125. function getSpellName(index)
  126.     local type = nil
  127.     if (index >= offNames.dances[3]) then type = offNames.dances
  128.     elseif (index >= offNames.espattacks[3]) then type = offNames.espattacks
  129.     elseif (index >= offNames.attacks[3]) then type = offNames.attacks
  130.     elseif (index >= offNames.espers[3]) then type = offNames.espers
  131.     else type = offNames.spells
  132.     end
  133.     local beginOffset = type[1]
  134.     local textLength = type[2]
  135.     local startSkillIndex = type[3]
  136.     local firstByte = beginOffset + ((index-startSkillIndex)*textLength)
  137.     local out = memory.readbyterange(firstByte,textLength,"CARTROM")
  138.     out = translate(out)
  139.     return out
  140. end
  141. function getItemName(index)
  142.     local out = memory.readbyterange(offItemNames + (index*lenItemNames),lenItemNames,"CARTROM")
  143.     out = translate(out,"don't-break-on-FF")
  144.     if (out == "") then out = "(None)" end
  145.     return out
  146. end
  147. local cursor_state = nil
  148. local inputs = { { nil, nil, nil, nil, "R", "L", "X", "A", },
  149.                  { "Right", "Left", "Down", "Up", "Start", "Select", "Y", "B" } }
  150. local keymap = {}
  151. local keymapLastFrame = {}
  152.  
  153. function buttonHandler()
  154.     -- X is at 0x0A as the 7th bit (0x40), Y is at 0x0B same
  155.     local input1 = memory.readbyte(0x04,"WRAM")
  156.     local input2 = memory.readbyte(0x05,"WRAM")
  157.     if (input1 == 0x00 and input2 == 0x00) then
  158.         input1 = memory.readbyte(0x0A)
  159.         input2 = memory.readbyte(0x0B)
  160.     end
  161.     keymapLastFrame = keymap
  162.     keymap = {}
  163.     for _,byte in pairs({ input1, input2 }) do
  164.         for i=1,#inputs[_] do
  165.             if (inputs[_][i] ~= nil) then
  166.                 if (bit.band(byte,bit.lshift(1,(i-1))) > 0) then
  167.                     keymap[inputs[_][i]] = true
  168.                 end
  169.             end
  170.         end
  171.     end
  172. end
  173. local enabledToggle = true
  174. local messageStartTime = 0
  175. local messageText = ""
  176. local messageDuration = 0
  177. function disabledCheck()
  178.     if (keymap["Select"] and keymap["R"] and not (keymapLastFrame["R"] and keymapLastFrame["Select"])) then
  179.         enabledToggle = not enabledToggle
  180.         if (enabledToggle) then message("Overlay enabled.",4)
  181.         else message("Overlay disabled.", 4)       
  182.         end
  183.     end
  184. end
  185. function message(msg,duration)
  186.     messageStartTime = os.time()
  187.     messageText = msg
  188.     messageDuration = duration
  189. end
  190. function messageCheck()
  191.     messageTime = os.time()
  192.     if (messageTime < (messageStartTime + messageDuration)) then
  193.         gui.drawText(0,210,messageText,"white","black")
  194.     end
  195. end
  196. function processEnemySpecial(idxTargetMonster)
  197.     local monStats = memory.readbyterange(offMonsterData + (lenMonsterData * idxTargetMonster),lenMonsterData,"CARTROM")
  198.     local specialAttack = monStats[0x1F]
  199.     local noEvade = (bit.band(specialAttack,0x80) > 0)
  200.     local noDamage = (bit.band(specialAttack,0x40) > 0)
  201.     specialAttack = bit.band(specialAttack,0x3F)
  202.     specialAttack = monSpecials[specialAttack]
  203.     if (specialAttack) then
  204.         if (noEvade or noDamage) then
  205.             specialAttack = specialAttack..")\n(Special:"
  206.             if (noEvade) then specialAttack = specialAttack .. "Can't dodge" end
  207.             if (noEvade and noDamage) then specialAttack = specialAttack .. " & " end
  208.             if (noDamage) then specialAttack = specialAttack .. "No damage" end
  209.         end
  210.     else
  211.         specialAttack = "+???"
  212.     end
  213.     return "(Special:Attack"..specialAttack..")"
  214. end
  215. local hintLoc = { x = 8, y = 142, height = 9 }
  216. function printHint(msg)
  217.     local _, extraLineCount = msg:gsub('\n', '\n')
  218.     gui.pixelText(hintLoc.x,hintLoc.y-(hintLoc.height * extraLineCount),msg,"white")
  219. end
  220. function doStealStuff(targetted_monster)
  221.     local stealLoc = 0x3310 + (2*targetted_monster)
  222.     local rare_steal = memory.read_u8(stealLoc)
  223.     local common_steal = memory.read_u8(stealLoc+1)
  224.     local itemNames = { getItemName(common_steal), getItemName(rare_steal) }
  225.     if (common_steal == 0xFF) then itemNames[1] = "---" end
  226.     if (rare_steal   == 0xFF) then itemNames[2] = "---" end
  227.     local out = "Nothing to steal!"
  228.     if (itemNames[1] ~= "---" or itemNames[2] ~= "---") then
  229.         out = "Common Steal (7/8): "..itemNames[1].."\n  Rare Steal (1/8): "..itemNames[2]
  230.     end
  231.     return out
  232. end
  233. function main()
  234.     disabledCheck()
  235.     messageCheck()
  236.     if (not enabledToggle) then return end
  237.  
  238.  
  239.     local paused = memory.read_u8(0x62ab)
  240.     if (paused > 0) then
  241. -- should probably get a check so it ONLY works in battle
  242. --[[
  243.         -- show enemy health if paused
  244.         for i=0,5 do
  245.             local hp = memory.read_u16_le(0x3bfc + (2*i))
  246.             if (hp > 0) then
  247.                 local x = memory.read_u16_le(0x80c3 + (2*i)) -- monster loc x in pixels
  248.                 local y = memory.read_u16_le(0x80cf + (2*i)) -- monster loc y in pixels
  249.                 gui.drawText(x,y,hp)
  250.             end
  251.         end
  252. ]]--
  253.     end
  254.  
  255.     -- reads memory of 'cursor state' which appears to be the type of command being hovered over
  256.     local last_cursor_state = cursor_state
  257.     cursor_state = memory.read_u8(0x7bc2)
  258.     local which_character = memory.read_u8(0x62ca) -- offsets cursor info
  259.     local idxRecentCommand = memory.read_u8(0x890F + which_character) -- the command we're hovering on main menu
  260.     local recentCommandType = memory.read_u8(0x202E + (12*which_character) + (3*idxRecentCommand))
  261.     --23 = scrolling page up or down
  262.     if (cursor_state == 23 or cursor_state == 24) then cursor_state = last_cursor_state end
  263.     --30 = rage
  264.     if (cursor_state == 30) then
  265.         local rages = memory.readbyterange(0x257E,256)
  266.         local battleMenuColumn = 0x892F -- row and scrolled are derived from this (+4/-4)
  267.         local column = memory.read_u8(battleMenuColumn + which_character)
  268.         local row = memory.read_u8(battleMenuColumn + which_character + 4)
  269.         local scrolled = memory.read_u8(battleMenuColumn + which_character - 4)
  270.         local hoverRage = rages[((row+scrolled)*2) + column]
  271.         local menuClosing = (memory.read_u8(0x7BCB) > 0) -- get rid of little text-blink as rage menu closes
  272.         if (hoverRage < 255 and not menuClosing) then
  273.             local rageLoc = 0xF4600 + (2*hoverRage)
  274.             local rageAttacks = { memory.readbyte(rageLoc,"CARTROM"), memory.readbyte(rageLoc+1,"CARTROM") }
  275.             for i=1,2 do rageAttacks[i] = getSpellName(rageAttacks[i]) end --.."("..rageAttacks[i]..")" end
  276.             local monStats = memory.readbyterange(offMonsterData + (lenMonsterData * hoverRage),lenMonsterData,"CARTROM")
  277.             local out = "Rage: "..rageAttacks[1] .. "/" .. rageAttacks[2]
  278.             if (rageAttacks[2] == "Special" or rageAttacks[1] == "Special") then
  279.                 specialAttack = processEnemySpecial(hoverRage)
  280.                 out = specialAttack .. "\n"..out
  281.             end
  282.             printHint(out)
  283.             if (checkIfHelpButton()) then
  284.                 local blockedStatuses = { monStats[0x14], monStats[0x15], monStats[0x16] }
  285.                 local appliedStatuses = { monStats[0x1B], monStats[0x1C], monStats[0x1D] }
  286.                 local absorbElements = monStats[0x17]
  287.                 local immuneElements = monStats[0x18]
  288.                 local weakElements = monStats[0x19]
  289.                 local bytes = ""
  290.                 for i=0,#monStats do
  291.                     bytes = bytes .. " " .. hexformat(monStats[i])
  292.                     if ((i+1) % 8 == 0) then bytes = bytes .. "\n" end
  293.                 end
  294.                 local infoText = ""
  295.                 blockedStatuses = getStatuses(blockedStatuses[1],blockedStatuses[2],blockedStatuses[3])
  296.                 appliedStatuses = getStatuses(appliedStatuses[1],appliedStatuses[2],appliedStatuses[3])
  297.                 if (#appliedStatuses > 0) then infoText = infoText.."GAINS:"..appliedStatuses.."\n" end
  298.                 if (#blockedStatuses > 0) then infoText = infoText.. "BLOCK:"..blockedStatuses.."\n" end
  299.                 absorbElements = getElements(absorbElements)
  300.                 immuneElements = getElements(immuneElements)
  301.                 weakElements = getElements(weakElements)
  302.                 if (#absorbElements > 0) then infoText = infoText.. "ABS:"..absorbElements.."\n" end
  303.                 if (#immuneElements > 0) then infoText = infoText.. "IMM:"..immuneElements.."\n" end
  304.                 if (#weakElements > 0) then infoText = infoText.. "WEAK:"..weakElements.."\n" end
  305.                 infoText = trim(infoText)
  306.                 gui.pixelText(0,0,string.format("%-99s",infoText),"white")
  307.             end
  308.         end
  309.     -- dances
  310.     elseif (cursor_state == 33) then
  311.         local dances = memory.readbyterange(0x267E,8)
  312.         local battleMenuColumn = 0x8937 -- row and scrolled are derived from this (+4/-4)
  313.         local column = memory.read_u8(battleMenuColumn + which_character)
  314.         local row = memory.read_u8(battleMenuColumn + which_character + 4)
  315.         local scrolled = memory.read_u8(battleMenuColumn + which_character - 4)
  316.    
  317.         local hoverDance = dances[((row+scrolled)*2) + column]
  318.         local menuClosing = (memory.read_u8(0x7BCB) > 0) -- get rid of little text-blink as dance menu closes
  319.         if (hoverDance < 255 and not menuClosing) then
  320.             local danceLoc = 0xFFE80 + (4*hoverDance)
  321.             local danceAttacks = { memory.readbyte(danceLoc,"CARTROM"), memory.readbyte(danceLoc+1,"CARTROM"),
  322.                             memory.readbyte(danceLoc+2,"CARTROM"), memory.readbyte(danceLoc+3,"CARTROM") }
  323.             for i=1,4 do danceAttacks[i] = getSpellName(danceAttacks[i]) end
  324.             local danceChances = { 7, 6, 2, 1 }
  325.             if (MOD_TYPE == "BNW") then danceChances = { 7, 5, 3, 1 } end
  326.             local out = string.format("Dance: (%s/16) %s\n       (%s/16) %s\n       (%s/16) %s\n       (%s/16) %s", danceChances[1], danceAttacks[1], danceChances[2], danceAttacks[2], danceChances[3], danceAttacks[3], danceChances[4], danceAttacks[4])
  327.             printHint(out)
  328.         end
  329.     -- targetting enemies
  330.     elseif (cursor_state == 0x38) then
  331.         local targetted_monster = memory.read_u8(0x7B7E)
  332.         -- only bother if we're actually targetting a monster (ie not an ally)
  333.         if (targetted_monster > 0) then
  334.             targetted_monster = (math.log(targetted_monster) / math.log(2))
  335.             if (recentCommandType == 0x00) then -- fight
  336.                 local weaponPropLoc = 0x3CBC --main, then off
  337.                 weaponPropLoc = weaponPropLoc + (2*which_character)
  338.                 local my_weapons = { memory.read_u8(weaponPropLoc), memory.read_u8(weaponPropLoc + 1) }
  339.                 -- the offhand will be shortcircuited if first passes
  340.                 my_weapons[1] = (bit.band(my_weapons[1],0x10) > 0 and my_weapons[1] < 32)
  341.                 if (my_weapons[1] or (bit.band(my_weapons[2],0x10) > 0 and my_weapons[2] < 32)) then
  342.                     local out = doStealStuff(targetted_monster)
  343.                     local weaponLoc = 0x3CA8
  344.                     weaponLoc = weaponLoc + (2*which_character)
  345.                     local providingWeapon = "ThiefKnife"
  346.                     if (my_weapons[1]) then
  347.                         providingWeapon = getItemName(memory.read_u8(weaponLoc))
  348.                     else -- use off-hand since it wasn't from mainhand
  349.                         providingWeapon = getItemName(memory.read_u8(weaponLoc + 1))
  350.                     end
  351.                     printHint("* Shown due to "..providingWeapon..":\n"..out)
  352.                 end
  353.             elseif (recentCommandType == 0x05 or recentCommandType == 0x06) then -- we're stealing and targetting enemies
  354.                 printHint(doStealStuff(targetted_monster))
  355.             elseif (recentCommandType == 0x0D) then --sketch
  356.                 local idxTargetMonster = memory.read_u16_le(0x2001 + (2*targetted_monster))
  357.                 local cantSketchByte = memory.readbyte(offMonsterData + (lenMonsterData * idxTargetMonster) + 0x13,"CARTROM")
  358.                 if (bit.band(cantSketchByte,0x20) > 0) then
  359.                     printHint("Can't sketch!")
  360.                 else
  361.                     local sketchLoc = 0xF4300 + (2*idxTargetMonster)
  362.                     local sketchAttacks = { memory.readbyte(sketchLoc,"CARTROM"), memory.readbyte(sketchLoc+1,"CARTROM") }
  363.                     for i=1,2 do sketchAttacks[i] = getSpellName(sketchAttacks[i]) end
  364.                     local out = "Sketch: (75%) "..sketchAttacks[2] .. "\n        (25%) " .. sketchAttacks[1] ..""
  365.                     if (sketchAttacks[2] == "Special" or sketchAttacks[1] == "Special") then
  366.                         specialAttack = processEnemySpecial(idxTargetMonster)
  367.                         out = specialAttack.."\n"..out
  368.                     end
  369.                     printHint(out)
  370.                 end
  371.             elseif (recentCommandType == 0x0E) then -- chaos control
  372.                 local idxTargetMonster = memory.read_u16_le(0x2001 + (2*targetted_monster))
  373.                 local cantControlByte = memory.readbyte(offMonsterData + (lenMonsterData * idxTargetMonster) + 0x13,"CARTROM")
  374.                 if (bit.band(cantControlByte,0x80) > 0) then
  375.                     printHint("Can't control!")
  376.                 else
  377.                     local controlLoc = 0xF3D00 + (4*idxTargetMonster)
  378.                     local controlAttacks = { memory.readbyte(controlLoc,"CARTROM"), memory.readbyte(controlLoc+1,"CARTROM"), memory.readbyte(controlLoc+2,"CARTROM"), memory.readbyte(controlLoc+3,"CARTROM") }
  379.                     local hasSpecial = false
  380.                     local namesControlAttacks = {}
  381.                     for i=1,4 do
  382.                         if (controlAttacks[i] ~= 0xFF) then
  383.                             namesControlAttacks[#namesControlAttacks + 1] = getSpellName(controlAttacks[i])
  384.                             if (namesControlAttacks[#namesControlAttacks] == "Special") then hasSpecial = true end
  385.                             if (#namesControlAttacks == 3) then namesControlAttacks[3] = "\n         "..namesControlAttacks[3] end
  386.                         end
  387.                     end
  388.                     local out = "Control: "..table.concat(namesControlAttacks,", ")
  389.                     if (hasSpecial) then
  390.                         specialAttack = processEnemySpecial(idxTargetMonster)
  391.                         out = specialAttack.."\n"..out
  392.                     end
  393.                     printHint(out)
  394.                 end
  395.             end
  396.         end
  397.     end
  398. end
  399.  
  400. while true do
  401.     emu.frameadvance();
  402.     -- prevent it from doing dumb things while rom isn't even loaded
  403.     if (romname ~= "Null") then
  404.         buttonHandler()
  405.         main()
  406.     end
  407. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement