• Just a reminder that providing specifics on, sharing links to, or naming websites where ROMs can be accessed is against the rules. If your post has any of this information it will be removed.
  • Ever thought it'd be cool to have your art, writing, or challenge runs featured on PokéCommunity? Click here for info - we'd love to spotlight your work!
  • Our weekly protagonist poll is now up! Vote for your favorite Trading Card Game 2 protagonist in the poll by clicking here.
  • 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

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:
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.
 
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.
 
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