Made for BizHawk 2.9.1. It provides a enemy drop prediction and a entity slot state view.
Press Num7 for switching between drop prediction and a entity slot state view, press Num9 for hitbox view toggle.
--This script is created for BizHawk 2.9.1. Please use 2.9 or newer version.
--Press Num7 for switching enemy drop prediction and entity slot display. Press Num9 for hitbox view toggle.
nds.setscreenlayout("Vertical")
client.SetGameExtraPadding(0,0,200,0)
nds.setscreeninvert(false)
gui.defaultPixelFont("fceux")
gui.defaultTextBackground("clear")
enemy_name={"Bat","Zombie","Skeleton","Ghost","Banshee","Bone Scimitar","Sea Stinger","Nominon","Axe Knight","Une","Merman","Necromancer","Bone Archer","Spear Guard","Invisible Man","Gelso","Needles","Demon","Fishhead","Dark Octopus","Killer Fish","Forneus","The Creature","Black Crow","Skull Spider","Scarecrow","Sea Demon","Winged Guard","Nightmare","Rock Knight","Fire Demon","Bitterfly","Specter","Grave Digger","Werebat","Black Fomor","Enkidu","Bone Pillar","Skeleton Frisky","Skeleton Hero","Dullahan","Skeleton Rex","White Dragon","Saint Elmo","Lorelai","Edimmu","Decarabia","Merman","Ladycat","Ectoplasm","Curse Diva","Miss Murder","Automaton ZX26","Skeleton Beast","Balloon","Arachne","Lizardman","Armored Beast","Yeti","Thunder Demon","Owl","Werewolf","Altair","Mandragora","Jersey Devil","Owl Knight","Chosen Une","Stone Rose","Mad Butcher","White Fomor","Evil Force","Flea Man","Ghoul","Peeping Eye","Gargoyle","Blood Skeleton","Black Panther","Mimic","Draculina","Tin Man","Polkir","Nova Skeleton","Gashida","Devil","Gurkha Master","Red Smasher","Cave Troll","Blade Master","Lilith","Lizardman Blade","Hammer Shaker","Rebuild","Imp","Bugbear","Spectral Sword","Automaton ZX27","Medusa Head","Gorgon Head","Mad Snatcher","Great Knight","King Skeleton","Winged Skeleton","Final Knight","Jiang Shi","Demon Lord","Double Hammer","Weapon Master","Giant Skeleton","Arthroverta","Brachyura","Maneater","Rusalka","Goliath","Gravedorcus","Albus","Barlowe","Wallman","Blackmore","Eligor","Death","Dracula"}
function update_RAM()
RNG = mainmemory.read_u32_le(0x1389C0)
HP = mainmemory.read_s16_le(0x1002B4)
HP_max = mainmemory.read_s16_le(0x1002B6)
MP = mainmemory.read_s16_le(0x1002B8)
MP_max = mainmemory.read_s16_le(0x1002BA)
heart = mainmemory.read_s16_le(0x1002BC)
heart_max = mainmemory.read_s16_le(0x1002BE)
MP_timer = mainmemory.read_u8(0x0FFEC0)
inv1_1 = mainmemory.read_u8(0x1098E4)
inv1_2 = mainmemory.read_u8(0x1098E5)
inv1_3 = mainmemory.read_u8(0x1098E6)
inv2 = mainmemory.read_u8(0x109908)
X = mainmemory.read_s32_le(0x0FFC58)
Y = mainmemory.read_s32_le(0x0FFC5C)
X_speed = mainmemory.read_s32_le(0x10985C)
Y_speed = mainmemory.read_s32_le(0x109860)
screen_X = mainmemory.read_s32_le(0x1000BC)
screen_Y = mainmemory.read_s32_le(0x1000C0)
IGT = mainmemory.read_u32_le(0x100374)
global_timer = mainmemory.read_u32_le(0x0FFC54)
end
function watch_RAM()
gui.pixelText(256,192,"RNG: ")
gui.pixelText(316,192,string.format("%08X", RNG))
gui.pixelText(256,212,"HP: ")
gui.pixelText(316,212,HP.."/"..HP_max)
gui.pixelText(256,222,"MP: ")
gui.pixelText(316,222,MP.."/"..MP_max)
gui.pixelText(256,232,"HEART: ")
gui.pixelText(316,232,heart.."/"..heart_max)
gui.pixelText(256,242,"MP TIMER: ")
gui.pixelText(316,242,MP_timer)
gui.pixelText(256,252,"INV 1: ")
gui.pixelText(316,252,inv1_1..":"..inv1_2..":"..inv1_3)
gui.pixelText(256,262,"INV 2: ")
gui.pixelText(316,262,inv2)
gui.pixelText(256,272,"X: ")
gui.pixelText(316,272,X/4096)
gui.pixelText(256,282,"Y: ")
gui.pixelText(316,282,Y/4096)
gui.pixelText(256,292,"X SPD: ")
gui.pixelText(316,292,X_speed/4096)
gui.pixelText(256,302,"Y SPD: ")
gui.pixelText(316,302,Y_speed/4096)
IGT_string_length = string.len(tostring(IGT))
gui.pixelText((455-IGT_string_length*6),354,IGT)
global_timer_lag_length = string.len(tostring(global_timer))
gui.pixelText((455-global_timer_lag_length*6),364,global_timer)
end
last_input = {}
user_input = {}
function update_key_pressed()
new_input = input.get()
for k,v in pairs(new_input) do
if v and not last_input[k] then
user_input[k] = true
else
user_input[k] = false
end
end
last_input = new_input
end
predict_switch_count = 0 --0 if off, 1 if drop predict, 2 if entity slot
function predict_switch()
if user_input.Keypad7 then
predict_switch_count = predict_switch_count+ 1
end
if predict_switch_count > 2 then
predict_switch_count = 0
end
end
function draw_entity_list(X, Y)
for slot = 0x7D, 0xE8 do
if mainmemory.read_u8(0x1092A0+slot*0x160+12)==0x23 then
text_color = "deepskyblue"
elseif mainmemory.read_u8(0x1092A0+slot*0x160+12)~=0 then
text_color = "#FF00FF00"
else
text_color = "white"
end
slot_X = (slot - 0x7D)%12
slot_Y = (slot - 0x7D)//12
gui.pixelText(X+slot_X*16, Y+slot_Y*10, string.format("%02X",slot), text_color)
end
if mainmemory.read_u8(0x10C0D3)==2 then
union_secare_effect_slot = (mainmemory.read_u32_le(0x10C0D0)-0x021092A0)/0x160
slot_X = (union_secare_effect_slot - 0x7D)%12
slot_Y = (union_secare_effect_slot - 0x7D)//12
gui.drawBox(X+slot_X*16-1, Y+slot_Y*10-1, X+slot_X*16+13, Y+slot_Y*10+9, "white", "clear")
end
for i, v in ipairs({0x09,0x0A,0x0B,0x12,0x13,0x14}) do
if mainmemory.read_u8(0x1092A0+v*0x160+0x153)==2 then
ignis_particle_slot = (mainmemory.read_u32_le(0x1092A0+v*0x160+0x150)-0x021092A0)/0x160
slot_X = (ignis_particle_slot - 0x7D)%12
slot_Y = (ignis_particle_slot - 0x7D)//12
gui.drawBox(X+slot_X*16-1, Y+slot_Y*10-1, X+slot_X*16+13, Y+slot_Y*10+9, "white", "clear")
end
end
end
function draw_hitbox()
screen_X = mainmemory.read_s32_le(0x1000BC)/4096
screen_Y = mainmemory.read_s32_le(0x1000C0)/4096
for i = 0x04, 0xE8 do
if mainmemory.read_u8(0x1092A0+i*0x160+12)~=0 then
obj_X = mainmemory.read_s32_le(0x1092A0+i*0x160+0x24)/4096 --object's slot, HP, inv
obj_Y = mainmemory.read_s32_le(0x1092A0+i*0x160+0x28)/4096
if obj_X < 256 and obj_Y > 0 then
if mainmemory.read_u8(0x1092A0+i*0x160+12)==0x11 or mainmemory.read_u8(0x1092A0+i*0x160+12)==0x13 then
obj_HP = mainmemory.read_s16_le(0x1092A0+i*0x160+0x128)
gui.pixelText(obj_X, obj_Y+192, string.format("%X%s%d", i,":",obj_HP), nil, "black", "gens")
obj_inv1 = mainmemory.read_u8(0x1092A0+i*0x160+0xC5)
obj_inv2 = mainmemory.read_u8(0x1092A0+i*0x160+0xC6)
obj_inv3 = mainmemory.read_u8(0x1092A0+i*0x160+0xC7)
if obj_inv1+obj_inv2+obj_inv3 > 0 then
gui.pixelText(obj_X, obj_Y+192+5, obj_inv1..","..obj_inv2..","..obj_inv3, nil, "black", "gens")
end
else
gui.pixelText(obj_X, obj_Y+192, string.format("%02X", i), nil, "black", "gens")
end
end
for j = 0, 1 do --object's hitboxes
if mainmemory.read_u16_le(0x128BDC+i*0x14+j*10)~=0 then
box_x1 = mainmemory.read_s16_le(0x128BDC+i*0x14+j*10+2)
box_y1 = mainmemory.read_s16_le(0x128BDC+i*0x14+j*10+4)
box_x2 = mainmemory.read_s16_le(0x128BDC+i*0x14+j*10+6)
box_y2 = mainmemory.read_s16_le(0x128BDC+i*0x14+j*10+8)
if math.min(box_x1,box_x2)-screen_X > 255 then --hitbox OoB
elseif math.max(box_y1,box_y2)-screen_Y < 0 then --hitbox OoB
elseif math.max(box_x1,box_x2)-screen_X > 255 and math.min(box_y1,box_y2)-screen_Y < 0 then --hitbox on right-top cornor
gui.drawBox(math.min(box_x1,box_x2)-screen_X, math.max(box_y1,box_y2)-screen_Y+192, 255, 192, "clear", "#40FFFFFF")
gui.drawLine(math.min(box_x1,box_x2)-screen_X, math.max(box_y1,box_y2)-screen_Y+192, math.min(box_x1,box_x2)-screen_X, 192, "white")
gui.drawLine(math.min(box_x1,box_x2)-screen_X, math.max(box_y1,box_y2)-screen_Y+192, 255, math.max(box_y1,box_y2)-screen_Y+192, "white")
elseif math.max(box_x1,box_x2)-screen_X > 255 then --hitbox on right edge
gui.drawBox(math.min(box_x1,box_x2)-screen_X, box_y1-screen_Y+192, 255, box_y2-screen_Y+192, "clear", "#40FFFFFF")
gui.drawLine(math.min(box_x1,box_x2)-screen_X, box_y1-screen_Y+192, math.min(box_x1,box_x2)-screen_X, box_y2-screen_Y+192, "white")
gui.drawLine(math.min(box_x1,box_x2)-screen_X, box_y1-screen_Y+192, 255, box_y1-screen_Y+192, "white")
gui.drawLine(math.min(box_x1,box_x2)-screen_X, box_y2-screen_Y+192, 255, box_y2-screen_Y+192, "white")
elseif math.min(box_y1,box_y2)-screen_Y < 0 then --hitbox on top edge
gui.drawBox(box_x1-screen_X, math.max(box_y1,box_y2)-screen_Y+192, box_x2-screen_X, 192, "clear", "#40FFFFFF")
gui.drawLine(box_x1-screen_X, math.max(box_y1,box_y2)-screen_Y+192, box_x2-screen_X, math.max(box_y1,box_y2)-screen_Y+192, "white")
gui.drawLine(box_x1-screen_X, math.max(box_y1,box_y2)-screen_Y+192, box_x1-screen_X, 192, "white")
gui.drawLine(box_x2-screen_X, math.max(box_y1,box_y2)-screen_Y+192, box_x2-screen_X, 192, "white")
else --hitbox in middle of screen
gui.drawBox(box_x1-screen_X, box_y1-screen_Y+192, box_x2-screen_X, box_y2-screen_Y+192, "white", "#40FFFFFF")
end
end
end
end
end
end
color_background = "#FF606060"
color_no_item_drop = "#FF404040"
color_glyph_undrop = "#FF999900"
color_glyph_drop = "#FF009900"
color_rare = "#FF00FF00"
color_commen = "deepskyblue"
color_glyph = "red"
color_gold = "yellow"
color_1G = "dodgerblue"
color_10G = "brown"
color_50G = "white"
function mul32(a, b)
-- separate the value into two 16-bit values to prevent type casting
local x, y, z = {}, {}, {}
x[1] = a & 0xffff
x[2] = (a>>16) & 0xffff
y[1] = b & 0xffff
y[2] = (b>>16) & 0xffff
-- calculate for each halfword
local v, c
v = x[1] * y[1]
z[1], c = v & 0xffff, v >> 16
v = c + x[2] * y[1] + x[1] * y[2]
z[2], c = v & 0xffff, v >> 16
-- compose them and return it
return z[1] | (z[2]<<16)
end
function add32(a, b)
return (a + b) & 0xFFFFFFFF
end
function update_RNG_seq()
RNG_t_seq = {}
mod10000 = {}
RNG_t = mainmemory.read_u32_le(0x1389C0)
for i = 1, 186 do
RNG_t = add32(mul32(bit.arshift(RNG_t,8), 0x3243f6ad), 0x1b0cb175)
RNG_t_seq[i] = RNG_t
mod10000[i] = RNG_t % 10000
end
end
function predict_item(X, Y)
gui.drawBox(X, Y, X+179, Y+5, color_background, color_background)
if r_drop~=0 then
for i = 1, 180 do
if mod10000[i] < rarerate then --rare-commem selection
selected_chance = r_chance
drop_color = color_rare
else
selected_chance = c_chance
drop_color = color_commen
end
drop_chance = math.min(5000,(selected_chance * 100 + lck * 5) * rate_multipler)
if mod10000[i+1] < drop_chance then --rare/commem drop
if not (drop_color==color_commen and c_drop==0) then
gui.drawLine(X+i-1, Y, X+i-1, Y+5, drop_color)
end
else
if mod10000[i+3] < goldrate then --gold drop
--gui.drawLine(X+i-1, Y+3, X+i-1 , Y+5, color_gold)
gold_drop = RNG_t_seq[i+2]%3
if gold_drop == 0 then
drop_color = color_1G
elseif gold_drop == 1 then
drop_color = color_10G
else
drop_color = color_50G
end
gui.drawBox(X+i-1 ,Y, X+i-1, Y+2, drop_color)
end
end
end
else --enemy only has commem drop
for i = 1, 180 do
selected_chance = c_chance
if mod10000[i] >= commonrate then
selected_chance = 0
end
drop_chance = math.min(5000,(selected_chance * 100 + lck * 5) * rate_multipler)
if mod10000[i+1] < drop_chance then
gui.drawBox(X+i-1,Y,X+i-1,Y+5,color_commen)
else
if mod10000[i+3] < goldrate then --gold drop
--gui.drawLine(X+i-1, Y+3, X+i-1 , Y+5, color_gold)
gold_drop = RNG_t_seq[i+2]%3
if gold_drop == 0 then
drop_color = color_1G
elseif gold_drop == 1 then
drop_color = color_10G
else
drop_color = color_50G
end
gui.drawBox(X+i-1 ,Y, X+i-1, Y+2, drop_color)
end
end
end
end
end
function predict_glyph(X, Y)
if g_chance ~= 0x64 then
drop_chance = g_chance*100 + lck*10
for i = 1, 180 do
if mod10000[i] < drop_chance then
gui.drawLine(X+i-1, Y, X+i-1, Y+5, color_glyph)
end
end
end
end
function predict_enemy_drop(X, Y)
update_RNG_seq()
accessory1 = mainmemory.read_u16_le(0x1002CE)
accessory2 = mainmemory.read_u16_le(0x1002D0)
lck = mainmemory.read_s16_le(0x0FFD68)
difficulty = mainmemory.read_u8(0x100794)
rarerate = 4000 + lck * 8 * (1+difficulty*4)
rate_multipler = 1
if accessory1 == 0x25 then
rarerate = rarerate + 1000
rate_multipler = rate_multipler + 0.1
end
if accessory2 == 0x25 then
rarerate = rarerate + 1000
rate_multipler = rate_multipler + 0.1
end
rarerate = math.min(7000, rarerate)
commonrate = 5000 + lck * 8 * (1+difficulty*4)
if accessory1 == 0x24 then
commonrate = commonrate + 1000
end
if accessory2 == 0x24 then
commonrate = commonrate + 1000
end
commonrate = math.min(7000, commonrate)
goldrate = 500
if accessory1 == 0x22 then
goldrate = goldrate + 1792
end
if accessory2 == 0x22 then
goldrate = goldrate + 1792
end
draw_coodinate_Y = Y
enemy_list = {}
glyph_list = {}
glyph_valid_list = {}
enemy_count = 0
for slot = 0x2D, 0x7C do
if mainmemory.read_u32_le(0x1092A0+slot*0x160)~=0 then --enemy list
if mainmemory.read_u8(0x1092A0+slot*0x160+12)==0x11 or mainmemory.read_u8(0x1092A0+slot*0x160+12)==0x13 then
enemy_ID = mainmemory.read_u8(0x1092A0+slot*0x160+0x132)+1
enemy_list[enemy_ID] = true
end
end
end
for slot = 0x7D, 0xE8 do --glyph list
if mainmemory.read_u8(0x1092A0+slot*0x160+12)==0x23 then
glyph_ID = mainmemory.read_u8(0x1092A0+slot*0x160+0xD8)
if mainmemory.read_u8(0x1092A0+slot*0x160+0xD0)==3 then
glyph_valid_list[glyph_ID] = true
end
glyph_list[glyph_ID] = true
end
end
for enemy_id = 1, 121 do
if enemy_list[enemy_id] then
c_drop = mainmemory.read_u16_le(0x0B6340+enemy_id*0x24+0x8)
r_drop = mainmemory.read_u16_le(0x0B6340+enemy_id*0x24+0xA)
g_drop = mainmemory.read_u16_le(0x0B6340+enemy_id*0x24+0x14)
c_chance = mainmemory.read_u8(0x0B6340+enemy_id*0x24+0x1A)
r_chance = mainmemory.read_u8(0x0B6340+enemy_id*0x24+0x1B)
g_chance = mainmemory.read_u8(0x0B6340+enemy_id*0x24+0x16)
gui.pixelText(X, draw_coodinate_Y, enemy_name[enemy_id], nil, nil, "gens")
draw_coodinate_Y = draw_coodinate_Y + 8
if c_drop==0 and r_drop==0 then
gui.drawBox(X, draw_coodinate_Y, X+179, draw_coodinate_Y+5, color_no_item_drop, color_no_item_drop)
draw_coodinate_Y = draw_coodinate_Y + 8
else
predict_item(X, draw_coodinate_Y)
draw_coodinate_Y = draw_coodinate_Y + 8
end
if g_drop~=0 then
if glyph_valid_list[g_drop] then
gui.drawBox(X, draw_coodinate_Y, X+179, draw_coodinate_Y+5, color_glyph_drop, color_glyph_drop)
elseif glyph_list[g_drop] then
gui.drawBox(X, draw_coodinate_Y, X+179, draw_coodinate_Y+5, color_glyph_undrop, color_glyph_undrop)
else
gui.drawBox(X, draw_coodinate_Y, X+179, draw_coodinate_Y+5, color_background, color_background)
end
predict_glyph(X, draw_coodinate_Y)
draw_coodinate_Y = draw_coodinate_Y + 8
end
end
end
end
extra_counter_anchor = 0
hit_box_switch = false
while true do
update_RAM()
update_key_pressed()
--frame counter
if user_input.Keypad0 then
extra_counter_anchor = emu.framecount()
end
extra_counter = emu.framecount() - extra_counter_anchor
moive_mode = movie.mode()
gui.pixelText(256,354,emu.framecount()..":"..extra_counter.." ("..moive_mode..")")
--lag counter
gui.pixelText(256,364,emu.lagcount(),"red")
predict_switch()
watch_RAM()
if user_input.Keypad9 then
hit_box_switch = not hit_box_switch
end
if hit_box_switch then
draw_hitbox()
end
if predict_switch_count == 0 then
gui.pixelText(266, 10, "OFF")
end
if predict_switch_count == 1 then
predict_enemy_drop(266, 2)
end
if predict_switch_count == 2 then
draw_entity_list(260, 10)
end
if client.ispaused() then
emu.yield()
else
emu.frameadvance()
end
end