--[[ SNES Madou Monogatari - Hanamaru Daiyouchienji Semi-automated Map Maker, written by gocha. W-I-P! (>_<)]]---- Usually, you don't need to adjust the following size, but if the script-- didn't work properly because of something which begins a dialog,-- decrease the size and try to avoid it (I don't know if it will succeed though).-- The size is originally 256x159, but it must be the multiple of 8.-- Therefore, if you have no problem, just set it to 256x152 (152=8*19).local imagePieceSize = { x = 256, y = 8*19 }local debugDisplay = true-- [ startup ] -----------------------------------------------------------------require("gd")if emu then error("Unknown SNES EmuLua host.") endemu = snes9xif not emu then error("Unknown SNES EmuLua host.") end-- Instant ROM checkif string.char(unpack(memory.readbyterange(0x80ffb2, 4))) ~= "ADYJ" then error("Unsupported ROM: make sure if the current ROM is Madou Monogatari")end-- [ ROM/RAM addresses ] -------------------------------------------------------RAM = { fadeLevel = 0x7e0c5b, -- screen fade level pauseFlag = 0x7e0266, -- becomes 1 when the game is paused overworldFlag = 0x7e0980, -- becomes 1 when the game is overworld map frames = 0x7e1300, -- ingame frame counter arlePosWord = 0x7e16c5, -- Arle's position in OW (0byyyyyyyyyxxxxxxx) arleXPosR = 0x7e0025, arleYPosR = 0x7e0027, arleXPosW = 0x7e4827, arleYPosW = 0x7e4829, bgXPosW = 0x7e16e2, bgYPosW = 0x7e16e6, roomLeft = 0x7e1733, roomTop = 0x7e172f, roomRight = 0x7e1735, roomBottom = 0x7e1731, disableOWMove = 0x7e1401, encounter = 0x7e1408}if memory.readbyte(RAM.fadeLevel) ~= 15 or memory.readbyte(RAM.overworldFlag) ~= 1 then gui.popup("Let the game show the overworld screen, or the script won't run.") returnendif memory.readword(RAM.arleXPosW) % 8 ~= 0 or memory.readword(RAM.arleYPosW) % 8 ~= 0 then gui.popup("Arle's position is odd... you cheated?") returnendif input.popup([[This script scans the game screen continuously, then makes a large map of the current room.Before running the script, you may need to confirm some conditions. * The game shows the overworld screen. To make Arle stand about the center of the room is good. * Make sure if "graphic clip windows" is OFF. Usually, you can toggle it by pressing 8. * Make sure if "text in message" is OFF, or annoying texts will appear in the screenshot. * [NYI] Auto frameskip is not bad, but if you want to control animated sprites properly, set the frame skip rate to 0.Make sure if the frameskip amount is set to 0 (NOT auto frameskip)Warnings: * DO NOT LOAD ANY SAVESTATES DURING RUNNING THE SCRIPT.Continue?]], "okcancel") ~= "ok" then returnendlocal savScriptStart = savestate.create()savestate.save(savScriptStart, "quiet")local snapInfo = { x = 0, y = 0, width = imagePieceSize.x, height = imagePieceSize.y }local room = { left = memory.readword(RAM.roomLeft), top = memory.readword(RAM.roomTop), right = memory.readword(RAM.roomRight), bottom = memory.readword(RAM.roomBottom)}room.scrollWidth, room.scrollHeight = room.right - room.left, room.bottom - room.toproom.width, room.height = room.scrollWidth + 256, room.scrollHeight + 152 -- why it isn't 159?room.xPieces, room.yPieces = math.ceil(room.width / snapInfo.width), math.ceil(room.height / snapInfo.height)local gdMap = gd.createTrueColor(room.width, room.height)local snapProgress = { x = 0, y = 0 }if not gdMap then error("Memory allocation error (gd.createTrueColor failed)")end-- [ main routines ] -----------------------------------------------------------local captStates = { enterPause = 0, quitPause = 1, doScreenShot = 2, didScreenShot = 3, endMapMaking = 4}local captState = captStates.enterPauselocal stateTimeCount = 0local aborted = trueemu.registerexit( function() if aborted then memory.writebyte(RAM.disableOWMove, 0) endend)emu.registerbefore( function() local pad = {} local tookScreenShot = false if captState == captStates.enterPause then -- adjust screen position local targetX, targetY = room.left + (snapProgress.x * snapInfo.width), room.top + (snapProgress.y * snapInfo.height) local targetStepX, targetStepY = math.floor((targetX + 0x70) / 8), math.floor((targetY + 0x58) / 8) local stepXMax, stepYMax = math.floor((room.right + 256) / 8), math.floor((room.bottom + 159) / 8) if targetStepX > stepXMax then targetStepX = stepXMax end if targetStepY > stepYMax then targetStepY = stepYMax end local bgX, bgY = memory.readword(RAM.bgXPosW), memory.readword(RAM.bgYPosW) local arleX, arleY = memory.readword(RAM.arleXPosW), memory.readword(RAM.arleYPosW) local stepX, stepY = math.floor(arleX/8), math.floor(arleY/8) local realStepX, realStepY = math.floor(memory.readword(RAM.arlePosWord) % 128), math.floor(memory.readword(RAM.arlePosWord) / 128) local moveSpeed = 8 -- do not change if stepX == targetStepX and stepY == targetStepY then if stateTimeCount >= 4 and memory.readbyte(RAM.overworldFlag) == 1 and memory.readword(RAM.frames) % 2 == 0 then pad.start = 1 -- autofire the start button memory.writebyte(RAM.disableOWMove, 0) end else if stepX < targetStepX then bgX = bgX + moveSpeed arleX = arleX + moveSpeed stepX = stepX + 1 elseif stepX > targetStepX then bgX = bgX - moveSpeed arleX = arleX - moveSpeed stepX = stepX - 1 end if stepY < targetStepY then bgY = bgY + moveSpeed arleY = arleY + moveSpeed stepY = stepY + 1 elseif stepY > targetStepY then bgY = bgY - moveSpeed arleY = arleY - moveSpeed stepY = stepY - 1 end -- clipping if bgX < room.left then bgX = room.left end if bgY < room.top then bgY = room.top end if bgX > room.right then bgX = room.right end if bgY > room.bottom then bgY = room.bottom end memory.writeword(RAM.bgXPosW, bgX) memory.writeword(RAM.bgYPosW, bgY) memory.writeword(RAM.arleXPosW, arleX) memory.writeword(RAM.arleYPosW, arleY) memory.writebyte(RAM.disableOWMove, 1) if stepX == targetStepX and stepY == targetStepY then stateTimeCount = 0 end end elseif captState == captStates.quitPause then if memory.readbyte(RAM.pauseFlag) == 1 and memory.readword(RAM.frames) % 2 == 0 then pad.start = 1 -- autofire the start button end elseif captState == captStates.doScreenShot then if memory.readbyte(RAM.fadeLevel) == 15 then if stateTimeCount >= 30 then local gdSnap = gd.createFromGdStr(gui.gdscreenshot()) local mapX, mapY = snapProgress.x * snapInfo.width, snapProgress.y * snapInfo.height if (snapProgress.x + 1) == room.xPieces then mapX = room.width - snapInfo.width end if (snapProgress.y + 1) == room.yPieces then mapY = room.height - snapInfo.height end gd.copy(gdMap, gdSnap, mapX, mapY, snapInfo.x, snapInfo.y, snapInfo.width, snapInfo.height) tookScreenShot = true snapProgress.x = snapProgress.x + 1 if not (snapProgress.x < room.xPieces) then snapProgress.x = 0 snapProgress.y = snapProgress.y + 1 end end else stateTimeCount = 0 end elseif captState == captStates.didScreenShot then savestate.load(savScriptStart, "quiet") end joypad.set(1, pad) stateTimeCount = stateTimeCount + 1 -- state transition local newState = captState if captState == captStates.enterPause then if memory.readbyte(RAM.pauseFlag) == 1 then newState = captStates.quitPause end elseif captState == captStates.quitPause then if memory.readbyte(RAM.overworldFlag) == 1 and memory.readbyte(RAM.fadeLevel) == 15 then newState = captStates.doScreenShot end elseif captState == captStates.doScreenShot then if tookScreenShot then if not (snapProgress.y < room.yPieces) then newState = captStates.endMapMaking else newState = captStates.didScreenShot end end elseif captState == captStates.didScreenShot then newState = captStates.enterPause end if captState ~= newState then captState = newState stateTimeCount = 0 end memory.writebyte(RAM.encounter, 0)end)emu.registerafter( function()end)gui.register( function() if not debugDisplay and captState ~= captStates.enterPause then return end local fadeLevel = memory.readbyte(RAM.fadeLevel) if fadeLevel > 15 then fadeLevel = 0 end fadeLevel = 15 gui.opacity(0.68 * fadeLevel / 15.0) local targetX, targetY = room.left + (snapProgress.x * snapInfo.width), room.top + (snapProgress.y * snapInfo.height) local targetStepX, targetStepY = math.floor((targetX + 0x70) / 8), math.floor((targetY + 0x58) / 8) local stepXMax, stepYMax = math.floor((room.right + 256) / 8), math.floor((room.bottom + 159) / 8) if targetStepX > stepXMax then targetStepX = stepXMax end if targetStepY > stepYMax then targetStepY = stepYMax end local bgX, bgY = memory.readword(RAM.bgXPosW), memory.readword(RAM.bgYPosW) local arleX, arleY = memory.readword(RAM.arleXPosW), memory.readword(RAM.arleYPosW) local stepX, stepY = math.floor(arleX/8), math.floor(arleY/8) local realStepX, realStepY = math.floor(memory.readword(RAM.arlePosWord) % 128), math.floor(memory.readword(RAM.arlePosWord) / 128) gui.text(2, 2, string.format([[ARLE (%03X,%03X)BACK (%03X,%03X)-(%03X,%03X)STEP (%d,%d:%d,%d)-(%d,%d)-(%d,%d)SNAP (%d,%d)-(%d,%d)STAT %d(%d)]],arleX, arleY,bgX, bgY, targetX, targetY,stepX, stepY, realStepX, realStepY, targetStepX, targetStepY, stepXMax, stepYMax,snapProgress.x+1, snapProgress.y+1, room.xPieces, room.yPieces,captState, stateTimeCount))end)while captState ~= captStates.endMapMaking do emu.frameadvance()endgdMap:pngEx("hanamaroom.png", 1)emu.message("Saved the map of the room.")savestate.load(savScriptStart, "quiet")aborted = false