-- Features:
-- 1. Encounter prediction: predicts steps until encounter, and what will be encountered.
-- Does not predict front/back/pincer/side/preemptive variants, not will it work properly
-- on the floating continent. (there is tentative support for this (which actually makes up
-- a good part of the code), but it is buggy, and only works well when one is on the world map,
-- and has 3 or 4 chars in party, so it is disabled by default)
-- 2. Outline treasure chests and show their contents and status (green = untaken, red = taken)
-- 3. Outline squares that trigger events. Blue = transition to new area, light blue = transition
-- to same area, very light blue = transition to map, yellow = unclassified event.
-- 4. In battle, hp and mp bars are displayed over the enemies, as well as their battle gauges.
-- Their level is also displayed. On gau's turns, a "?" is displayed by any monster whose
-- rage is not known. Additionally, the enemies' resistances etc. are displayed in a compact
-- but hopefully readable manner, as a set of 4 3x3 sqares of pixels, where each has the
-- following format:
-- fil ; fire (red) ice (blue) lightning (yellow)
-- pwp ; poison (green) wind (gray) holy (white)
-- ew ; earth (brown) water (blue)
-- The order of the sqares is, from left to right, absorb, nullify, resist, weak.
-- 5. Extra information is displayed for the currently selected monster.
-- normally, only its drops and hp/mp are displayed, but if locke is selecting the
-- monster with the steal command, its steals are also listed. If relm is selecting
-- a monster with control or sketch, that information will also be indicated.
-- 6. While in the rage menu, information about what the selected rage does is
-- displayed. Only the attack part of the rage is displayed so far, since I haven't thought
-- of a compact way of displaying all the status information associated with a rage.
addHours = 0
addMinutes = 4
addSeconds = 9
addFrames = 21
textX = 160
textY = 120
lMode1 = memory.readword(0x7e1501)
lMode2 = memory.readbyte(0x7e1503)
lMode3 = memory.readbyte
cMode1 = memory.readword(0x7e1501)
cMode2 = memory.readbyte(0x7e1503)
autoText = false
drawpixel = function(x,y,color)
if x < 0 or y < 0 then return end
if x >= 0x100 or y >= 0xE0 then return end
gui.drawpixel(x,y,color)
end
-- the following section handles out of bounds checking.
-- This will hopefully not be necessary in later versions of snes9x-lua
-- The bounds checking for lines is slightly more complicated, so I did
-- not bother implementing it properly.
min = function(a,b) if a < b then return a else return b end end
max = function(a,b) if a < b then return b else return a end end
order = function(a,b) if a > b then return b, a else return a,b end end
olddrawbox = gui.drawbox
gui.drawbox = function(x1,y1,x2,y2,color)
x1,x2 = order(x1,x2)
y1,y2 = order(y1,y2)
x1 = min(max(0,x1),255)
y1 = min(max(0,y1),238)
x2 = min(max(0,x2),255)
y2 = min(max(0,y2),238)
olddrawbox(x1,y1,x2,y2,nil,color)
end
oldtext = gui.text
gui.text = function(x,y,text)
if x >= 0 and x < 256 and y >= 0 and y < 239 then
oldtext(x,y,text)
end
end
horline = function(x1,y,x2,color)
x1,x2 = order(x1,x2)
x1 = min(max(0,x1),255)
x2 = min(max(0,x2),255)
if y >= 0 and y < 239 then
gui.drawline(x1,y,x2,y,color)
end
end
vertline = function(x,y1,y2,color)
y1,y2 = order(y1,y2)
y1 = min(max(0,y1),238)
y2 = min(max(0,y2),238)
if x >= 0 and x < 256 then
gui.drawline(x,y1,x,y2,color)
end
end
startgauge = function(x,y,w)
gx, gy, gw = x, y, w
end
drawgauge = function(c,m,col)
if m == 0 then m = 1 end
local g = c*gw/m
horline(gx,gy,gx+g,col)
horline(gx+g,gy,gx+gw,"#000000")
gy = gy-1
end
tochar = function(i)
if i < 0x80 then return ""
elseif i < 0x9a then return string.char(string.byte("A")+i-0x80)
elseif i < 0xb4 then return string.char(string.byte("a")+i-0x9a)
elseif i < 0xbe then return string.char(string.byte("0")+i-0xb4)
elseif i == 0xfe then return " "
else return "" end
end
readsnesstring = function(from,maximum)
local res = ""
local tmp = memory.readbyte(from)
local i = 0
while tmp ~= 0xFF do
if i == maximum then break end
res = res .. tochar(tmp)
from = from+1
tmp = memory.readbyte(from)
i = i+1
end
return res
end
getitemname = function(id)
return readsnesstring(0xd2b300+0xd*id+1,12)
end
getmonstername = function(id)
return readsnesstring(0xcfc050+0xa*id,10)
end
getattackname = function(id)
local x = 0
local maxlen = 10
if id < 0x36 then
x = id*7
maxlen = 7
elseif id < 0x51 then
x = id*8-0x36
maxlen = 8
elseif id < 0x56 then x = id*0xA-0xd8
else x = id*0xA-0xd8 -- vet ikke hvorfor indeksen blir d8 her.
--else id < 0x5b then x = id*0xA-0xc4
end
return readsnesstring(0xe6f567+x,maxlen)
end
-- This is probably not the best way of determining the game mode, but it works.
mode_battle = function()
return memory.readword(0x7e1501) == 0x0ba7 and memory.readbyte(0x7e1503) == 0xC1
end
mode_cave = function()
return memory.readword(0x7e1501) == 0x0182 and memory.readbyte(0x7e1503) == 0xC0
end
mode_map = function()
return memory.readword(0x7e1501) == 0xa728 and memory.readbyte(0x7e1503) == 0xEE
end
mode_menu = function()
return memory.readword(0x7e1501) == 0x1387 and memory.readbyte(0x7e1503) == 0xC3
end
function HandleKeys()
keys = input.get()
if press('T') then
autoText = not autoText
end
last_keys = keys
end
function press(button)
if keys[button] and not last_keys[button] then
return true
end
return false
end
hours = 0
minutes = 0
seconds = 0
milliseconds = 0
function CalculateTime()
frames = snes9x.framecount()
frames = frames + addHours * 216000 + addMinutes * 3600 + addSeconds * 60 + addFrames
hours = math.floor(frames / 216000)
minutes = math.floor((frames - hours * 216000) / 3600)
seconds = math.floor((frames - hours * 216000 - minutes * 3600) / 60)
milliseconds = math.floor((frames - hours * 216000 - minutes * 3600 - seconds * 60) * 100 / 60)
end
function OutputTime()
gui.text(textX, textY, "Time: " .. hours .. ":" .. string.format("%02d",minutes) .. ":" .. string.format("%02d",seconds) .. "." .. string.format("%02d",milliseconds))
gui.text(textX, textY + 10, "Offset: " .. addHours .. ":" .. string.format("%02d",addMinutes) .. ":" .. string.format("%02d",addSeconds) .. "." .. string.format("%02d",math.floor(addFrames * 100 / 60)))
end
-- prints a compact representation of elemental properties.
-- There are 8 elements. Each is assigned a color, and a place
-- in a 3x3 matrix of pixels.
-- fil ; fire (red) ice (blue) lightning (yellow)
-- pwp ; poison (green) wind (gray) holy (white)
-- ew ; earth (brown) water (blue)
-- Colors need not be uniqe, as the position fixes things.
-- This is then surrounded by black border.
print_element = function(x,y,elem)
-- first draw the border
horline(x, y, x+4, "#404040")
vertline(x+4, y, y+4, "#404040")
vertline(x, y, y+4, "#404040")
horline(x, y+4, x+4, "#404040")
-- now draw the elements
local x,y=x+1,y+1
putpix = function(x,y,b,c)
if b == 1 then drawpixel(x,y,c) else drawpixel(x,y,"#000000") end
end
cols = {
"#ff0000","#0000ff","#ffff00",
"#00ff00","#808080","#ffffff",
"#a08000","#0000ff","#000000"
}
for i = 1, 9 do
dx, dy = (i-1)%3, math.floor((i-1)/3)
putpix(x+dx,y+dy,elem % 2,cols[i])
elem = math.floor(elem/2)
end
end
get_special = function(id)
-- Information about the special attack
-- This is used by rage, sketch, control.
local special = memory.readbyte(0xcf001f+0x20*id)
local specialn = readsnesstring(0xcfd0d0+id*10,10)
local no_damage,no_dodge = bit(special, 6),bit(special,7)
special = special % 0x40
desc = "???"
if special == 0x30 then desc = "absorb hp"
elseif special == 0x31 then desc = "absorb mp"
elseif special >= 0x32 then desc = "remove reflect"
elseif special >= 0x20 then desc = string.format("+%d%%",(special-0x1f)*50)
else
local descs = { "Dark","Zombie","Poison","MagiTek","Clear","Imp","Petrify",
"Death", "Condemned", "Near Fatal", "Image", "Mute", "Berserk",
"Muddle", "Seizure", "Sleep", "Dance", "Regen", "Slow", "Haste",
"Stop", "Shell", "Safe", "Reflect", "Rage", "Freeze", "Life 3",
"Morph", "Chant!", "Disappear!", "Dog block", "Float" }
desc = descs[special+1]
end
specialn = specialn.." ("..desc..")"
return specialn, no_damage, no_dodge
end
print_status = function(status)
-- Må finne ut hva bitsene i hver status byte betyr.
end
-- bit operations, since lua is silly enough to miss these
bit = function(n,i) return math.floor(n/2^i) % 2 end
bor = function(a,b)
local c = 0
local pot = 1
while a ~= 0 or b ~= 0 do
if a % 2 == 1 or b % 2 == 1 then c = c+pot end
a = math.floor(a/2)
b = math.floor(b/2)
pot = pot*2
end
return c
end
-- to get the pixel distance from an encounter, we
-- need to fix an occational off-by-1 error for
-- count. this happens on the first frame when we reach
-- a whole square. we thus need to keep track of whether
-- the last position was whole or not
last_position_whole = true
while true do
-- Are we in battle?
offset = 100
if mode_battle() then
--snes9x.speedmode("maximum")
-- memory.writebyte(0x7e3bfc, 0)
-- memory.writebyte(0x7e3bfd, 0)
-- memory.writebyte(0x7e3bfe, 0)
-- memory.writebyte(0x7e3bff, 0)
-- memory.writebyte(0x7e3c00, 0)
-- memory.writebyte(0x7e3c01, 0)
-- memory.writebyte(0x7e3c02, 0)
-- memory.writebyte(0x7e3c03, 0)
-- memory.writebyte(0x7e3c04, 0)
-- memory.writebyte(0x7e3c05, 0)
-- memory.writebyte(0x7e3c06, 0)
-- memory.writebyte(0x7e3c07, 0)
sel_side, sel_i = memory.readbyte(0x7e7ace), memory.readbyte(0x7e7acf)
sel_enemy, sel_party = memory.readbyte(0x7e7b7e),memory.readbyte(0x7e7b7d)
sel_multi = memory.readbyte(0x7e7b7f)
-- loop through present monsters
for i = 0, 5 do
-- a few shortcuts
j, k = 2*i, 2*(4+i)
state = memory.readword(0x7e3aa0+k)
if state ~= 0 and bit(state,15) ~= 1 then
-- get coordinates (we should know the size of the monster too, but we don't)
x = memory.readbyte(0x7e80c2+1+j)
-- this size is really the x-offset of the pointing hand relative to the start
-- of the sprite. For a normal attack, this works fine, but for monsters on the
-- right hand side of the screen, this offset is 0, as the hand is on their left
-- side. To do things properly, I should get this in a more direct fashion, but
-- I can't be bothered
xsize = memory.readbyte(0x7e807a+1+j)
if xsize == 0 then xsize = 0x20 end
y = memory.readbyte(0x7e80ce+1+j)
-- other interesting information
id = memory.readword(0x7e3388+j)
xp = memory.readword(0x7e3d8c+j)
gold = memory.readword(0x7e3da0+j)
hp = memory.readword(0x7e3bf4+k)
mp = memory.readword(0x7e3c08+k)
mhp = memory.readword(0x7e3c1c+k)
mmp = memory.readword(0x7e3c30+k)
level= memory.readbyte(0x7e3b18+k)
speed= memory.readbyte(0x7e3b19+k)
gauge= memory.readword(0x7e3218+k)
-- elements
absorb = memory.readbyte(0x7e3bcc+k)
nullify = memory.readbyte(0x7e3bcd+k)
resist = memory.readbyte(0x7e3be1+k)
weak = memory.readbyte(0x7e3be0+k)
stealn = memory.readbyte(0x7e3311+j)
stealr = memory.readbyte(0x7e3310+j)
dropn = memory.readbyte(0xcf3000+4*id+3)
dropr = memory.readbyte(0xcf3000+4*id+2)
name = getmonstername(id)
-- Print the gauges
startgauge(x,y,xsize)
drawgauge(mp, mmp, "#0000ff")
drawgauge(hp, mhp, "#00ff00")
drawgauge(gauge, 0x10000, "#ff0000")
by = gy
gui.text(x-16, by-8, string.format("%2d",level))
print_element(x, by-6, absorb)
print_element(x+4, by-6, nullify)
print_element(x+8, by-6, resist)
print_element(x+12, by-6, weak)
gui.text(x+18, by-8, ""..hp)
active_slot = memory.readbyte(0x7e0201)
active_player = memory.readbyte(0x7e3ed8+active_slot*2)
menu_pos = memory.readbyte(0x7e890f+active_slot)
relm_info = active_player == 8 and menu_pos == 1
locke_info = active_player == 1 and menu_pos == 1
gau_info = active_player == 0xb
if gau_info then
local id_byte, id_bit = math.floor(id/8), id % 8
if bit(memory.readbyte(0x7e1d2c+id_byte),id_bit) == 0 then
-- monster not known!
gui.text(x-8,y,"?")
end
end
-- More detailed information for the monster we point to.
-- This will depend on how it is being pointed to
if (sel_side == 0 or sel_side == 2) and sel_i == i and
sel_multi == 0 and sel_enemy ~= 0 then
--gui.text(10,offset,"Level "..level.." "..name)
--offset = offset+10
gui.text(10,offset,string.format("HP:%d/%d, MP:%d/%d",hp,mhp,mp,mmp))
offset = offset+10
--gui.text(10,offset,string.format("XP:%d, gold:%d",xp,gold))
--offset = offset+10
if locke_info then
gui.text(10,offset,string.format("Steal %s/%s", getitemname(stealn), getitemname(stealr)))
offset = offset+10
end
gui.text(10,offset,string.format("Drops %s/%s", getitemname(dropn), getitemname(dropr)))
offset = offset+10
-- Special attack description
specialn = get_special(id)
-- Sketch
sketch1 = memory.readbyte(0xcf4300+2*id)
sketch2 = memory.readbyte(0xcf4300+2*id+1)
-- control
control, controln = {}, {}
for ctrl_i = 0, 3 do
control[ctrl_i] = memory.readbyte(0xcf3d00+4*id+ctrl_i)
end
if sketch1 == 0xEF then sketch1n = specialn else sketch1n = getattackname(sketch1) end
if sketch2 == 0xEF then sketch2n = specialn else sketch2n = getattackname(sketch2) end
for ctrl_i = 0, 3 do
local ctrl,foo = control[ctrl_i]
if ctrl == 0xEF then foo = specialn
elseif ctrl == 0xFF then foo = "Nothing"
else foo = getattackname(ctrl) end
controln[ctrl_i] = foo
end
if relm_info then
gui.text(10,offset,string.format("Sketch %s/%s", sketch2n, sketch1n))
offset = offset+10
gui.text(10,offset,string.format("Control %s/%s",controln[0],controln[1]))
offset = offset+10
gui.text(10,offset,string.format(" %s/%s",controln[2],controln[3]))
offset = offset+10
end
end
end end
-- Rage information when in the rage menu
if memory.readbyte(0x7e7bd4) == 0x24 then
x = memory.readbyte(0x7e892f)
y = memory.readbyte(0x7e8933)
scroll = memory.readbyte(0x7e892b)
i = 2*(y+scroll)+x
id = memory.readbyte(0x7e257e + i)
if id ~= 0xFF then
-- What rages does this monster have?
rage1 = memory.readbyte(0xcf4600+2*id)
rage2 = memory.readbyte(0xcf4600+2*id+1)
-- Special attack description
specialn = get_special(id)
if rage1 == 0xEF then rage1n = specialn else rage1n = getattackname(rage1) end
if rage2 == 0xEF then rage2n = specialn else rage2n = getattackname(rage2) end
gui.text(10,offset,string.format("%s/%s", rage1n, rage2n))
offset = offset+10
end
end
elseif mode_cave() then
snes9x.speedmode("normal")
-- get the screen position
sx = memory.readword(0x7e8297)
sy = memory.readword(0x7e8299)
if sx >= 0x8000 then sx = sx-0x10000 end
if sy >= 0x8000 then sy = sy-0x10000 end
-- prepare to loop through treasures
area = memory.readword(0x7e0082)
table = 0xed8634
i = memory.readword(0xed82f4+2*area)
to = memory.readword(0xed82f4+2*(area+1))
-- loop through the treasures
while i ~= to do
-- read from the treasure information array
xi = memory.readbyte(table+i)
yi = memory.readbyte(table+i+1)
flagi = memory.readword(table+i+2)
item = memory.readbyte(table+i+4)
name = getitemname(item)
-- Has the box been taken? To find out, we need to
-- do some bit manipulation.
byte_index = math.floor(flagi/8)
bit_index = flagi % 8
if byte_index >= 0x40 then
byte_index = byte_index - 0x40*math.floor(byte_index/0x40)
end
flag = memory.readbyte(0x7e1e40+byte_index)
flag = flag % (2^(bit_index+1))
flag = math.floor(flag/ 2^bit_index)
-- flag is now 0 if not taken and 1 if taken
if flag == 0 then color = "#00ff00" else color = "#ff0000" end
-- get the real x,y position, using the fact that
-- each square is 0x10 big.
x,y = 0x10*xi, 0x10*yi
-- relative coordinates
rx, ry = x-sx, y-sy
-- draw a nice red box around the treasure box
gui.drawbox(rx, ry, rx+0x10, ry+0x10, color)
gui.text(rx, ry-5, name)
i = i + 5
end
table = 0xdfbb00
i = memory.readword(table+2*area)
to = memory.readword(table+2*(area+1))
-- loop through the transitions
while i ~= to do
-- read from the treasure information array
xi = memory.readbyte(table+i)
yi = memory.readbyte(table+i+1)
hmm = memory.readword(table+i+2)
xti = memory.readbyte(table+i+4)
yti = memory.readbyte(table+i+5)
area2 = hmm % 0x200
-- is it to the same area? If so, color it
-- light blue. Else make it normal blue.
if area2 == area then color = "#8080ff"
else color = "#0000ff" end
-- get the real x,y position, using the fact that
-- each square is 0x10 big.
x,y = 0x10*xi, 0x10*yi
-- relative coordinates
rx, ry = x-sx, y-sy
gui.drawbox(rx, ry, rx+0x10, ry+0x10, color)
i = i + 6
end
table = 0xedf480
i = memory.readword(table+2*area)
to = memory.readword(table+2*(area+1))
-- loop through the wide transitions
while i ~= to do
-- read from the treasure information array
xi = memory.readbyte(table+i)
yi = memory.readbyte(table+i+1)
dxi = memory.readbyte(table+i+2)
dyi = 0
hmm = memory.readword(table+i+3)
xti = memory.readbyte(table+i+5)
yti = memory.readbyte(table+i+6)
area2 = hmm % 0x200
if dxi >= 0x80 then dxi, dyi = 0, dxi % 0x80 end
-- is it to the same area? If so, color it
-- light blue. Else make it normal blue.
if area2 == area then color = "#8080ff"
elseif area2 == 0x1ff then color = "#b0b0ff"
else color = "#0000ff" end
-- get the real x,y position, using the fact that
-- each square is 0x10 big.
x,y,dx,dy = 0x10*xi, 0x10*yi, 0x10*dxi, 0x10*dyi
-- relative coordinates
rx, ry = x-sx, y-sy
gui.drawbox(rx, ry, rx+dx+0x10, ry+dy+0x10, color)
i = i + 7
end
table= 0xc40000
i = memory.readword(0xc40000+2*area)
to = memory.readword(0xc40000+2*(area+1))
-- loop through the reactions
while i ~= to do
-- read from the treasure information array
xi = memory.readbyte(table+i)
yi = memory.readbyte(table+i+1)
react1 = memory.readword(table+i+2)
react2 = memory.readbyte(table+i+4)
color = "#ffff00"
-- get the real x,y position, using the fact that
-- each square is 0x10 big.
x,y = 0x10*xi, 0x10*yi
-- relative coordinates
rx, ry = x-sx, y-sy
gui.drawbox(rx, ry, rx+0x10, ry+0x10, color)
i = i + 5
end
else
snes9x.speedmode("normal")
end
-- encounter counter
if mode_map() or mode_cave() then
-- Equipment-derived information is not kept permanently, it is recomputed
-- at the beginning of every frame, I think. We need to know if the party
-- has the moogle charm or charm bangle effect.
-- We will therefore loop through the equipment of all characters.
field_effects = 0
status_effects = 0
char_in_party = 0
for id = 0, 0xF do
tmp = memory.readbyte(0x7e1850+id)
if tmp % 8 == memory.readbyte(0x7e1a6d) then
-- character is present. If necessary, information about
-- row and slot is also awailable in tmp.
char_in_party = char_in_party + 1
x = id*0x25+0x1F -- start of equipment
for i = 0, 5 do
item = memory.readbyte(0x7e1600+x+i)
if item ~= 0xFF then
-- item actually equipped
field_effects = bor(field_effects,memory.readbyte(0xd85005+item*0x1e))
status_effects = bor(status_effects,memory.readbyte(0xd8500a+item*0x1e))
end
end
end
end
-- field_effects is what is later put in 11df
if mode_map() then
terrain = memory.readbyte(0x7e11f9) % 8
at_22 = memory.readbyte(0xc0c28f+terrain) -- lave tall, men hva betyr de?
a = memory.readbyte(0xc0c297+terrain) -- denne arrayen følger rett etter. samme størrelsesorden.
-- b og c er faktisk x og y-posisjonen på kartet til *forrige encounter*!
last_x = memory.readbyte(0x7e1f60)
last_y = memory.readbyte(0x7e1f61)
zone_x, zone_y = math.floor(last_x/0x20),math.floor(last_y/0x20)
zone = memory.readbyte(0x7e1f64)*0x40+zone_y*8+zone_x
-- a må inneholde en mapping fra terreng til offsets i følgende array.
-- merkelig at slikt trengs.
pack_index = memory.readbyte(0xcf5400+zone*4+a)
gui.text(textX, textY + 30, "pack index: " .. pack_index)
-- den høye byten her angir kanskje om det er world of balance eller ruin eller noe annet?
encounter_rate_index = math.floor(memory.readbyte(0xcf5800+zone) / 4^at_22) % 4
-- field_effects har info om charm bangle etc. encounter_rate_index info om more/less enc.
x = (field_effects)%4*8 + encounter_rate_index*2
-- x er nå indeks inn i encounter rate-tabellen.
encounter_rate = memory.readword(0xc0c29f+x)
else
area = memory.readword(0x7e0082)
ax,ay = math.floor(area/4), area % 4
a = (memory.readbyte(0xcf5880+ax) / 4^ay) % 4
x = ((memory.readbyte(0x7e11df) % 4) * 4 + a) * 2
encounter_rate = memory.readword(0xc0c2bf+x)
if bit(memory.readbyte(0x7e0525),7) == 0 then
encounter_rate = 0
end
pack_index = memory.readbyte(0xcf5600+area)
gui.text(textX, textY + 30, "pack index: " .. pack_index)
end
enc1 = memory.readword(0x7e1f6e)
enc2 = memory.readbyte(0x7e1fa1)
enc3 = memory.readbyte(0x7e1fa4)
count = 1
if encounter_rate ~= 0 then
for i = 0, 100 do
enc1 = enc1 + encounter_rate
if enc1 >= 0x10000 then enc1 = 0xff00 end
enc2 = (enc2+1) % 0x100
if enc2 == 0 then enc3 = (enc3 + 0x11) % 0x100 end
if (memory.readbyte(0xc0fd00+enc2)+enc3) % 0x100 < math.floor(enc1/0x100) then break end
count = count+1
end
gui.text(10,offset,"Encounter in ".. count.." steps.")
offset = offset+10
-- Veldt?
if pack_index == 0xFF then
enc_veldt = (memory.readbyte(0x7e1fa5)+1) % 0x40
-- find a nonzero byte in the encountered formations bitarray.
while memory.readbyte(0x7e1ddd+enc_veldt) == 0 do
enc_veldt = (enc_veldt+1) % 0x40
end
enc5 = memory.readbyte(0x7e1fa3)
enc4 = (memory.readbyte(0x7e1fa2)+1) % 0x100
if enc4 == 0 then enc5 = enc5 + 0x17 end
bit_index = (memory.readbyte(0x7ec0fd00+enc4)+enc5) % 0x8
bitset = memory.readbyte(0x7e1ddd+enc_veldt)
-- find a nonzero bit, starting at a random position
while bit(bitset,bit_index) == 0 do
bit_index = (bit_index+1) % 8
end
formation = enc_veldt*8+bit_index
else
x = pack_index*8
-- Encounter type. x is the index into the monster pack array
enc1 = (memory.readbyte(0x7e1fa2)+1) % 0x100
enc2 = memory.readbyte(0x7e1fa3)
if enc1 == 0 then enc2 = enc2+0x17 end
a = (memory.readbyte(0xc0fd00+enc1)+enc2) % 0x100
if a > 0x50 then x = x+2 end
if a > 0xa0 then x = x+2 end
if a > 0xf0 then x = x+2 end
formation = memory.readword(0xcf4800+x)
end
-- At this point, we have all the frame-independent information about the
-- encounter we can get. The following is an attempt to handle the frame-
-- dependent information by predicting at what frame an encounter will occur.
-- This is formation variation (on the floating continent only), attack type
-- (front, back, pincer, side) and preemptiv or not. However, this ended in
-- failure. It works for 3-4 chars in party on the map, mostly, but not for
-- fewer chars, and not when not on the world map. If you want to enable it,
-- comment the following line
skip_frame_dependent = true
if not skip_frame_dependent then
-- To find out more about the encounter, we need the frame-
-- dependent be variable. We must first predict the number
-- of pixels we are away from an encounter.
if mode_map() then
faceing = memory.readbyte(0x7e00f6)
xpos = memory.readword(0x7e00c6)
ypos = memory.readword(0x7e00c8)
else
-- have to loop through all chars to find out who is the leader?
-- However, the position we get this way does not move smoothly. Bleh.
for id = 0, 0xF do
local delta = 0x29*id
xpos, ypos = 0,0
if bit(memory.readbyte(0x7e0867+delta),7) == 1 then
faceing = memory.readbyte(0x7e086f+delta)
xpos = memory.readword(0x7e086a+delta)
ypos = memory.readword(0x7e086d+delta)
break
end
end
end
if faceing == 0 then pixels = ypos % 0x10
elseif faceing == 1 then
pixels = xpos % 0x10
if pixels ~= 0 then pixels = 0x10-pixels end
elseif faceing == 2 then
pixels = ypos % 0x10
if pixels ~= 0 then pixels = 0x10-pixels end
else pixels = xpos % 0x10
end
-- this is the pixels left in our current step. then add the number
-- of whole steps
standing_still = false
if pixels == 0 then
-- this is a whole position. was the last position whole too?
if last_position_whole then
standing_still = true
pixels = 0x10*count
else pixels = 0x10*(count-1) end
last_position_whole = true
else
pixels = pixels + 0x10*(count-1)
last_position_whole = false
end
-- sprint shoes?
if mode_cave() and bit(status_effects,5) then
pixels = math.floor(pixels/2)
end
-- compensate for the time it takes to start moving
if standing_still then
if mode_map() then pixels = pixels+2
else
if bit(status_effects,5) == 1 then
pixels = pixels + memory.readbyte(0x7e0021e) % 4
else pixels = pixels + memory.readbyte(0x7e0021e) % 4 end
end
end
-- we assume that we are walking with maximum speed of 1 pixel per frame
-- this will lead to be getting this value at the beginning of battle
be = 4*((memory.readbyte(0x7e021e)+pixels+0x22) % 0x3c)
gui.text(10,90,string.format("be: %x vs %x, %x %x", memory.readbyte(0x7e00be), be,pixels,xpos))
if math.floor(formation / 0x8000) == 1 then
-- High bit of formation set: randomly add 0..3 to formation
be = (be+1) % 0x100
a = memory.readbyte(0xc0fd00+be) % 4
formation = (formation+a) % 0x8000
end
end
info1 = memory.readbyte(0xcf5900+4*formation)
info2 = memory.readbyte(0xcf5900+4*formation+1)
info3 = memory.readbyte(0xcf5900+4*formation+2)
info4 = memory.readbyte(0xcf5900+4*formation+3)
-- Build monster list
start = 0xcf6201+15*formation
present = memory.readbyte(start)
which = {}
number_of_enemies = 0
for i = 1, 6 do
if present % 2 == 1 then
id = memory.readbyte(start+i)
if which[id] then which[id] = which[id]+1
else which[id] = 1 end
number_of_enemies = number_of_enemies + 1
end
present = math.floor(present/2)
end
-- Display list
for id, num in pairs(which) do
gui.text(10, offset, ""..num.." "..getmonstername(id))
offset = offset + 10
end
if not skip_frame_dependent then
-- What kind of encounter?
variant = math.floor(info1 / 0x10) % 0x10
-- variant is four bits long, and those bits are front, back, pincer, side; inverted.
-- from high to low.
-- let us extract these
allowed = {}
allowed_number = 0
for i = 0, 3 do
allowed[i] = 1 - variant % 2
allowed_number = allowed_number + allowed[i]
variant = math.floor(variant/2)
end
if bit(status_effects,1) == 1 then
-- back guard, disable back and pincer.
-- This will prefer to remove pincer if not
-- both can be removed.
for i = 1, 2 do if allowed_number > 1 then
allowed[i] = 0
allowed_number = allowed_number-1
end end
end
if char_in_party < 3 and allowed_number > 1 then
-- not enough characters to do side attack
allowed[3] = 0
allowed_number = allowed_number - 1
end
-- Now we pick one of the possibilities
-- First find the sum of the weights
local sum = 0
for i = 0, 3 do if allowed[i] == 1 then
sum = sum + memory.readbyte(0xc25279+i)+1
end end
-- before this point, be has been incremented by other things
be = be + 0xa + 2*number_of_enemies + char_in_party
-- get a random number from 0 to sum-1, using be
be = (be+1) % 0x100
rnd = math.floor(memory.readbyte(0xc0fd00+be)*sum/0x100)
-- now loop through again, and pick the first that is bigger
-- than the number
sum = 0
for i = 0, 3 do if allowed[3-i] == 1 then
sum = sum + memory.readbyte(0xc25279+i)+1
if sum > rnd then
chosen = 3-i
break
end
end end
vardesc = { "Front", "Back", "Pincer", "Side" }
-- Finally, describe the result
if chosen ~= 0 then
gui.text(10, offset, string.format("%s attack",vardesc[chosen+1]))
offset = offset+10
end
end
end
end
HandleKeys()
if autoText then
gui.text(textX,textY + 20,"(T) Auto Text : On")
else
gui.text(textX,textY + 20,"(T) Auto Text : Off")
end
if autoText and (snes9x.framecount() % 6 == 0 or ((snes9x.framecount() - 1) % 6 == 0)) then
curPad = joypad.get()
curPad.A = true
joypad.set(curPad)
end
if (memory.readbyte(0x7e1dd2) % 2 == 0) and (memory.readword(0x7e1501) == 0xa509) and (memory.readbyte(0x7e1503) == 0xEE) then
x1 = memory.readword(0x7e0b00)
y1 = memory.readword(0x7e0b02)
x2 = memory.readword(0x7e0b04)
y2 = memory.readword(0x7e0b06)
gui.line(x1 + 176, 156, x1 + 176, 219, "#008080")
gui.line(176, y1 + 156, 239, y1 + 156, "#008080")
gui.line(x2 + 176, 156, x2 + 176, 219, "#008080")
gui.line(176, y2 + 156, 239, y2 + 156, "#008080")
gui.pixel(x1 + 176, y1 + 156, "#00ffff")
gui.pixel(x2 + 176, y2 + 156, "#00ffff")
end
--for moviemaking
--cMode1, cMode2 = memory.readword(0x7e1501), memory.readbyte(0x7e1503)
--if lMode1 ~= cMode1 and lMode2 ~= cMode2 then
-- snes9x.pause()
--end
--lMode1 = cMode1
--lMode2 = cMode2
CalculateTime()
OutputTime()
snes9x.frameadvance()
end
Comments
0 B
|👍
/👎