--[[Card prize scanner bot v2
(GBA) Kingdom Hearts: Chain of Memories
FatRatKnight
Make a save state just before you clear a battle. Find the exact frame where
the white flash fills the screen, then select a spot four frames before that.
Now run this script, and then unpause emulation.
The script should be able to gather information on its own while you kick back
and enjoy a relaxing drink. More specifically, the script saves a state where
you started it, then advances a few frames to see what shows up. Once it
identifies the prize, it records it and reloads state. In the meantime, it will
display its current findings.
The row specifies type of card, as well as the first 7 characters of the name.
The column specifies value of card, going from left to right in order of
increasing values. Leftmost is 0, and goes up as you go to the right.
The numbers in cyan are the totals for that row or column.
If an enemy card is produced, it will show information in the lower-left
corner: ID of card and how many times it has shown up.
In the lower right corner is the total number of cards the script has seen, not
counting the enemy cards, as well as a number in scary-red telling how many
times the script has failed to detect a prize object. I hope this scary-red
number remains zero throughout!
Press the PrintoutKey (probably "Z") to dump results into the Output Console.
If you have emulation paused, you will need to hold the key while advancing a
frame. At this point, highlight it and copy/paste should work then.
I feel as though I have perfected this script! Data gathering should be easy.
]]--
local PrintoutKey= "Z" -- Hold this key, then advance a frame to print results
local R1u= memory.readbyte
local R2s= memory.readwordsigned
local W4s= memory.writedword
local R4s= memory.readdwordsigned
local Keys, LastKeys= input.get(), input.get()
local function UpdateKeys() LastKeys= Keys; Keys= input.get() end
local function Press(k) return Keys[k] and (not LastKeys[k]) end
--*****************************************************************************
local function GetString_word(addr,len)
--*****************************************************************************
local str= ""
for i= 0, len-1 do
local v= R1u(addr + 2*i)
if v == 0 then break end
str= str .. string.char(v)
end
str= str .. string.rep(" ",len-#str)
return str
end
--*****************************************************************************
local function Roll(rTbl) -- [1], [2], [3], [4]
--*****************************************************************************
return bit.band(0xFFFFFFFF,bit.bxor(
bit.lshift(rTbl[2], 2),bit.lshift(rTbl[4], 1),
bit.rshift(rTbl[1],30),bit.rshift(rTbl[3],31)
))
end
local T= {0,0,0,0}
for i= 1, 4 do
T[i]= R4s(0x02034030+4*(i-1))
end
local z= savestate.create()
savestate.save(z)
local Error= 0
local M_Prize= {}
local E_Prize= {}
--*****************************************************************************
local function Fn()
--*****************************************************************************
local Y= 0
local Total= 0
local ValTotal= {[0]=0,0,0,0,0,0,0,0,0,0}
for k,v in pairs(M_Prize) do
local TypeTotal= 0
gui.text(0,Y,string.format("%2d:%s",k,
GetString_word(R4s(0x09EF7048+4*k),7)
),0x00FF00FF)
for i= 0, 9 do
gui.text(38+18*i,Y,string.format("%4d",v[i]))
ValTotal[i]= ValTotal[i]+v[i]
TypeTotal= TypeTotal+v[i]
end
gui.text(218,Y,string.format("%4d",TypeTotal),0x00FFFFFF)
Total= Total+TypeTotal
Y= Y+6
end
for i= 0, 9 do gui.text(38+18*i,Y,string.format("%4d",ValTotal[i]),0x00FFFFFF) end
gui.text(208,144,string.format("%8d",Total),0xFF8000FF)
local X= 0
for k,v in pairs(E_Prize) do
gui.text(X,152,string.format("%3d:%4d",k,v))
X= X+40
end
gui.text(208,152,string.format("%8d",Error),0xFF2000FF)
end
gui.register(Fn)
--*****************************************************************************
local function PrintData()
--*****************************************************************************
print("ID:Name | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9||Total")
print("--:-----------------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----||-----")
local Total= 0
local ValTotal= {[0]=0,0,0,0,0,0,0,0,0,0}
for k,v in pairs(M_Prize) do
local TypeTotal= 0
local str= string.format("%2d:%s",k,
GetString_word(R4s(0x09EF7048+4*k),17)
)
for i= 0, 9 do
str= string.format("%s|%5d",str,v[i])
ValTotal[i]= ValTotal[i]+v[i]
TypeTotal= TypeTotal+v[i]
end
print(string.format("%s||%6d",str,TypeTotal))
Total= Total+TypeTotal
end
print()
local str= "Totals "
for i= 0, 9 do str= string.format("%s|%5d",str,ValTotal[i]) end
print(str)
print("Total: ",Total)
print()
for k,v in pairs(E_Prize) do
print(string.format("%3d:%s>> %d times",
k,
GetString_word(R4s(0x08F70ABC + k*0x34),17), --Indexing card name
v
))
end
print("Fail count: ",Error)
end
--#############################################################################
--#############################################################################
--*****************************************************************************
local function LocateCombatAddr()
--*****************************************************************************
local addr= R4s(0x02039B84)
--This is the limits of my error checking. Make sure I'm looking correctly!
if addr == 0 then return false end -- No combat
if R4s(addr-0x20) ~= -0x200 then return false end -- Wrong size
if R4s(addr-0x08) ~= 0x081213DC then return false end -- ptr not in use
return addr -- MainCombatPointer
end
--*****************************************************************************
local function IdentifyPrize(obj_RAM)
--*****************************************************************************
--I have "PrizeCardInit". "PrizeCard" is two indirections away.
local obj_PrizeCard= R4s(R4s(obj_RAM+0x10)+4)
--Now I have the object!
local ID,val= R2s(obj_PrizeCard+0x3C),R2s(obj_PrizeCard+0x3E)
M_Prize[ID]= M_Prize[ID] or {[0]=0,0,0,0,0,0,0,0,0,0}
M_Prize[ID][val]= M_Prize[ID][val]+1
return true -- We're good
end
--*****************************************************************************
local function HeartlessPrize(obj_RAM)
--*****************************************************************************
--I have the object!
local CardID= R2s(obj_RAM+0x1A0)
E_Prize[CardID]= E_Prize[CardID] or 0 -- Ensure it exists
E_Prize[CardID]= E_Prize[CardID]+1
return true -- Data recorded
end
--*****************************************************************************
local function SeekPrize(addr) --Input: Obj_active_start
--*****************************************************************************
while addr ~= 0 do
local obj= R4s(addr)
local ROM_ptr= R4s(obj)
if ROM_ptr == 0x9EE75F0 then --PrizeCardInit
return IdentifyPrize(R4s(obj+4))
elseif ROM_ptr == 0x9EE77A4 then --Heartless card
return HeartlessPrize(R4s(obj+4))
end
addr= R4s(addr+8) -- Next object
end
return false -- Something wrong...
end
--*****************************************************************************
local function HandleFrame()
--*****************************************************************************
UpdateKeys()
if Press(PrintoutKey) then PrintData() end
emu.frameadvance()
end
--*****************************************************************************
local function Botty()
--*****************************************************************************
while true do
--Run the simulation
for i= 1, 4 do
W4s(0x02034030+4*(i-1),T[i])
end
for t= 0, 6 do HandleFrame() end
--Analyze the data
local addr= LocateCombatAddr()
if addr then
addr= R4s(addr+0x34)
if not SeekPrize(addr) then Error=Error+1 end
else
Error=Error+1
end
-- local ID,v= R1u(0x02018F9C),R1u(0x02018F9E)
-- tbl[ID]= tbl[ID] or {[0]=0,0,0,0,0,0,0,0,0,0}
-- tbl[ID][v]= tbl[ID][v]+1
--Reset the experiment
savestate.load(z)
table.insert(T,1,Roll(T)); T[5]= nil
end
end
Botty()
--*****************************************************************************
while true do
--*****************************************************************************
emu.frameadvance()
end