User File #638519240293555563

Upload All User Files

#638519240293555563 - Pac-Man World 3 DS Autosplitter (IGT/LRT)

Pac-Man World 3 DS Autosplitter.lua
30 downloads
Uploaded 5/21/2024 9:33 PM by inconsistent (see all 30)
Livesplit Autosplitter for DS Pac-Man World 3. This is the version with a load-time remover (compare as game time). Run it through Bizhawk's Lua console and it should work. Only tested with Bizhawk 2.9.1, but it could work on other emulators like DeSmuME with minimal changes.
-- Autosplitter for Pac-Man World 3 DS - @inconsistent_dg
-- Adapted from Trysdyn Black's autosplitters https://github.com/trysdyn/bizhawk-speedrun-lua/
-- Requires LiveSplit 1.7+. Only tested on BizHawk 2.9.1

local current_level = -1
local in_title_screen = true
local boss_hp = 0
local boss_hp_initialized = false
local igt_running_address

local function init_livesplit()
    local rom_hash = gameinfo.getromhash()
    
    -- Check region
    if rom_hash == "40A92219FB2D1F5905E1CF18684690AF" then -- US
        title_screen_address = 0x148E57
        level_address = 0x086D34
        boss_hp_address = 0x26C568
        igt_running_address = 0x086D30
    elseif rom_hash == "C64BDA6908D73D65973E994ED9363DEC" then -- EU
        title_screen_address = 0x149A97
        level_address = 0x0862F4
        boss_hp_address = 0x271B48
        igt_running_address = 0x0862F0
    else -- Error if neither
        error("\nLoaded ROM isn't DS PMW3!\n" ..
              "Make sure to run an official version of DS PMW3,\n" .. 
              "then load this script again")
    end

    pipe_handle = io.open("//./pipe/LiveSplit", 'a')

    if not pipe_handle then
        error("\nFailed to open LiveSplit named pipe!\n" ..
              "Please make sure LiveSplit is running and is at least 1.7, " ..
              "then load this script again")
    end

    pipe_handle:write("reset\r\n")
    pipe_handle:flush()

    return pipe_handle
end

local function write_to_livesplit(command)
    if pipe_handle then
        pipe_handle:write(command .. "\r\n")
        pipe_handle:flush()
    else
        error("pipe_handle is not initialized")
    end
end

local function check_title_screen()
    local title_screen = memory.readbyte(title_screen_address)
    if title_screen == 1 then
        if not in_title_screen then
            -- Reset when going back to title screen
            write_to_livesplit("reset")
        end
        in_title_screen = true
    else
        if in_title_screen then
            -- Start timer when leaving title screen
            write_to_livesplit("starttimer")
            in_title_screen = false
        end
    end
end

local function check_level()
    local new_level = memory.readbyte(level_address)
    if new_level > current_level and new_level ~= 16 then
        -- Split when the level increases, except when the level is 16
        -- The 2nd half of Gogekka Heights is 16, so it shouldn't split
        write_to_livesplit("split")
    end

    if new_level == 16 then
        -- Set the current level back to 7 after level 16
        -- So the rest of the splits work normally
        current_level = 7
    else
        current_level = new_level
    end
end

local function check_final_boss()
    local new_boss_hp = memory.readbyte(boss_hp_address)
    if current_level == 13 then
        if new_boss_hp == 3 then
            boss_hp_initialized = true
        elseif new_boss_hp == 0 and boss_hp_initialized then
            -- Time ends when boss HP goes to 0 after being initialized to 3
            write_to_livesplit("split")
            boss_hp_initialized = false -- Reset the flag after the split
        end
    end
    boss_hp = new_boss_hp
end

local previous_igt_running = nil

local function check_igt()
    local igt_running = memory.readbyte(igt_running_address)
    if igt_running ~= previous_igt_running then
        if igt_running == 4 then
            write_to_livesplit("unpausegametime")
        else
            write_to_livesplit("pausegametime")
        end
        previous_igt_running = igt_running
    end
end


local pipe_handle = nil
local success, err = pcall(function()
    pipe_handle = init_livesplit()
end)

if not success then
    print(err)
    return
end

memory.usememorydomain("Main RAM") -- Required for NDS memory grabs

while true do
    check_title_screen()
    check_igt()
    check_level()
    check_final_boss()
    emu.frameadvance()
end