SHOW:
|
|
- or go back to the newest paste.
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 initialize() | |
33 | gui.defaultPixelFont("fceux") | |
34 | - | local x = bit.band(memory.readbyte(0x0A,"WRAM"),0x40) |
34 | + | |
35 | - | if (x > 0) then return true end |
35 | + | |
36 | - | local y = bit.band(memory.readbyte(0x0B,"WRAM"),0x40) |
36 | + | |
37 | - | if (y > 0) then return true end |
37 | + | |
38 | espers = { 0x26F6E1, 8, 54 }, | |
39 | attacks = { 0x26F7B9, 10, 81 }, | |
40 | espattacks = { 0x26FE8F, 10, 256 }, | |
41 | dances = { 0x26FF9D, 12, 283 }, | |
42 | } | |
43 | offMonsterData = 0xF0000 | |
44 | lenMonsterData = 32 | |
45 | offItemNames = 0x12B300 | |
46 | lenItemNames = 13 | |
47 | if (MOD_TYPE == "ROTDS") then | |
48 | offNames.spells = { 0x316100, 9, 0 } | |
49 | offNames.espers = { 0x3162E6, 12, 54 } | |
50 | offNames.attacks = { 0x31645E, 16, 81 } | |
51 | offNames.espattacks = { 0x316DDE, 16, 256 } | |
52 | offNames.dances = { 0x3170FE, 13, 283 } | |
53 | lenItemNames = 13 | |
54 | end | |
55 | end | |
56 | initialize() | |
57 | ||
58 | -- maps each font byte to the appropriate character, FF should break loop, space for anything else | |
59 | 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] = ".", } | |
60 | ||
61 | local monSpecials = { | |
62 | [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%", | |
63 | } | |
64 | function hexformat(input) return string.format("%02x", input):upper() end | |
65 | function trim(out) return (out:gsub("^%s*(.-)%s*$", "%1")) end | |
66 | -- skipFF to anything to prevent breaking on FF | |
67 | function translate(byteTable,skipFF) | |
68 | local out = "" | |
69 | local start = 0 | |
70 | if (not byteTable[start]) then start = 1 end | |
71 | for i=start,#byteTable do | |
72 | local hex = byteTable[i] | |
73 | if ((hex == 0xFF or not hex) and not skipFF) then break end | |
74 | if (fontMap[hex]) then | |
75 | out = out .. fontMap[hex] | |
76 | else | |
77 | out = out .. " " | |
78 | end | |
79 | end | |
80 | out = trim(out) | |
81 | return out | |
82 | end | |
83 | function getStatuses(byteA,byteB,byteC) | |
84 | local statuses = { {"Blind", "Zombi", "Poisn", "M-Tek", "Invis", "Imp ", "Stone", "Death"}, | |
85 | {"Doom ", "Hurt ", "Image", "Mute ", "Brsrk", "Muddl", "Sap ", "Sleep"}, | |
86 | {"Float", "Regen", "Slow ", "Haste", "Stop ", "Shell", "Safe ", "Rflct"} } | |
87 | local outStatuses = {} | |
88 | for _,byte in pairs({ byteA, byteB, byteC }) do | |
89 | for i=1,#statuses[_] do | |
90 | if (bit.band(byte,bit.lshift(1,(i-1))) > 0) then | |
91 | outStatuses[#outStatuses+1] = statuses[_][i] | |
92 | end | |
93 | end | |
94 | end | |
95 | local out = "" | |
96 | for _,v in pairs(outStatuses) do | |
97 | out = out .. v .. " " | |
98 | if (_ % 6 == 0) then out = out .. "\n " end | |
99 | end | |
100 | return trim(out) | |
101 | end | |
102 | function getElements(byte) | |
103 | local elements = { "FIR", "ICE", "THN", "POI", "WND", "HLY", "ERT", "WTR" } | |
104 | local outElements = {} | |
105 | for i=1,#elements do | |
106 | if (bit.band(byte,bit.lshift(1,(i-1))) > 0) then | |
107 | outElements[#outElements+1] = elements[i] | |
108 | end | |
109 | end | |
110 | local out = "" | |
111 | for _,v in pairs(outElements) do | |
112 | out = out .. v .. " " | |
113 | --if (_ % 5 == 0) then out = out .. "\n" end | |
114 | end | |
115 | return trim(out) | |
116 | end | |
117 | function getSpellName(index) | |
118 | local type = nil | |
119 | if (index >= offNames.dances[3]) then type = offNames.dances | |
120 | elseif (index >= offNames.espattacks[3]) then type = offNames.espattacks | |
121 | elseif (index >= offNames.attacks[3]) then type = offNames.attacks | |
122 | elseif (index >= offNames.espers[3]) then type = offNames.espers | |
123 | else type = offNames.spells | |
124 | end | |
125 | local beginOffset = type[1] | |
126 | local textLength = type[2] | |
127 | local startSkillIndex = type[3] | |
128 | local firstByte = beginOffset + ((index-startSkillIndex)*textLength) | |
129 | local out = memory.readbyterange(firstByte,textLength,"CARTROM") | |
130 | out = translate(out) | |
131 | return out | |
132 | end | |
133 | function getItemName(index) | |
134 | local out = memory.readbyterange(offItemNames + (index*lenItemNames),lenItemNames,"CARTROM") | |
135 | out = translate(out,"don't-break-on-FF") | |
136 | if (out == "") then out = "(None)" end | |
137 | return out | |
138 | end | |
139 | local cursor_state = nil | |
140 | local inputs = { { nil, nil, nil, nil, "R", "L", "X", "A", }, | |
141 | { "Right", "Left", "Down", "Up", "Start", "Select", "Y", "B" } } | |
142 | local keymap = {} | |
143 | local keymapLastFrame = {} | |
144 | ||
145 | function buttonHandler() | |
146 | -- X is at 0x0A as the 7th bit (0x40), Y is at 0x0B same | |
147 | local input1 = memory.readbyte(0x04,"WRAM") | |
148 | local input2 = memory.readbyte(0x05,"WRAM") | |
149 | if (input1 == 0x00 and input2 == 0x00) then | |
150 | input1 = memory.readbyte(0x0A) | |
151 | input2 = memory.readbyte(0x0B) | |
152 | end | |
153 | keymapLastFrame = keymap | |
154 | keymap = {} | |
155 | for _,byte in pairs({ input1, input2 }) do | |
156 | for i=1,#inputs[_] do | |
157 | if (inputs[_][i] ~= nil) then | |
158 | if (bit.band(byte,bit.lshift(1,(i-1))) > 0) then | |
159 | keymap[inputs[_][i]] = true | |
160 | end | |
161 | end | |
162 | end | |
163 | end | |
164 | end | |
165 | ||
166 | function checkIfHelpButton() | |
167 | -- X is at 0x0A as the 7th bit (0x40), Y is at 0x0B same | |
168 | if (keymap["X"] or keymap["Y"] or keymapLastFrame["X"] or keymapLastFrame["Y"]) then return true end | |
169 | return false | |
170 | end | |
171 | ||
172 | local enabledToggle = true | |
173 | local messageStartTime = 0 | |
174 | local messageText = "" | |
175 | local messageDuration = 0 | |
176 | function disabledCheck() | |
177 | if (keymap["Select"] and keymap["R"] and not (keymapLastFrame["R"] and keymapLastFrame["Select"])) then | |
178 | enabledToggle = not enabledToggle | |
179 | if (enabledToggle) then message("Overlay enabled.",4) | |
180 | else message("Overlay disabled.", 4) | |
181 | end | |
182 | end | |
183 | end | |
184 | function message(msg,duration) | |
185 | messageStartTime = os.time() | |
186 | messageText = msg | |
187 | messageDuration = duration | |
188 | end | |
189 | function messageCheck() | |
190 | messageTime = os.time() | |
191 | if (messageTime < (messageStartTime + messageDuration)) then | |
192 | gui.drawText(0,210,messageText,"white","black") | |
193 | end | |
194 | end | |
195 | function processEnemySpecial(idxTargetMonster) | |
196 | local monStats = memory.readbyterange(offMonsterData + (lenMonsterData * idxTargetMonster),lenMonsterData,"CARTROM") | |
197 | local specialAttack = monStats[0x1F] | |
198 | local noEvade = (bit.band(specialAttack,0x80) > 0) | |
199 | local noDamage = (bit.band(specialAttack,0x40) > 0) | |
200 | specialAttack = bit.band(specialAttack,0x3F) | |
201 | specialAttack = monSpecials[specialAttack] | |
202 | if (specialAttack) then | |
203 | if (noEvade or noDamage) then | |
204 | specialAttack = specialAttack..")\n(Special:" | |
205 | if (noEvade) then specialAttack = specialAttack .. "Can't dodge" end | |
206 | if (noEvade and noDamage) then specialAttack = specialAttack .. " & " end | |
207 | if (noDamage) then specialAttack = specialAttack .. "No damage" end | |
208 | end | |
209 | else | |
210 | specialAttack = "+???" | |
211 | end | |
212 | return "(Special:Attack"..specialAttack..")" | |
213 | end | |
214 | local hintLoc = { x = 8, y = 142, height = 9 } | |
215 | function printHint(msg) | |
216 | local _, extraLineCount = msg:gsub('\n', '\n') | |
217 | gui.pixelText(hintLoc.x,hintLoc.y-(hintLoc.height * extraLineCount),msg,"white") | |
218 | end | |
219 | function doStealStuff(targetted_monster) | |
220 | local stealLoc = 0x3310 + (2*targetted_monster) | |
221 | local rare_steal = memory.read_u8(stealLoc) | |
222 | local common_steal = memory.read_u8(stealLoc+1) | |
223 | local itemNames = { getItemName(common_steal), getItemName(rare_steal) } | |
224 | if (common_steal == 0xFF) then itemNames[1] = "---" end | |
225 | if (rare_steal == 0xFF) then itemNames[2] = "---" end | |
226 | local out = "Nothing to steal!" | |
227 | if (itemNames[1] ~= "---" or itemNames[2] ~= "---") then | |
228 | out = "Common Steal (7/8): "..itemNames[1].."\n Rare Steal (1/8): "..itemNames[2] | |
229 | end | |
230 | return out | |
231 | end | |
232 | function main() | |
233 | disabledCheck() | |
234 | messageCheck() | |
235 | if (not enabledToggle) then return end | |
236 | ||
237 | ||
238 | local paused = memory.read_u8(0x62ab) | |
239 | if (paused > 0) then | |
240 | -- should probably get a check so it ONLY works in battle | |
241 | --[[ | |
242 | -- show enemy health if paused | |
243 | for i=0,5 do | |
244 | local hp = memory.read_u16_le(0x3bfc + (2*i)) | |
245 | if (hp > 0) then | |
246 | local x = memory.read_u16_le(0x80c3 + (2*i)) -- monster loc x in pixels | |
247 | local y = memory.read_u16_le(0x80cf + (2*i)) -- monster loc y in pixels | |
248 | gui.drawText(x,y,hp) | |
249 | end | |
250 | end | |
251 | ]]-- | |
252 | end | |
253 | ||
254 | -- reads memory of 'cursor state' which appears to be the type of command being hovered over | |
255 | local last_cursor_state = cursor_state | |
256 | cursor_state = memory.read_u8(0x7bc2) | |
257 | local which_character = memory.read_u8(0x62ca) -- offsets cursor info | |
258 | local idxRecentCommand = memory.read_u8(0x890F + which_character) -- the command we're hovering on main menu | |
259 | local recentCommandType = memory.read_u8(0x202E + (12*which_character) + (3*idxRecentCommand)) | |
260 | --23 = scrolling page up or down | |
261 | if (cursor_state == 23 or cursor_state == 24) then cursor_state = last_cursor_state end | |
262 | --30 = rage | |
263 | if (cursor_state == 30) then | |
264 | local rages = memory.readbyterange(0x257E,256) | |
265 | local battleMenuColumn = 0x892F -- row and scrolled are derived from this (+4/-4) | |
266 | local column = memory.read_u8(battleMenuColumn + which_character) | |
267 | local row = memory.read_u8(battleMenuColumn + which_character + 4) | |
268 | local scrolled = memory.read_u8(battleMenuColumn + which_character - 4) | |
269 | local hoverRage = rages[((row+scrolled)*2) + column] | |
270 | local menuClosing = (memory.read_u8(0x7BCB) > 0) -- get rid of little text-blink as rage menu closes | |
271 | if (hoverRage < 255 and not menuClosing) then | |
272 | local rageLoc = 0xF4600 + (2*hoverRage) | |
273 | local rageAttacks = { memory.readbyte(rageLoc,"CARTROM"), memory.readbyte(rageLoc+1,"CARTROM") } | |
274 | for i=1,2 do rageAttacks[i] = getSpellName(rageAttacks[i]) end --.."("..rageAttacks[i]..")" end | |
275 | local monStats = memory.readbyterange(offMonsterData + (lenMonsterData * hoverRage),lenMonsterData,"CARTROM") | |
276 | local out = "Rage: "..rageAttacks[1] .. "/" .. rageAttacks[2] | |
277 | if (rageAttacks[2] == "Special" or rageAttacks[1] == "Special") then | |
278 | specialAttack = processEnemySpecial(hoverRage) | |
279 | out = specialAttack .. "\n"..out | |
280 | end | |
281 | printHint(out) | |
282 | if (checkIfHelpButton()) then | |
283 | local blockedStatuses = { monStats[0x14], monStats[0x15], monStats[0x16] } | |
284 | local appliedStatuses = { monStats[0x1B], monStats[0x1C], monStats[0x1D] } | |
285 | local absorbElements = monStats[0x17] | |
286 | local immuneElements = monStats[0x18] | |
287 | local weakElements = monStats[0x19] | |
288 | local bytes = "" | |
289 | for i=0,#monStats do | |
290 | bytes = bytes .. " " .. hexformat(monStats[i]) | |
291 | if ((i+1) % 8 == 0) then bytes = bytes .. "\n" end | |
292 | end | |
293 | local infoText = "" | |
294 | blockedStatuses = getStatuses(blockedStatuses[1],blockedStatuses[2],blockedStatuses[3]) | |
295 | appliedStatuses = getStatuses(appliedStatuses[1],appliedStatuses[2],appliedStatuses[3]) | |
296 | if (#appliedStatuses > 0) then infoText = infoText.."GAINS:"..appliedStatuses.."\n" end | |
297 | if (#blockedStatuses > 0) then infoText = infoText.. "BLOCK:"..blockedStatuses.."\n" end | |
298 | absorbElements = getElements(absorbElements) | |
299 | immuneElements = getElements(immuneElements) | |
300 | weakElements = getElements(weakElements) | |
301 | if (#absorbElements > 0) then infoText = infoText.. "ABS:"..absorbElements.."\n" end | |
302 | if (#immuneElements > 0) then infoText = infoText.. "IMM:"..immuneElements.."\n" end | |
303 | if (#weakElements > 0) then infoText = infoText.. "WEAK:"..weakElements.."\n" end | |
304 | infoText = trim(infoText) | |
305 | gui.pixelText(0,0,string.format("%-99s",infoText),"white") | |
306 | end | |
307 | end | |
308 | -- dances | |
309 | elseif (cursor_state == 33) then | |
310 | local dances = memory.readbyterange(0x267E,8) | |
311 | local battleMenuColumn = 0x8937 -- row and scrolled are derived from this (+4/-4) | |
312 | local column = memory.read_u8(battleMenuColumn + which_character) | |
313 | local row = memory.read_u8(battleMenuColumn + which_character + 4) | |
314 | local scrolled = memory.read_u8(battleMenuColumn + which_character - 4) | |
315 | ||
316 | local hoverDance = dances[((row+scrolled)*2) + column] | |
317 | local menuClosing = (memory.read_u8(0x7BCB) > 0) -- get rid of little text-blink as dance menu closes | |
318 | if (hoverDance < 255 and not menuClosing) then | |
319 | local danceLoc = 0xFFE80 + (4*hoverDance) | |
320 | local danceAttacks = { memory.readbyte(danceLoc,"CARTROM"), memory.readbyte(danceLoc+1,"CARTROM"), | |
321 | memory.readbyte(danceLoc+2,"CARTROM"), memory.readbyte(danceLoc+3,"CARTROM") } | |
322 | for i=1,4 do danceAttacks[i] = getSpellName(danceAttacks[i]) end | |
323 | local danceChances = { 7, 6, 2, 1 } | |
324 | if (MOD_TYPE == "BNW") then danceChances = { 7, 5, 3, 1 } end | |
325 | 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]) | |
326 | printHint(out) | |
327 | end | |
328 | -- targetting enemies | |
329 | elseif (cursor_state == 0x38) then | |
330 | local targetted_monster = memory.read_u8(0x7B7E) | |
331 | -- only bother if we're actually targetting a monster (ie not an ally) | |
332 | if (targetted_monster > 0) then | |
333 | targetted_monster = (math.log(targetted_monster) / math.log(2)) | |
334 | if (recentCommandType == 0x00) then -- fight | |
335 | local weaponPropLoc = 0x3CBC --main, then off | |
336 | weaponPropLoc = weaponPropLoc + (2*which_character) | |
337 | local my_weapons = { memory.read_u8(weaponPropLoc), memory.read_u8(weaponPropLoc + 1) } | |
338 | -- the offhand will be shortcircuited if first passes | |
339 | my_weapons[1] = (bit.band(my_weapons[1],0x10) > 0 and my_weapons[1] < 32) | |
340 | if (my_weapons[1] or (bit.band(my_weapons[2],0x10) > 0 and my_weapons[2] < 32)) then | |
341 | local out = doStealStuff(targetted_monster) | |
342 | local weaponLoc = 0x3CA8 | |
343 | weaponLoc = weaponLoc + (2*which_character) | |
344 | local providingWeapon = "ThiefKnife" | |
345 | if (my_weapons[1]) then | |
346 | providingWeapon = getItemName(memory.read_u8(weaponLoc)) | |
347 | else -- use off-hand since it wasn't from mainhand | |
348 | providingWeapon = getItemName(memory.read_u8(weaponLoc + 1)) | |
349 | end | |
350 | printHint("* Shown due to "..providingWeapon..":\n"..out) | |
351 | end | |
352 | elseif (recentCommandType == 0x05 or recentCommandType == 0x06) then -- we're stealing and targetting enemies | |
353 | printHint(doStealStuff(targetted_monster)) | |
354 | elseif (recentCommandType == 0x0D) then --sketch | |
355 | local idxTargetMonster = memory.read_u16_le(0x2001 + (2*targetted_monster)) | |
356 | local cantSketchByte = memory.readbyte(offMonsterData + (lenMonsterData * idxTargetMonster) + 0x13,"CARTROM") | |
357 | if (bit.band(cantSketchByte,0x20) > 0) then | |
358 | printHint("Can't sketch!") | |
359 | else | |
360 | local sketchLoc = 0xF4300 + (2*idxTargetMonster) | |
361 | local sketchAttacks = { memory.readbyte(sketchLoc,"CARTROM"), memory.readbyte(sketchLoc+1,"CARTROM") } | |
362 | for i=1,2 do sketchAttacks[i] = getSpellName(sketchAttacks[i]) end | |
363 | local out = "Sketch: (75%) "..sketchAttacks[2] .. "\n (25%) " .. sketchAttacks[1] .."" | |
364 | if (sketchAttacks[2] == "Special" or sketchAttacks[1] == "Special") then | |
365 | specialAttack = processEnemySpecial(idxTargetMonster) | |
366 | out = specialAttack.."\n"..out | |
367 | end | |
368 | printHint(out) | |
369 | end | |
370 | elseif (recentCommandType == 0x0E) then -- chaos control | |
371 | local idxTargetMonster = memory.read_u16_le(0x2001 + (2*targetted_monster)) | |
372 | local cantControlByte = memory.readbyte(offMonsterData + (lenMonsterData * idxTargetMonster) + 0x13,"CARTROM") | |
373 | if (bit.band(cantControlByte,0x80) > 0) then | |
374 | printHint("Can't control!") | |
375 | else | |
376 | local controlLoc = 0xF3D00 + (4*idxTargetMonster) | |
377 | local controlAttacks = { memory.readbyte(controlLoc,"CARTROM"), memory.readbyte(controlLoc+1,"CARTROM"), memory.readbyte(controlLoc+2,"CARTROM"), memory.readbyte(controlLoc+3,"CARTROM") } | |
378 | local hasSpecial = false | |
379 | local namesControlAttacks = {} | |
380 | for i=1,4 do | |
381 | if (controlAttacks[i] ~= 0xFF) then | |
382 | namesControlAttacks[#namesControlAttacks + 1] = getSpellName(controlAttacks[i]) | |
383 | if (namesControlAttacks[#namesControlAttacks] == "Special") then hasSpecial = true end | |
384 | if (#namesControlAttacks == 3) then namesControlAttacks[3] = "\n "..namesControlAttacks[3] end | |
385 | end | |
386 | end | |
387 | local out = "Control: "..table.concat(namesControlAttacks,", ") | |
388 | if (hasSpecial) then | |
389 | specialAttack = processEnemySpecial(idxTargetMonster) | |
390 | out = specialAttack.."\n"..out | |
391 | end | |
392 | printHint(out) | |
393 | end | |
394 | end | |
395 | end | |
396 | end | |
397 | end | |
398 | ||
399 | while true do | |
400 | emu.frameadvance(); | |
401 | -- prevent it from doing dumb things while rom isn't even loaded | |
402 | if (romname ~= "Null") then | |
403 | buttonHandler() | |
404 | main() | |
405 | end | |
406 | end |