User File #17475295745275850

Upload All User Files

#17475295745275850 - GBA Kingdom Hearts: Chain of Memories -- Prize Card scan bot v2

KH_CardScanBot_v2.lua
702 downloads
Uploaded 9/17/2014 11:40 PM by FatRatKnight (see all 245)
My masterpiece! ... Well, data collection can now be done entirely without my direct input, I hope.
I fixed up the display by flipping it. Rather than the type running off the edge to the right, it now runs downward, with value going to the right instead. I can make much better use of vertical space this way. It still has no concept of screen space, but there's a lot more effective space with the direction I'm having it head.
I also do better than just list out a bunch of IDs for each card. I even identify the name (though I hope I picked the right set of pointers)! The first seven characters of the name is provided, but the full 17 characters is provided if you use the print out button (default: Z) in the script.
Pick a moment just four frames before the white flash. Start the script. Unpause emulation. If you did something wrong, either the little red number goes up (too early or wrong situation), or the exact same card keeps showing up (too late).
I made this script so that all the data collection for prize cards does not have to rely on my hands, though it's also easier for me to use. Also, I'd love to know whether the script is identifying the wrong names.
--[[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