• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Emulator Savestate for v17

308
Posts
4
Years
  • This script is for Pokémon Essentials v17 and v17.2.

    This script allows you to save and load as if you are running an emulator with savestates
    - Save a Savestate with the Button's SHIFT + 1-9.
    - Load a Savestate with the Button's 1-9.

    This Script is based on * Emulator Savestate Script for Pokémon Essentials v0.1 by shiney570 ( Skype: imatrix.wt ;Deviantart: shiney570 ;PokeCom: shiney570)
    Also credits to FL because he helped shiney570 a lot with things shiney570 didn't know!

    However, the original version didn't save everything, especially the pokemon data, maybe also the item data. Thus there is a new version.

    Code:
    #===============================================================================
    # * Emulator Savestate Script for Pokémon Essentials v0.1
    #
    # - Save a Savestate with the Button's SHIFT + 1-9.
    # - Load a Savestate with the Button's 1-9.
    #
    #  Have any bugs/questions/suggestions?
    #  - Contact me! ( Skype: imatrix.wt ;Deviantart: shiney570 ;PokeCom: shiney570)
    # 
    # Whenever you use don't forget to give credits. Also credit FL because he 
    # helped me a lot with things I didn't know!
    #
    #===============================================================================
    # * Emulator Savestate Script for Pokémon Essentials v17
    # bug fixes by derFischae
    #===============================================================================
    SAVESTATES                              = true
    ALLOW_SAVESTATES_ONLY_IN_DEBUG_MODE     = false
    
    #===============================================================================
    # * Updating the Inputs.
    #===============================================================================
    class Scene_Map
      def main
        createSpritesets
        Graphics.transition
        loop do
          Graphics.update
          Input.update
          update
          if SAVESTATES
            update_savestates
          end
          if ($saveStateFrameCount && SAVESTATES )
            $saveStateFrameCount+=1
            if $saveStateFrameCount>=80
              $saveStateBitmap.bitmap.clear
              $saveStateFrameCount=nil
            end
          end
          if $scene != self
            break
          end
        end
        Graphics.freeze
        disposeSpritesets
        if $game_temp.to_title
          Graphics.transition
          Graphics.freeze
        end
      end
    end
    #===============================================================================
    # * Method for Updating Savestates
    #===============================================================================
    def update_savestates
        if $DEBUG || (!$DEBUG && (!ALLOW_SAVESTATES_ONLY_IN_DEBUG_MODE))
          sub=30
          for i in 1..9
            if Input.press?(sub+i)
              if !$game_player.moving?
                if !$saveStateBitmap
                  $saveStateBitmap=BitmapSprite.new(DEFAULTSCREENWIDTH,100)
                  $saveStateBitmap.z=99999999
                  $saveStateBitmap.bitmap.font.name="Arial"
                  $saveStateBitmap.bitmap.font.size=25
                end
                if Input.press?(Input::SHIFT)
                  pbSaveStage("#{i}")
                else
                  pbLoadStage("#{i}")
                end
              else
                Kernel.pbMessage("Player is moving")
              end
            end
          end
        end
      end
    #==================================================================================
    # * Save Stages.
    #=================================================================================
    #========version of derFischae
    def pbSaveStage(slot)
      safesave=false
      $Trainer.metaID=$PokemonGlobal.playerID
      # Creates Savestate folder if it doesn't exists yet.
      if !(safeExists?("Data/Savestates"))
        Dir.mkdir("Data/Savestates")
      end
      savefile="Data/Savestates/savestate#{slot}.rxdata"
      #savefile = RTP.getSaveFileName("Savestate#{slot}.rxdata")
      begin
        File.open(savefile,"wb"){|f|
           Marshal.dump($Trainer,f)
           Marshal.dump(Graphics.frame_count,f)
           if $data_system.respond_to?("magic_number")
             $game_system.magic_number = $data_system.magic_number
           else
             $game_system.magic_number = $data_system.version_id
           end
           $game_system.save_count+=1
           Marshal.dump($game_system,f)
           Marshal.dump($PokemonSystem,f)
           Marshal.dump($game_map.map_id,f)
           Marshal.dump($game_switches,f)
           Marshal.dump($game_variables,f)
           Marshal.dump($game_self_switches,f)
           Marshal.dump($game_screen,f)
           Marshal.dump($MapFactory,f)
           Marshal.dump($game_player,f)
           $PokemonGlobal.safesave=safesave
           Marshal.dump($PokemonGlobal,f)
           Marshal.dump($PokemonMap,f)
           Marshal.dump($PokemonBag,f)
           Marshal.dump($PokemonStorage,f)
        }
        Graphics.frame_reset
      rescue
        return false
      end
      printSavestateMessage("Saved State to Slot #{slot}.")
      return true
    end
    #==================================================================================
    # * Load Stages.
    #=================================================================================
    #=====Version of derFischae
    def pbLoadStage(slot)
      $PokemonTemp   = PokemonTemp.new
      $game_temp     = Game_Temp.new
      $game_system   = Game_System.new
      $PokemonSystem = PokemonSystem.new if !$PokemonSystem
      savefile="Data/Savestates/savestate#{slot}.rxdata"
      #savefile = RTP.getSaveFileName("Savestate#{slot}.rxdata")
      if !(safeExists?(savefile))
        printSavestateMessage("Savestate from Slot #{slot} was not found.")
        return
      end
      FontInstaller.install
      data_system = pbLoadRxData("Data/System")
      mapfile = ($RPGVX) ? sprintf("Data/Map%03d.rvdata",data_system.start_map_id) :
                           sprintf("Data/Map%03d.rxdata",data_system.start_map_id)
      if data_system.start_map_id==0 || !pbRgssExists?(mapfile)
        Kernel.pbMessage(_INTL("No starting position was set in the map editor.\1"))
        Kernel.pbMessage(_INTL("The game cannot continue."))
        @scene.pbEndScene
        $scene = nil
        return
      end
      if safeExists?(savefile)
        trainer      = nil
        framecount   = 0
        mapid        = 0
        haveBackup   = false
        showContinue = false
        begin
          trainer       = nil
          framecount    = nil
          game_system   = nil
          pokemonSystem = nil
          mapid         = nil
          File.open(savefile){|f|
            trainer       = Marshal.load(f)
            framecount    = Marshal.load(f)
            game_system   = Marshal.load(f)
            pokemonSystem = Marshal.load(f)
            mapid         = Marshal.load(f)
          }
          raise "Corrupted file" if !trainer.is_a?(PokeBattle_Trainer)
          raise "Corrupted file" if !framecount.is_a?(Numeric)
          raise "Corrupted file" if !game_system.is_a?(Game_System)
          raise "Corrupted file" if !pokemonSystem.is_a?(PokemonSystem)
          raise "Corrupted file" if !mapid.is_a?(Numeric)
          $game_system=game_system
          $PokemonSystem=pokemonSystem
          showContinue = true
        rescue
          if safeExists?(savefile+".bak")
            begin
              trainer, framecount, $game_system, $PokemonSystem, mapid = pbTryLoadFile(savefile+".bak")
              haveBackup   = true
              showContinue = true
            rescue
            end
          end
          if haveBackup
            Kernel.pbMessage(_INTL("The save file is corrupt. The previous save file will be loaded."))
          else
            Kernel.pbMessage(_INTL("The save file is corrupt, or is incompatible with this game."))
            if !Kernel.pbConfirmMessageSerious(_INTL("Do you want to delete the save file and start anew?"))
              $scene = nil
              return
            end
            begin; File.delete(savefile); rescue; end
            begin; File.delete(savefile+".bak"); rescue; end
            $game_system   = Game_System.new
            $PokemonSystem = PokemonSystem.new if !$PokemonSystem
            Kernel.pbMessage(_INTL("The save file was deleted."))
          end
        end
        if showContinue
          if !haveBackup
            begin; File.delete(savefile+".bak"); rescue; end
          end
        end
      end
      $ItemData = readItemList("Data/items.dat")
      unless safeExists?(savefile)
        pbPlayBuzzerSE
        next
      end
      metadata = nil
      File.open(savefile){|f|
        Marshal.load(f) # Trainer already loaded
        $Trainer             = trainer
        Graphics.frame_count = Marshal.load(f)
        $game_system         = Marshal.load(f)
        Marshal.load(f) # PokemonSystem already loaded
        Marshal.load(f) # Current map id no longer needed
        $game_switches       = Marshal.load(f)
        $game_variables      = Marshal.load(f)
        $game_self_switches  = Marshal.load(f)
        $game_screen         = Marshal.load(f)
        $MapFactory          = Marshal.load(f)
        $game_map            = $MapFactory.map
        $game_player         = Marshal.load(f)
        $PokemonGlobal       = Marshal.load(f)
        metadata             = Marshal.load(f)
        $PokemonBag          = Marshal.load(f)
        $PokemonStorage      = Marshal.load(f)
        magicNumberMatches = false
        if $data_system.respond_to?("magic_number")
          magicNumberMatches = ($game_system.magic_number==$data_system.magic_number)
        else
          magicNumberMatches = ($game_system.magic_number==$data_system.version_id)
        end
        if !magicNumberMatches || $PokemonGlobal.safesave
          if pbMapInterpreterRunning?
            pbMapInterpreter.setup(nil,0)
          end
          begin
            $MapFactory.setup($game_map.map_id) # calls setMapChanged
          rescue Errno::ENOENT
            if $DEBUG
              Kernel.pbMessage(_INTL("Map {1} was not found.",$game_map.map_id))
              map = pbWarpToMap
              if map
                $MapFactory.setup(map[0])
                $game_player.moveto(map[1],map[2])
              else
                $game_map = nil
                $scene = nil
                return
              end
            else
              $game_map = nil
              $scene = nil
              Kernel.pbMessage(_INTL("The map was not found. The game cannot continue."))
            end
          end
          $game_player.center($game_player.x, $game_player.y)
        else
          $MapFactory.setMapChanged($game_map.map_id)
        end
      }
      if !$game_map.events # Map wasn't set up
        $game_map = nil
        $scene = nil
        Kernel.pbMessage(_INTL("The map is corrupt. The game cannot continue."))
        return
      end
      $PokemonMap = metadata
      $PokemonEncounters = PokemonEncounters.new
      $PokemonEncounters.setup($game_map.map_id)
      pbAutoplayOnSave
      $game_map.update
      $PokemonMap.updateMap
      $scene = Scene_Map.new
      printSavestateMessage("Loaded State from Slot #{slot}.")
      return
    end
    
    
    
    
    #===============================================================================
    # * Printing Message.
    #===============================================================================
    def printSavestateMessage(message)
      $saveStateBitmap.bitmap.clear
      textos=[]
      textos.push([message,5,5,false,Color.new(254,254,76),Color.new(35,35,35)])
      pbDrawTextPositions($saveStateBitmap.bitmap,textos)
      $saveStateFrameCount=0
    end
    
    #===============================================================================
    # * defineing inputs 1-9.
    #===============================================================================
    module Input    
      NUMBER0 = 30 
      NUMBER1 = 31
      NUMBER2 = 32
      NUMBER3 = 33
      NUMBER4 = 34
      NUMBER5 = 35
      NUMBER6 = 36
      NUMBER7 = 37
      NUMBER8 = 38
      NUMBER9 = 39
      class << self
        alias old_self_button_to_key_shiney_123 :buttonToKey
      end
      
      def self.buttonToKey(button)
        case button
        when Input::NUMBER0
          return [0x30] # 0
        when Input::NUMBER1
          return [0x31] # 1
        when Input::NUMBER2
          return [0x32] # 2
        when Input::NUMBER3
          return [0x33] # 3
        when Input::NUMBER4
          return [0x34] # 4
        when Input::NUMBER5
          return [0x35] # 5
        when Input::NUMBER6
          return [0x36] # 6
        when Input::NUMBER7
          return [0x37] # 7
        when Input::NUMBER8
          return [0x38] # 8
        when Input::NUMBER9
          return [0x39] # 9
        end 
        self.old_self_button_to_key_shiney_123(button)
      end
    end
     
    Last edited:

    IndieLynne

    IndieLynne
    2
    Posts
    4
    Years
  • Thank you! I haven't been able to work on my game, DuskGhost, in a few months, but when I get the chance I will add this. Hopefully the issue will be solved.
     
    971
    Posts
    7
    Years
    • Age 21
    • Seen Nov 28, 2022
    I would also suggest saving $PokemonTemp, $game_temp, $game_system and $PokemonSystem to the savestate file, as these contain important information that will otherwise be reset upon loading the savestate. For instance, flash sprites, fly data, surf data, whether the save is or isn't a new game, the map's BGM, map transfer data, active message data, text speed, battle scene and style, frame, textskin, font, screensize, border, language, run style, bgm/se volume, and text input type. You're also missing out on the actively running event, as well as the position and frame of the message box, how often has been saved so far, and the position of the playing BGM.

    In other words, crucial data.
     
    308
    Posts
    4
    Years
  • Thank you for your report.
    This script saves all data an ordinary savestate also stores.
    So this script is fine for having multiple savestates and switching easily between them. And I'm fine with that.
    Maybe the name of this script is problematic since it suggests that you can store everything at any time.
    But this name comes from shiney570 who did the original version this script is based on and I will not change it now.

    If you want to make the savestates to be real emulator savestates, then feel free to update the code and implement all that data you mentioned.
     
    Back
    Top