-- This script is for snes9x, but can probably easily be made to work with -- BizHawk or other emulators. -- -- Press Z to toggle verbosity of the HUD -- Press X to toggle collision map -- -- Contact Pinkus on Discord for any bugs or wanted changes. local SETTINGS = { ["hud"] = 1, ["tilemap"] = false, } local function tohex(num, i) return string.format("%0" .. tostring(i) .. "X", num) end local function in_overworld() return memory.readbyte(0x7E001B) == 0 end local function get_quad(quad_hori, quad_vert) if quad_hori == 0 and quad_vert == 0 then return "<^" elseif quad_hori ~= 0 and quad_vert == 0 then return "^>" elseif quad_hori == 0 and quad_vert ~= 0 then return "" end return "wut" end local function print_write(a, b) print(tohex(a, 4) .. "[" .. tohex(b, 2) .. "]", " -> ", tohex(memory.readwordunsigned(a), 2), "x:", tohex(memory.getregister("x"), 4), "pc:", tohex(memory.getregister("pc"), 6)) end local function get_uw_tile(y, x, tilemap_mask, layer) local tmp_y = bit.band(y, tilemap_mask) local tmp_x = bit.rshift(bit.band(x, tilemap_mask), 3) local coord_y = bit.lshift(bit.band(tmp_y, 0xFFF8), 3) local coord_x = bit.band(tmp_x, 0x003F) local tile_idx = coord_x + coord_y if layer == 1 then tile_idx = tile_idx + 0x1000 end tile_idx = bit.band(tile_idx, 0xFFFF) local tile = memory.readbyteunsigned(0x7F2000 + tile_idx) return {tile_idx, tile} end local function get_ow_tile(y, x, tilemap_mask) local tilemap_x_offset = memory.readwordunsigned(0x7E0708) local tilemap_x_mask = memory.readwordunsigned(0x7E070A) local tilemap_y_offset = memory.readwordunsigned(0x7E070C) local tilemap_y_mask = memory.readwordunsigned(0x7E070E) local tmp_02 = bit.rshift(bit.band(x, tilemap_mask), 3) local tmp_00 = bit.band(y, tilemap_mask) local tmp_06 = bit.lshift(bit.band(tmp_00 - tilemap_x_offset, tilemap_x_mask), 3) local tile_idx = bit.bor(bit.band(tmp_02 - tilemap_y_offset, tilemap_y_mask), tmp_06) tmp_06 = bit.lshift(memory.readwordunsigned(0x7E2000 + tile_idx), 2) local tmp = bit.rshift(bit.band(tmp_00, 8), 2) tmp_06 = bit.bor(tmp_06, tmp) local tile_idx2 = bit.lshift(bit.bor(tmp_06, bit.band(tmp_02, 1)), 1) tmp_06 = memory.readwordunsigned(0x0F8000 + tile_idx2) local tile_idx3 = bit.band(tmp_06, 0x1FF) local tile = memory.readwordunsigned(0x0FFD94 + tile_idx3) if tile >= 0x10 and tile < 0x1C then tile = bit.bor(bit.rol(bit.lshift(bit.band(tile, 0x40), 1), 2), tile) end return {tile_idx, bit.band(tile, 0xFF)} end local collision_map = { -- {{{ [0x01] = 1, [0x02] = 1, [0x03] = 1, [0x04] = 1, [0x0B] = 1, [0x26] = 1, [0x27] = 1, [0x43] = 1, [0x44] = 1, [0x46] = 1, [0x50] = 1, [0x51] = 1, [0x52] = 1, [0x53] = 1, [0x54] = 1, [0x55] = 1, [0x56] = 1, [0x57] = 1, [0x67] = 1, [0x6C] = 1, [0x6D] = 1, [0x6E] = 1, [0x6F] = 1, [0x70] = 1, [0x71] = 1, [0x72] = 1, [0x73] = 1, [0x74] = 1, [0x75] = 1, [0x76] = 1, [0x77] = 1, [0x78] = 1, [0x79] = 1, [0x7A] = 1, [0x7B] = 1, [0x7C] = 1, [0x7D] = 1, [0x7E] = 1, [0x7F] = 1, [0xC0] = 1, [0xC1] = 1, [0xC2] = 1, [0xC3] = 1, [0xC4] = 1, [0xC5] = 1, [0xC6] = 1, [0xC7] = 1, [0xC8] = 1, [0xC9] = 1, [0xCA] = 1, [0xCB] = 1, [0xCC] = 1, [0xCD] = 1, [0xCE] = 1, [0xCF] = 1, [0xF0] = 1, [0xF1] = 1, [0xF2] = 1, [0xF3] = 1, [0xF4] = 1, [0xF5] = 1, [0xF6] = 1, [0xF7] = 1, [0xF8] = 1, [0xF9] = 1, [0xFA] = 1, [0xFB] = 1, [0xFC] = 1, [0xFD] = 1, [0xFE] = 1, [0xFF] = 1, } local semi_collision_map = { [0x0E] = 1, [0x0F] = 1, [0x10] = 1, [0x11] = 1, [0x12] = 1, [0x13] = 1, [0x18] = 1, [0x19] = 1, [0x1A] = 1, [0x1B] = 1, } local chest_map = { [0x58] = true, -- 0x58: chest attribute 0 [0x59] = true, -- 0x59: chest attribute 1 [0x5A] = true, -- 0x5A: chest attribute 2 [0x5B] = true, -- 0x5B: chest attribute 3 [0x5C] = true, -- 0x5C: chest attribute 4 [0x5D] = true, -- 0x5D: chest attribute 5 } -- }}} local ow_names = { -- {{{ [0x00] = "Lost Woods", [0x02] = "NE House", [0x03] = "Spectacle Rock", [0x05] = "Death Mountain East", [0x07] = "TR", [0x0A] = "Death Mountain Cave", [0x0F] = "Waterfall Near Zora's Domain", [0x10] = "Lost Woods Entrance", [0x11] = "Fortune Teller's House", [0x12] = "Teleport Lake", [0x13] = "Sanctuary", [0x14] = "Cemetary", [0x15] = "River Area", [0x16] = "Witch's Hut", [0x17] = "Another Waterfall", [0x18] = "Kakariko Village", [0x1A] = "Another Forest", [0x1B] = "HC", [0x1D] = "Bridge Near Castle", [0x1E] = "EP", [0x22] = "Blacksmiths' House", [0x25] = "Octorok Area", [0x28] = "Fencepost Maze", [0x29] = "Kakariko Library", [0x2A] = "Haunted Grove", [0x2B] = "Before Flute Area", [0x2C] = "Link's House", [0x2D] = "Bridge to HC", [0x2E] = "S of EP", [0x2F] = "Peg Circle", [0x30] = "Desert of Mystery", [0x32] = "Bluffs Near Desert", [0x33] = "Near the Swamp", [0x34] = "Great Swamp N", [0x35] = "Lake Hylia", [0x37] = "Ice Rod Cave", [0x3A] = "Sleeping Man Area", [0x3B] = "Great Swamp S", [0x3C] = "Great Swamp SE", [0x3F] = "Lake Hylia SE", [0x40] = "SW", [0x42] = "NE House", [0x43] = "GT", [0x45] = "Death Mountain East", [0x47] = "TR", [0x4A] = "Magic Cape Cave", [0x4F] = "Mysterious Pond", [0x50] = "Outside SW", [0x51] = "Fortune Teller's House", [0x52] = "Small Lake", [0x53] = "Dark Sanctuary", [0x54] = "Dark Graveyard", [0x55] = "Dark Waterway", [0x56] = "Dark Witch's Hut", [0x57] = "Dark Lake Hylia Shore", [0x58] = "TT", [0x5A] = "House I've Never Seen", [0x5B] = "Pyramid of Power", [0x5D] = "Broken Bridge", [0x5E] = "Hedge Maze", [0x62] = "Locked Chest House", [0x65] = "Octorok Area", [0x68] = "Shovel Game", [0x69] = "Arrow Game", [0x6A] = "Haunted Grove", [0x6B] = "Outside Haunted Grove", [0x6C] = "Dark Link's House", [0x6D] = "Peg Bridge", [0x6E] = "Outside Hedge Maze", [0x6F] = "Dark Peg Circle", [0x70] = "Mire", [0x72] = "Outside Mire", [0x73] = "Outside Dark Swamp", [0x74] = "Dark Swamp N", [0x75] = "Frozen Lake Hylia", [0x77] = "Black Ice Cave", [0x7A] = "Dark Sleeping Man", [0x7B] = "Swamp", [0x7C] = "Dark Swamp SE", [0x7F] = "Dark Waterfall", [0x80] = "Unknown 1, Master Sword Area", [0x88] = "Unknown 2", [0x93] = "Unknown 3", [0x94] = "Unknown 4, Master Sword Area", [0x95] = "Unknown 5, Zora's Domain", [0x96] = "Unknown 6", [0x97] = "Unknown 7", [0x9C] = "Unknown 8", [0x9D] = "Unknown 9", [0x9E] = "Unknown 10, Lost Woods Overlay", [0x9F] = "Unknown 11, Rain", } local function ow_name(idx) return ow_names[idx] or "Unknown!!" end -- }}} local uw_names = { -- {{{ [0x00] = "Ganon", [0x01] = "HC, N Corridor", [0x02] = "HC, Switch", [0x03] = "Houlihan", [0x04] = "TR, Crysta-roller", [0x05] = "Empty Clone", [0x06] = "Swamp, Arrghus[Boss]", [0x07] = "Hera, Moldorm[Boss]", [0x08] = "Cave, Healing Fairy", [0x09] = "PoD", [0x0A] = "PoD, Stalfos Trap", [0x0B] = "PoD, Turtle", [0x0C] = "GT, Entrance", [0x0D] = "GT, Agahnim2[Boss]", [0x0E] = "IP, Entrance", [0x0F] = "Empty Clone", [0x10] = "Ganon Evacuation Route", [0x11] = "HC, Bombable Stock", [0x12] = "Sanctuary", [0x13] = "TR, Hokku-Bokku Key Room 2", [0x14] = "TR, Big Key", [0x15] = "TR", [0x16] = "Swamp, Swimming Treadmill", [0x17] = "Hera, Moldorm Fall", [0x18] = "Cave", [0x19] = "PoD, Dark Maze", [0x1A] = "PoD, Big Chest", [0x1B] = "PoD, Mimics", [0x1C] = "GT, Ice Armos", [0x1D] = "GT, Final Hallway", [0x1E] = "IP, Bomb Floor", [0x1F] = "IP, Big Key", [0x20] = "ATower, Agahnim[Boss]", [0x21] = "HC, Key-rat", [0x22] = "HC, Sewer Text Trigger", [0x23] = "TR, W Exit to Balcony", [0x24] = "TR, Big chest", [0x25] = "Empty Clone", [0x26] = "Swamp, Statue", [0x27] = "Hera, Big Chest", [0x28] = "Swamp, Entrance", [0x29] = "SW, Mothula[Boss]", [0x2A] = "PoD, Big Hub", [0x2B] = "PoD, Fairy", [0x2C] = "Cave", [0x2D] = "Empty Clone", [0x2E] = "IP, Compass", [0x2F] = "Cave, Kakariko Well HP", [0x30] = "ATower, Maiden Sacrifice Chamber", [0x31] = "Hera, Hardhat Beetles", [0x32] = "HC, Sewer Key Chest", [0x33] = "DP, Lanmolas[Boss]", [0x34] = "Swamp, Pre-Big Key", [0x35] = "Swamp, Big Key", [0x36] = "Swamp, Big Chest", [0x37] = "Swamp, Water Fill", [0x38] = "Swamp, Key Pot", [0x39] = "SW, Mothula Hole", [0x3A] = "PoD, Bombable Floor", [0x3B] = "PoD, Conveyor", [0x3C] = "Cave", [0x3D] = "GT, Torch Room 2", [0x3E] = "IP, Conveyor Hellway", [0x3F] = "IP, Map Chest", [0x40] = "ATower, Final Bridge", [0x41] = "HC, First Dark", [0x42] = "HC, 6 Ropes", [0x43] = "DP, Moving Wall", [0x44] = "TT, Big Chest", [0x45] = "TT, Jail Cells", [0x46] = "Swamp, Compass Chest", [0x47] = "Empty Clone", [0x48] = "Empty Clone", [0x49] = "SW, Gibdo Torch Puzzle", [0x4A] = "PoD, Entrance", [0x4B] = "PoD, S Mimics", [0x4C] = "GT, Mini-Helmasaur Conveyor", [0x4D] = "GT, Moldorm", [0x4E] = "IP, Bomb-Jump", [0x4F] = "IP Clone Room, Fairy", [0x50] = "HC, W Corridor", [0x51] = "HC, Throne", [0x52] = "HC, East Corridor", [0x53] = "DP, Popos 2", [0x54] = "Swamp, Upstairs Pits", [0x55] = "Uncle Death", [0x56] = "SW, Key Pot", [0x57] = "SW, Big Key", [0x58] = "SW, Big Chest", [0x59] = "SW, Final Section Entrance", [0x5A] = "PoD, Helmasaur King[Boss]", [0x5B] = "GT, Spike Pit", [0x5C] = "GT, Ganon-Ball Z", [0x5D] = "GT, Gauntlet 1/2/3", [0x5E] = "IP, Lonely Firebar", [0x5F] = "IP, Spike Floor", [0x60] = "HC, W Entrance", [0x61] = "HC, Main Entrance", [0x62] = "HC, East Entrance", [0x63] = "DP, Final Section Entrance", [0x64] = "TT, W Attic", [0x65] = "TT, East Attic", [0x66] = "Swamp, Hidden Chest", [0x67] = "SW, Compass Chest", [0x68] = "SW, Key Chest", [0x69] = "Empty Clone", [0x6A] = "PoD, Rupee", [0x6B] = "GT, Mimics Rooms", [0x6C] = "GT, Lanmolas", [0x6D] = "GT, Gauntlet 4/5", [0x6E] = "IP, Pengators", [0x6F] = "Empty Clone", [0x70] = "HC, Pre Jail Cells", [0x71] = "HC, Boomerang Chest", [0x72] = "HC, Map Chest", [0x73] = "DP, Big Chest", [0x74] = "DP, Map Chest", [0x75] = "DP, Big Key Chest", [0x76] = "Swamp, Water Drain", [0x77] = "Hera, Entrance", [0x78] = "Empty Clone", [0x79] = "Empty Clone", [0x7A] = "Empty Clone", [0x7B] = "GT", [0x7C] = "GT, Exploding Wall", [0x7D] = "GT, Warp Maze", [0x7E] = "IP, Bombable Floor", [0x7F] = "IP, Big Spike Traps", [0x80] = "HC, Jail Cell", [0x81] = "HC", [0x82] = "HC, Basement Chasm", [0x83] = "DP, W Entrance", [0x84] = "DP, Main Entrance", [0x85] = "DP, East Entrance", [0x86] = "Empty Clone", [0x87] = "Hera, Tile", [0x88] = "Empty Clone", [0x89] = "EP, Fairy", [0x8A] = "Empty Clone", [0x8B] = "GT, Spike Skip", [0x8C] = "GT, Big Chest", [0x8D] = "GT, Torches 2", [0x8E] = "IP", [0x8F] = "Empty Clone", [0x90] = "Mire, Vitreous[Boss]", [0x91] = "Mire, Final Switch", [0x92] = "Mire, Switches", [0x93] = "Mire, Floor Switch Puzzle", [0x94] = "Empty Clone", [0x95] = "GT, Final Collapsing Bridge", [0x96] = "GT, Torches 1", [0x97] = "Mire, Torch Puzzle", [0x98] = "Mire, Entrance", [0x99] = "EP, Eyegore Key", [0x9A] = "Empty Clone", [0x9B] = "GT, Warp Maze", [0x9C] = "GT, Invisible Floor Maze", [0x9D] = "GT, Invisible Floor", [0x9E] = "IP, Big Chest", [0x9F] = "IP", [0xA0] = "Mire, Pre-Vitreous", [0xA1] = "Mire, Fish", [0xA2] = "Mire, Bridge Key Chest", [0xA3] = "Mire", [0xA4] = "TR, Trinexx[Boss]", [0xA5] = "GT, Wizzrobes Rooms", [0xA6] = "GT, Moldorm Fall", [0xA7] = "Hera, Fairy", [0xA8] = "EP, Stalfos Spawn", [0xA9] = "EP, Big Chest", [0xAA] = "EP, Map Chest", [0xAB] = "TT, Key Pot", [0xAC] = "TT, Blind The Thief[Boss]", [0xAD] = "Empty Clone", [0xAE] = "IP", [0xAF] = "IP, Ice Bridge", [0xB0] = "ATower, Circle of Pots", [0xB1] = "Mire, Hourglass", [0xB2] = "Mire, Slug", [0xB3] = "Mire, Spike Key Chest", [0xB4] = "TR, Pre-Trinexx", [0xB5] = "TR, Dark Maze", [0xB6] = "TR, Chain Chomps", [0xB7] = "TR, Roller", [0xB8] = "EP, Big Key", [0xB9] = "EP, Lobby Cannonballs", [0xBA] = "EP, Key Pot", [0xBB] = "TT, Hellway", [0xBC] = "TT, Conveyor Toilet", [0xBD] = "Empty Clone", [0xBE] = "IP, Block Puzzle", [0xBF] = "IP Clone Room, Switch", [0xC0] = "ATower, Dark Bridge", [0xC1] = "Mire, Tile", [0xC2] = "Mire, Big Hub", [0xC3] = "Mire, Big Chest", [0xC4] = "TR, Last Switch Puzzle", [0xC5] = "TR, Laser Bridge", [0xC6] = "TR", [0xC7] = "TR, Torch Puzzle", [0xC8] = "EP, Armos Knights[Boss]", [0xC9] = "EP, Entrance", [0xCA] = "??", [0xCB] = "TT, NW Entrance", [0xCC] = "TT, NE Entrance", [0xCD] = "Empty Clone", [0xCE] = "IP, Hole to Kholdstare", [0xCF] = "Empty Clone", [0xD0] = "ATower, Dark Maze", [0xD1] = "Mire, Big Key", [0xD2] = "Mire, Mire02", [0xD3] = "Empty Clone", [0xD4] = "Empty Clone", [0xD5] = "TR, Laser Key", [0xD6] = "TR, Entrance", [0xD7] = "Empty Clone", [0xD8] = "EP, Zeldagamer", [0xD9] = "EP, Canonball", [0xDA] = "EP", [0xDB] = "TT, Main SW Entrance", [0xDC] = "TT, SE Entrance", [0xDD] = "Empty Clone", [0xDE] = "IP, Kholdstare[Boss]", [0xDF] = "Cave", [0xE0] = "ATower, Entrance", [0xE1] = "Cave, Lost Woods HP", [0xE2] = "Cave, Lumberjack's Tree HP", [0xE3] = "Cave, 1/2 Magic", [0xE4] = "Cave, Old Man Cave", [0xE5] = "Cave, Old Man Cave", [0xE6] = "Cave", [0xE7] = "Cave", [0xE8] = "Cave", [0xE9] = "Empty Clone", [0xEA] = "Cave, Spectacle Rock HP", [0xEB] = "Cave", [0xEC] = "Empty Clone", [0xED] = "Cave", [0xEE] = "Cave, Spiral Cave", [0xEF] = "Cave, 5 Chests", [0xF0] = "Cave, Old Man Starting Cave", [0xF1] = "Cave, Old Man Starting Cave", [0xF2] = "House", [0xF3] = "House, Old Woman", [0xF4] = "House, Angry Brothers", [0xF5] = "House, Angry Brothers", [0xF6] = "Empty Clone", [0xF7] = "Empty Clone", [0xF8] = "Cave", [0xF9] = "Cave", [0xFA] = "Cave", [0xFB] = "Cave", [0xFC] = "Empty Clone", [0xFD] = "Cave", [0xFE] = "Cave", [0xFF] = "Cave", } local function uw_name(idx) return uw_names[idx] or "Unknown!!" end -- }}} local mode_names = { -- {{{ [0x00] = "Triforce/Startup", [0x01] = "Game Select", [0x02] = "Copy Player", [0x03] = "Erase Player", [0x04] = "Name Player", [0x05] = "Loading Game", [0x06] = "Pre Dungeon", [0x07] = "Dungeon", [0x08] = "Pre Overworld", [0x09] = "Overworld", [0x0A] = "Pre Overworld (special)", [0x0B] = "Overworld (special)", [0x0C] = "Not Used", [0x0D] = "Blank Screen", [0x0E] = "Messaging", [0x0F] = "Close Spotlight", [0x10] = "Open Spotlight", [0x11] = "OW Hole", [0x12] = "Death", [0x13] = "Victory", [0x14] = "History", [0x15] = "Mirror", [0x16] = "Refilling", [0x17] = "Restart", [0x18] = "Aganon", [0x19] = "Triforce", [0x1A] = "End", [0x1B] = "Location Select", } local function mode_name(idx) return mode_names[idx] or "Unknown!" end -- }}} local submode_names = { -- {{{ [0x0700] = "Default", [0x0701] = "Intra-room", [0x0702] = "Inter-room", [0x0703] = "Overlay change", [0x0704] = "Open key/bk door", [0x0705] = "Animation", [0x0706] = "Upward floor", [0x0707] = "Downward floor", [0x0708] = "In-room staircase", [0x0709] = "Bombing/dash to open door", [0x070A] = "Aga Gtower", [0x070B] = "Turn off water", [0x070C] = "Turn on water", [0x070D] = "Watergate room", [0x070E] = "Inter-room spiral staircases", [0x070F] = "????", [0x0710] = "In-room staircases", [0x0711] = "Add sprite ?", [0x0712] = "Up inter-room staircase", [0x0713] = "Down inter-room staircase", [0x0714] = "Dmg pit", [0x0715] = "Warping", [0x0716] = "Barrier change?", [0x0717] = "Switch step", [0x0718] = "Crystal sequence", [0x0719] = "Mirror", [0x071A] = "Open Ganons door", } local function submode_name(mode, submode) return submode_names[bit.lshift(mode, 8) + submode] or "Unknown!" end -- }}} local function draw_text(base_x, base_y, lines) for i, line in pairs(lines) do local y = base_y + (8 * (i - 1)) gui.text(base_x, y, line) end end local function show_hud() local mode = memory.readbyteunsigned(0x7E0010) local submode = memory.readbyteunsigned(0x7E0011) local bg_layer = memory.readbyteunsigned(0x7E00EE) local ow_scr = memory.readwordunsigned(0x7E008A) local ol_idx = memory.readbyteunsigned(0x7E008C) local dng_scr = memory.readwordunsigned(0x7E00A0) local prev_dng_room = memory.readwordunsigned(0x7E00A2) local floor = memory.readwordunsigned(0x7E00A4) local dng_quad = get_quad(memory.readbyteunsigned(0x7E00A9), memory.readbyteunsigned(0x7E00AA)) local link_x = memory.readwordunsigned(0x7E0022) local link_y = memory.readwordunsigned(0x7E0020) local link_subx = memory.readbyteunsigned(0x7E002B) local link_suby = memory.readbyteunsigned(0x7E002A) local link_uw_scr = math.floor(link_y / 0x200) * 0x10 + math.floor(link_x / 0x200) local link_ow_scr = math.floor(link_y / 0x200) * 0x08 + math.floor(link_x / 0x200) local link_quad = get_quad(bit.band(link_x, 0x100), bit.band(link_y, 0x100)) local bg1_x = memory.readwordunsigned(0x7E00E0) local bg1_y = memory.readwordunsigned(0x7E00E6) local bg1_scr = math.floor(bg1_y / 0x200) * 0x10 + math.floor(bg1_x / 0x200) local bg1_quad = get_quad(bit.band(bg1_x, 0x100), bit.band(bg1_y, 0x100)) local bg2_x = memory.readwordunsigned(0x7E00E2) local bg2_y = memory.readwordunsigned(0x7E00E8) local bg2_scr = math.floor(bg2_y / 0x200) * 0x10 + math.floor(bg2_x / 0x200) local bg2_quad = get_quad(bit.band(bg2_x, 0x100), bit.band(bg2_y, 0x100)) local room_info = memory.readbyteunsigned(0x7E0403) local room_info_verbose = "" local tilemap_mask = memory.readwordunsigned(0x7E00EC) local tile if in_overworld() then tile = get_ow_tile(link_y + 0x14, link_x + 0x8, tilemap_mask) else tile = get_uw_tile(link_y + 0x14, link_x + 0x8, tilemap_mask, layer) end room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x01) > 0 and "C" or "c") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x02) > 0 and "C" or "c") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x04) > 0 and "C" or "c") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x08) > 0 and "C" or "c") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x10) > 0 and "C" or "c") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x20) > 0 and "K" or "k") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x40) > 0 and "K" or "k") room_info_verbose = room_info_verbose .. (bit.band(room_info, 0x80) > 0 and "B" or "b") local ow_scr_info = memory.readbyteunsigned(0x7EF280 + ow_scr) local ow_scr_info_verbose = "" ow_scr_info_verbose = ow_scr_info_verbose .. (bit.band(ow_scr_info, 0x02) > 0 and "S" or "s") ow_scr_info_verbose = ow_scr_info_verbose .. (bit.band(ow_scr_info, 0x020) > 0 and "O" or "o") ow_scr_info_verbose = ow_scr_info_verbose .. (bit.band(ow_scr_info, 0x040) > 0 and "H" or "h") local scroll_info = "" if in_overworld() then local in_darkworld = memory.readbyteunsigned(0x7E0FFF) if in_darkworld > 0 then link_ow_scr = bit.bor(link_ow_scr, 0x40) end if mode == 0x9 and (submode == 0x6 or submode == 0x14) then local direction = memory.readbyteunsigned(0x7E0418) local scroll_destination = memory.readwordunsigned(0x7E0610 + (direction * 2)) local current_scroll = memory.readwordunsigned(0x7E00E2 + (direction >= 0x02 and 0 or 6)) local scroll_offset = math.abs(scroll_destination - current_scroll) local low_offset = bit.band(scroll_offset, 7) if low_offset ~= 0 then scroll_info = "scroll is BAD by " .. tostring(low_offset) .. " pixels" else scroll_info = "scroll will end in " .. tostring(scroll_offset / 8) .. " frames" end elseif mode == 0x9 and submode == 0 then local hori_scroll = memory.readwordunsigned(0x7E00E2) local vert_scroll = memory.readwordunsigned(0x7E00E8) local up = memory.readwordunsigned(0x7E0610) local down = memory.readwordunsigned(0x7E0612) local left = memory.readwordunsigned(0x7E0614) local right = memory.readwordunsigned(0x7E0616) if vert_scroll < up then scroll_info = scroll_info .. "^" end if vert_scroll > down then scroll_info = scroll_info .. "v" end if hori_scroll < left then scroll_info = scroll_info .. "<" end if hori_scroll > right then scroll_info = scroll_info .. ">" end if scroll_info ~= "" then scroll_info = "Infinite transitions: " .. scroll_info end end end local room_info_lines if SETTINGS["hud"] == 2 then room_info_lines = { "Mode: " .. tohex(mode, 2) .. "/" .. tohex(submode, 2) .. " " .. mode_name(mode) .. "/" .. submode_name(mode, submode), "X/Y/L: " .. tohex(link_x, 4) .. " " .. tohex(link_y, 4) .. " " .. tostring(bg_layer), "Sx/Sy: " .. tohex(link_subx, 2) .. " " .. tohex(link_suby, 2), "I/T/M: " .. tohex(tile[1], 4) .. ": " .. tohex(tile[2], 2) .. " " .. tohex(0x7E0000 + bit.band(0x12000 + tile[1], 0x1FFFF), 6), "BG1: " .. tohex(bg1_x, 4) .. ", " .. tohex(bg1_y, 4) .. " BG2: " .. tohex(bg2_x, 4) .. ", " .. tohex(bg2_y, 4), "--------------------", } if in_overworld() then if scroll_info ~= "" then table.insert(room_info_lines, 1, scroll_info) end table.insert(room_info_lines, "OWM: " .. tohex(ow_scr, 4) .. " (" .. ow_name(ow_scr) .. ")") table.insert(room_info_lines, "LNK: " .. tohex(link_ow_scr, 4) .. " (" .. ow_name(link_ow_scr) .. ")") table.insert(room_info_lines, "Info: " .. ow_scr_info_verbose) table.insert(room_info_lines, "OL Idx: " .. tohex(ol_idx, 2)) else table.insert(room_info_lines, "DNG: " .. tohex(dng_scr, 2) .. " " .. dng_quad .. " (" .. uw_name(dng_scr) .. ")") table.insert(room_info_lines, "LNK: " .. tohex(link_uw_scr, 2) .. " " .. link_quad .. " (" .. uw_name(link_uw_scr) .. ")") table.insert(room_info_lines, "BG1: " .. tohex(bg1_scr, 2) .. " " .. bg1_quad .. " BG2: " .. tohex(bg2_scr, 2) .. " " .. bg2_quad) table.insert(room_info_lines, "Room: " .. room_info_verbose) end else if in_overworld() then room_info_lines = { "OWM: " .. tohex(ow_scr, 4), "LNK: " .. tohex(link_ow_scr, 4), "Info: " .. ow_scr_info_verbose, } else room_info_lines = { "DNG: " .. tohex(dng_scr, 2) .. " " .. dng_quad, "LNK: " .. tohex(link_uw_scr, 2) .. " " .. link_quad, "Room: " .. room_info_verbose, } end end draw_text(2, 220 - (8 * #room_info_lines), room_info_lines) end local function show_tilemap() local layer = memory.readwordunsigned(0x7E00EE) local tilemap_mask = memory.readwordunsigned(0x7E00EC) local bg_x = memory.readwordunsigned(0x7E00E2) local bg_y = memory.readwordunsigned(0x7E00E8) local link_x = memory.readwordunsigned(0x7E0022) local link_y = memory.readwordunsigned(0x7E0020) local higgs_x = link_x - bit.band(link_x - bg_x, 0xFF) local higgs_y = link_y - bit.band(link_y - bg_y, 0xFF) local x, y for y = higgs_y, higgs_y + 0x100, 8 do for x = higgs_x, higgs_x + 0x100, 8 do local tile if in_overworld() then tile = get_ow_tile(y, x, tilemap_mask)[2] else tile = get_uw_tile(y, x, tilemap_mask, layer)[2] end local scr_x = (x - higgs_x) - (higgs_x % 8) local scr_y = (y - higgs_y) - (higgs_y % 8) if collision_map[tile] then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#000000A0", "#FFFFFFA0") elseif semi_collision_map[tile] then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#808080A0", "#FFFFFFA0") -- gui.text(scr_x + 3, scr_y + 1, tostring(tile - 0x0B)) elseif chest_map[tile] then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#FF0000A0", "#FFFFFFA0") gui.text(scr_x + 3, scr_y + 1, tostring(tile - 0x57)) elseif tile == 0x60 then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#00FF00A0", "#FFFFFFA0") elseif tile == 0x08 then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#0000FF40", "#0000FF40") elseif tile == 0x09 then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#0000FF10", "#FFFFFF10") elseif tile == 0x54 or tile == 0x63 then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#00FFFFA0", "#FFFFFFA0") elseif tile >= 0xA0 and tile <= 0xA4 then gui.box(scr_x, scr_y, scr_x + 8, scr_y + 8, "#FF00FFA0", "#FFFFFFA0") end end end end function write_underworld_to_file() local bg1_file = io.open("bg1.txt", "w") local bg2_file = io.open("bg2.txt", "w") local tilemap_mask = memory.readwordunsigned(0x7E00EC) for y = 0, (0x200 * 16 - 1), 8 do local bg1_line = {} local bg2_line = {} for x = 0, (0x200 * 16 - 1), 8 do local bg1_idx = get_tile_index(y, x, tilemap_mask, 1) local bg2_idx = get_tile_index(y, x, tilemap_mask, 0) local bg1_tile = memory.readbyteunsigned(0x7F2000 + bg1_idx) local bg2_tile = memory.readbyteunsigned(0x7F2000 + bg2_idx) table.insert(bg1_line, tostring(bg1_tile)) table.insert(bg2_line, tostring(bg2_tile)) end bg1_file:write(table.concat(bg1_line, ",") .. "\n") bg2_file:write(table.concat(bg2_line, ",") .. "\n") end bg1_file:close() bg2_file:close() end local LAST_INPUTS = {} function read_inputs() local toggle_hud = input.get().Z local toggle_tilemap = input.get().X local write_underworld = input.get().C local custom_input = input.get().V if toggle_hud ~= LAST_INPUTS["toggle_hud"] and toggle_hud then SETTINGS["hud"] = SETTINGS["hud"] + 1 if SETTINGS["hud"] > 2 then SETTINGS["hud"] = 0 end end if toggle_tilemap ~= LAST_INPUTS["toggle_tilemap"] and toggle_tilemap then SETTINGS["tilemap"] = not SETTINGS["tilemap"] end if write_underworld ~= LAST_INPUTS["write_underworld"] and write_underworld then -- not useful for most ppl :) -- write_underworld_to_file() end if custom_input ~= LAST_INPUTS["custom_input"] and custom_input then -- memory.writebyte(0x7E0516, inc) end LAST_INPUTS["toggle_hud"] = toggle_hud LAST_INPUTS["toggle_tilemap"] = toggle_tilemap LAST_INPUTS["write_underworld"] = toggle_tilemap LAST_INPUTS["custom_input"] = custom_input end local function tick() local mode = memory.readbyteunsigned(0x7E0010) local submode = memory.readbyteunsigned(0x7E0011) if mode == 0x0C or (mode == 0x0E and submode == 0x01) then return end if SETTINGS.hud > 0 then show_hud() end if SETTINGS.tilemap then show_tilemap() end read_inputs() end -- memory.registerwrite(0x7E0010, 0x500, print_write) -- memory.writeword(0x7E0022, 0x0078) -- memory.writebyte(0x7E00EE, 0x00) while true do tick() snes9x.frameadvance() end