User File #638700540172624460

Upload All User Files

#638700540172624460 - Castlevania Order of Ecclesia drop predict+entity slot view

OoE(a).lua
12 downloads
Uploaded 4 days ago by hellagels (see all 15)
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