Advertisement
Dessyreqt

Super Metroid Recorder Script

Nov 14th, 2014
332
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.87 KB | None | 0 0
  1. --How many frames ahead of the "action" to start savestates
  2. preFrames = 45
  3.  
  4. --Game-specific RAM addresses
  5. RAM = {
  6.     words = {
  7.         hour = 0x7e09e0,
  8.         min = 0x7e09de,
  9.         sec = 0x7e09dc,
  10.         mil = 0x7e09da,
  11.         missiles = 0x7e09c8,
  12.         superMissiles = 0x7e09cc,
  13.         powerBombs = 0x7e09d0,
  14.         energyTanks = 0x7e09c4,
  15.         reserveTanks = 0x7e09d4,
  16.         items = 0x7e09a4,
  17.         weapons = 0x7e09a8,
  18.         health = 0x7e09c2,
  19.         reserve = 0x7e09d6
  20.     },
  21.     bytes = {
  22.         itemScene = 0x7e0009,
  23.         doorScene = 0x7e05f5,
  24.         elevatorScene = 0x7e0e18,
  25.         transition = 0x7e0925,
  26.         inGame = 0x7e0054 --128 if in game
  27.     }
  28. }
  29.  
  30. --Game-specific information that needs to be monitored
  31. gameInfo = {
  32.     time = {
  33.         hour = 0,
  34.         min = 0,
  35.         sec = 0,
  36.         mil = 0
  37.     },
  38.     pct = 0,
  39.     scene = {
  40.         item = 0,
  41.         door = 0,
  42.         elevator = 0,
  43.         transition = 0,
  44.         transitionStarted = false,
  45.     },
  46.     health = 0,
  47.     reserve = 0
  48. }
  49.  
  50. --Locations to output text
  51. output = {
  52.     time = {
  53.         x = 0,
  54.         y = 190
  55.     },
  56.     gameInfo = {
  57.         x = 0,
  58.         y = 200
  59.     }  
  60. }
  61.  
  62. --Tells the script when to do nothing regarding savestates
  63. function DoNothing()
  64.     UpdateGameInfo()
  65.    
  66.     if not GameActive() or memory.readbyte(RAM.bytes.inGame) ~= 128 then
  67.         return true
  68.     end
  69.     return false
  70. end
  71.  
  72. --Sets game information at the beginning of an attempt
  73. function SetGameInfo()
  74.     --set in-game time
  75.     gameInfo.hour = memory.readword(RAM.words.hour)
  76.     gameInfo.min = memory.readword(RAM.words.min)
  77.     gameInfo.sec = memory.readword(RAM.words.sec)
  78.     gameInfo.mil = math.floor(memory.readword(RAM.words.mil) * 100 / 60)
  79.  
  80.     gameInfo.scene.item = memory.readbyte(RAM.bytes.itemScene)
  81.     gameInfo.scene.door = memory.readbyte(RAM.bytes.doorScene)
  82.     gameInfo.scene.elevator = memory.readbyte(RAM.bytes.elevatorScene)
  83.     gameInfo.scene.transition = memory.readbyte(RAM.bytes.transition)
  84.    
  85.     gameInfo.health = memory.readword(RAM.words.health)
  86.     gameInfo.reserve = memory.readword(RAM.words.reserve)
  87.  
  88.     --set percentage
  89.     local missiles = math.floor(memory.readbyte(RAM.words.missiles) / 5)
  90.     local superMissiles = math.floor(memory.readbyte(RAM.words.superMissiles) / 5)
  91.     local powerBombs = math.floor(memory.readbyte(RAM.words.powerBombs) / 5)
  92.     local energyTanks = math.floor((memory.readword(RAM.words.energyTanks) - 99) / 100)
  93.     local reserveTanks = math.floor(memory.readword(RAM.words.reserveTanks) / 100)
  94.  
  95.     local items = memory.readword(RAM.words.items)
  96.     local variaSuit = items % 2
  97.     local springBall = math.floor((items % 4) / 2)
  98.     local morphBall = math.floor((items % 8) / 4)
  99.     local screwAttack = math.floor((items % 16) / 8)
  100.     local gravitySuit = math.floor((items % 64) / 32)
  101.     local hiJumpBoots = math.floor((items % 512) / 256)
  102.     local spaceJump = math.floor((items % 1024) / 512)
  103.     local bomb = math.floor((items % 8192) / 4096)
  104.     local speedBooster = math.floor((items % 16384) / 8192)
  105.     local grappleBeam = math.floor((items % 32768) / 16384)
  106.     local xRayScope = math.floor(items / 32768)
  107.  
  108.     local weapons = memory.readword(RAM.words.weapons)
  109.     local waveBeam = weapons % 2
  110.     local iceBeam = math.floor((weapons % 4) / 2)
  111.     local spazerBeam = math.floor((weapons % 8) / 4)
  112.     local plasmaBeam = math.floor((weapons % 16) / 8)
  113.     local chargeBeam = math.floor((weapons % 8192) / 4096)
  114.  
  115.     gameInfo.pct = missiles + superMissiles + powerBombs + energyTanks + reserveTanks + variaSuit + springBall + morphBall + screwAttack + gravitySuit + hiJumpBoots + spaceJump + bomb + speedBooster + grappleBeam + xRayScope + waveBeam + iceBeam + spazerBeam + plasmaBeam + chargeBeam
  116. end
  117.  
  118. function CalculateRtaTime()
  119.     frames = snes9x.framecount()
  120.     hours = math.floor(frames / 216000)
  121.     minutes = math.floor((frames - hours * 216000) / 3600)
  122.     seconds = math.floor((frames - hours * 216000 - minutes * 3600) / 60)
  123.     milliseconds = math.floor((frames - hours * 216000 - minutes * 3600 - seconds * 60) * 100 / 60)
  124. end
  125.  
  126. --Outputs relevant game information
  127. function OutputGameInfo()
  128.     CalculateRtaTime()
  129.     gui.text(output.gameInfo.x, output.gameInfo.y, "Pct: " .. gameInfo.pct .. "%")
  130.     gui.text(output.gameInfo.x, output.gameInfo.y + 10, "In-Game Time: " .. gameInfo.hour .. ":" .. string.format("%02d",gameInfo.min) .. ":" .. string.format("%02d",gameInfo.sec) .. "." .. string.format("%02d",gameInfo.mil) .. "    RTA Time: " .. hours .. ":" .. string.format("%02d",minutes) .. ":" .. string.format("%02d",seconds) .. "." .. string.format("%02d",milliseconds))
  131. end
  132.  
  133. function InScene()
  134.     if gameInfo.scene.transition == 10 then
  135.         gameInfo.scene.transitionStarted = true
  136.     end
  137.    
  138.     if ((gameInfo.scene.item + gameInfo.scene.elevator > 0) or (gameInfo.scene.door > 0 and gameInfo.scene.transition >= 10 and gameInfo.scene.transitionStarted)) then
  139.         if (gameInfo.scene.item + gameInfo.scene.door + gameInfo.scene.elevator ~= 111) then
  140.             return true
  141.         end
  142.     end
  143.    
  144.     gameInfo.scene.transitionStarted = false
  145.     return false
  146. end
  147.  
  148. function GameActive()
  149.     if (gameInfo.health > 0 or gameInfo.reserve > 0) and (not InScene()) then
  150.         return true
  151.     end
  152.     return false
  153. end
  154.  
  155. --Tells the script that the attempt should continue
  156. function ContinueAttempt()
  157.     if GameActive() then
  158.         return true
  159.     end
  160.     return false
  161. end
  162.  
  163. --Use this function to update info that may be checked later (for example, if you gain a life)
  164. function UpdateGameInfo()
  165.     --set in-game time
  166.     gameInfo.hour = memory.readword(RAM.words.hour)
  167.     gameInfo.min = memory.readword(RAM.words.min)
  168.     gameInfo.sec = memory.readword(RAM.words.sec)
  169.     gameInfo.mil = math.floor(memory.readword(RAM.words.mil) * 100 / 60)
  170.    
  171.     gameInfo.scene.item = memory.readbyte(RAM.bytes.itemScene)
  172.     gameInfo.scene.door = memory.readbyte(RAM.bytes.doorScene)
  173.     gameInfo.scene.elevator = memory.readbyte(RAM.bytes.elevatorScene)
  174.     gameInfo.scene.transition = memory.readbyte(RAM.bytes.transition)
  175.  
  176.     gameInfo.health = memory.readword(RAM.words.health)
  177.     gameInfo.reserve = memory.readword(RAM.words.reserve)
  178. end
  179.  
  180. --Tells the script that the last segment was okay (i.e. you didn't die)
  181. function SegmentOk()
  182.     if gameInfo.health > 0 or gameInfo.reserve > 0 then
  183.         return true
  184.     end
  185.     return false
  186. end
  187.  
  188. --Handles all the hotkey stuff
  189. function HandleKeys()
  190.     keys = input.get()
  191.  
  192.     if press('Q') then
  193.         segment.done = true
  194.     end
  195.    
  196.     if press('R') then
  197.         segment.failed = true
  198.     end
  199.  
  200.     if press('C') then
  201.         segment.failed = true
  202.         segment.bestTime = 999999
  203.         segment.bestInGameTime = 999999
  204.     end
  205.    
  206.     if press('B') then
  207.         segment.done = true
  208.         segment.rollback = true
  209.     end
  210.  
  211.     last_keys = keys                                              
  212. end
  213.  
  214. --Nothing below this point should have to change between games (though it might anyway)
  215.  
  216. segment = {
  217.     prevStart = savestate.create(),
  218.     prevBest = savestate.create(),
  219.     start = savestate.create(),
  220.     best = savestate.create(),
  221.     bestTime = 999999,
  222.     lastTime = 0,
  223.     curTime = 0,
  224.     prevCurTime = 0,
  225.     bestInGameTime = 999999,
  226.     lastInGameTime = 0,
  227.     curInGameTime = 0,
  228.     prevCurInGameTime = 0,
  229.     done = false,
  230.     actionStart = 0,
  231.     failed = false,
  232.     rollback = false
  233. }
  234.  
  235. firstSegment = true
  236. segment.curTime = 0
  237.  
  238. function press(button)
  239.     if keys[button] and not last_keys[button] then
  240.         return true
  241.     end
  242.     return false
  243. end
  244.  
  245. function StartSegment()
  246.     if firstSegment then
  247.         savestate.save(segment.prevStart)
  248.         firstSegment = false
  249.     end
  250.    
  251.     savestate.save(segment.start)
  252.     segment.done = false
  253.     segment.curTime = movie.framecount()
  254.     segment.curInGameTime = gameInfo.hour * 216000 + gameInfo.min * 3600 + gameInfo.sec * 60 + memory.readword(RAM.words.mil)
  255.     if segment.rollback then
  256.         segment.curTime = segment.prevCurTime
  257.         segment.curInGameTime = segment.prevCurInGameTime
  258.     end
  259.     segment.rollback = false
  260.     segment.bestTime = 999999
  261.     segment.bestInGameTime = 999999
  262.     segment.lastTime = 0
  263.     segment.lastInGameTime = 0    
  264. end
  265.  
  266. function OutputTime()
  267.     gui.text(output.time.x,output.time.y,string.format("Last: %d (%d)  Best: %d (%d)", segment.lastTime, segment.lastInGameTime, segment.bestTime, segment.bestInGameTime))          
  268. end
  269.    
  270. while true do
  271.     SetGameInfo()
  272.     StartSegment()
  273.    
  274.     local closerStart = false
  275.  
  276.     while not segment.done do
  277.         segment.failed = false
  278.         savestate.load(segment.start)        
  279.        
  280.         while not segment.done and not segment.failed and (DoNothing()) do
  281.             HandleKeys()
  282.             snes9x.frameadvance()
  283.             OutputGameInfo()
  284.            
  285.             --get a save state that's still in the transition, but close to the action for player control
  286.             if movie.framecount() == segment.actionStart - preFrames then
  287.                 savestate.save(segment.start)
  288.                 closerStart = true
  289.             end
  290.         end
  291.  
  292.         SetGameInfo()
  293.        
  294.         if not closerStart then
  295.             segment.actionStart = movie.framecount()
  296.         end
  297.  
  298.         while not segment.done and not segment.failed and ContinueAttempt() do
  299.             snes9x.frameadvance()
  300.             UpdateGameInfo()
  301.             OutputGameInfo()
  302.             OutputTime()
  303.             HandleKeys()
  304.         end
  305.        
  306.         segment.lastTime = (movie.framecount() - segment.curTime)
  307.         segment.lastInGameTime = gameInfo.hour * 216000 + gameInfo.min * 3600 + gameInfo.sec * 60 + memory.readword(RAM.words.mil) - segment.curInGameTime
  308.        
  309.         if ((segment.bestTime > segment.lastTime) or (segment.bestTime == segment.lastTime and segment.bestInGameTime >= segment.lastInGameTime)) and (segment.lastTime > 10) and not segment.failed and not segment.done and SegmentOk() then
  310.             segment.bestTime = segment.lastTime
  311.             segment.bestInGameTime = segment.lastInGameTime
  312.             savestate.save(segment.best)
  313.         end
  314.     end
  315.    
  316.     if not segment.rollback then
  317.         segment.prevCurTime = segment.curTime
  318.         segment.prevCurInGameTime = segment.curInGameTime
  319.         savestate.save(segment.prevBest)
  320.         savestate.load(segment.best)
  321.         savestate.save(segment.prevStart)
  322.         segment.prevStart, segment.start = segment.start, segment.prevStart
  323.     else
  324.         savestate.load(segment.prevBest)
  325.         savestate.save(segment.best)
  326.         savestate.load(segment.prevStart)
  327.         savestate.save(segment.start)
  328.     end
  329. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement