• Hi, Guest!
    Some images might be missing as we move away from using embedded images, sorry for the mess!
    From now on, you'll be required to use a third party to host images. You can learn how to add images here, and if your thread is missing images you can request them here.
    Do not use Discord to host any images you post, these links expire quickly!
Save File Calls

v20.1 Save File Calls 2022-12-16

This resource pertains to version 20.1 of Pokémon Essentials.
Pokémon Essentials Version
v20.1 ➖
gift.gif
(The Last Nurse Joy)

A simple script to check for save files from other games and use that data to give the player a reward!
Inspired by features like BW2's keys, Ranger's Manaphy Egg, Mario Kart Wii giving you Rosalina if you played Galaxy, etc.​

Code​

Paste in a new script section above Main, or download as a plugin for v20 here.
Ruby:
def pbSaveFile(name,ver=20)
  case ver
    when 20, 19
      location = File.join("C:/Users",System.user_name,"AppData/Roaming",name)
      return false unless File.directory?(location)
      file = File.join(location, 'Game.rxdata')
      return false unless File.file?(file)
      save_data = SaveData.get_data_from_file(file)
    when 18
      home = ENV['HOME'] || ENV['HOMEPATH']
      return false if home.nil?
      location = File.join(home, 'Saved Games', name)
      return false unless File.directory?(location)
      file = File.join(location, 'Game.rxdata')
      return false unless File.file?(file)
      save_data = SaveData.get_data_from_file(file).clone
      save_data = SaveData.to_hash_format(save_data) if save_data.is_a?(Array)
  end
  return save_data
end



def pbSaveTest(name,test,param=nil,ver=20)
  save = pbSaveFile(name,ver)
  result = false
  test = test.capitalize
    if save
        case test
        when "Exist"
            result = true
        when "Map"
            result = (save[:map_factory].map.map_id == param)
        when "Name"
            result = (save[:player].name == param)
        when "Switch"
            result = (save[:switches][param] == true)
        when "Variable"
            varnum = param[0]
            varval = param[1]
            if varval.is_a?(Numeric)
                result = (save[:variables][varnum] >= varval)
            else
                result = (save[:variables][varnum] == varval)
            end
        when "Party"
            party = save[:player].party
            for i in 0...party.length
                poke = party[i]
                result = true if poke.species == param
            end
        when "Seen"
            if ver == 18
                result = save[:player].seen[param]
            else
                result = (save[:player].pokedex.seen?(param))
            end
        when "Owned"
            if ver == 18
                result = save[:player].owned[param]
            else
                result = (save[:player].pokedex.owned?(param))
            end
        when "Item"
            if ver == 18
                oldbag = save[:bag].clone
                for i in 0...oldbag.pockets.length
                    pocket = oldbag.pockets[i]
                    for j in 0...pocket.length
                        item = pocket[j]
                        if item[0] == param
                            result = true
                            break
                        end
                    end
                end
            else
                result = (save[:bag].has?(param))
            end
        end
    end
  return result
end

#Old utilities to convert prior data
class PokeBattle_Trainer
  attr_accessor :trainertype, :name, :id, :metaID, :outfit, :language
  attr_accessor :party, :badges, :money
  attr_accessor :seen, :owned, :formseen, :formlastseen, :shadowcaught
  attr_accessor :pokedex, :pokegear
  attr_accessor :mysterygiftaccess, :mysterygift

  def self.convert(trainer)
    validate trainer => self
    ret = Player.new(trainer.name, trainer.trainertype)
    ret.id                    = trainer.id
    ret.character_ID          = trainer.metaID if trainer.metaID
    ret.outfit                = trainer.outfit if trainer.outfit
    ret.language              = trainer.language if trainer.language
    trainer.party.each { |p| ret.party.push(PokeBattle_Pokemon.convert(p)) }
    ret.badges                = trainer.badges.clone
    ret.money                 = trainer.money
    trainer.seen.each_with_index { |value, i| ret.pokedex.set_seen(i, false) if value }
    trainer.owned.each_with_index { |value, i| ret.pokedex.set_owned(i, false) if value }
    trainer.formseen.each_with_index do |value, i|
      species_id = GameData::Species.try_get(i)&.species
      next if species_id.nil? || value.nil?
      ret.pokedex.seen_forms[species_id] = [value[0].clone, value[1].clone] if value
    end
    trainer.formlastseen.each_with_index do |value, i|
      species_id = GameData::Species.try_get(i)&.species
      next if species_id.nil? || value.nil?
      ret.pokedex.set_last_form_seen(species_id, value[0], value[1]) if value
    end
    if trainer.shadowcaught
      trainer.shadowcaught.each_with_index do |value, i|
        ret.pokedex.set_shadow_pokemon_owned(i) if value
      end
    end
    ret.pokedex.refresh_accessible_dexes
    ret.has_pokedex           = trainer.pokedex
    ret.has_pokegear          = trainer.pokegear
    ret.mystery_gift_unlocked = trainer.mysterygiftaccess if trainer.mysterygiftaccess
    ret.mystery_gifts         = trainer.mysterygift.clone if trainer.mysterygift
    return ret
  end
end

class PokeBattle_Pokemon
  attr_accessor :name, :species, :form, :formTime, :forcedForm, :fused
  attr_accessor :personalID, :exp, :hp, :status, :statusCount
  attr_accessor :abilityflag, :genderflag, :natureflag, :natureOverride, :shinyflag
  attr_accessor :moves, :firstmoves
  attr_accessor :item, :mail
  attr_accessor :iv, :ivMaxed, :ev
  attr_accessor :happiness, :eggsteps, :pokerus
  attr_accessor :ballused, :markings, :ribbons
  attr_accessor :obtainMode, :obtainMap, :obtainText, :obtainLevel, :hatchedMap
  attr_accessor :timeReceived, :timeEggHatched
  attr_accessor :cool, :beauty, :cute, :smart, :tough, :sheen
  attr_accessor :trainerID, :ot, :otgender, :language
  attr_accessor :shadow, :heartgauge, :savedexp, :savedev, :hypermode, :shadowmoves

  def initialize(*args)
    raise "PokeBattle_Pokemon.new is deprecated. Use Pokemon.new instead."
  end

  def self.convert(pkmn)
    return pkmn if pkmn.is_a?(Pokemon)
    owner = Pokemon::Owner.new(pkmn.trainerID, pkmn.ot, pkmn.otgender, pkmn.language)
    natdex = [:NONE]
    GameData::Species.each_species { |s| natdex.push(s.id) }
    pkmn.species = natdex[pkmn.species]
    # Set level to 1 initially, as it will be recalculated later
    ret = Pokemon.new(pkmn.species, 1, owner, false, false)
    ret.forced_form      = pkmn.forcedForm if pkmn.forcedForm
    ret.time_form_set    = pkmn.formTime
    ret.exp              = pkmn.exp
    ret.steps_to_hatch   = pkmn.eggsteps
    GameData::Status.each do |s|
      pkmn.status = s.id if s.icon_position == pkmn.status
    end
    ret.status           = pkmn.status
    ret.statusCount      = pkmn.statusCount
    ret.gender           = pkmn.genderflag
    ret.shiny            = pkmn.shinyflag
    ret.ability_index    = pkmn.abilityflag
    ret.nature           = pkmn.natureflag
    ret.nature_for_stats = pkmn.natureOverride
    ret.item             = pkmn.item
    ret.mail             = PokemonMail.convert(pkmn.mail) if pkmn.mail
    pkmn.moves.each { |m| ret.moves.push(PBMove.convert(m)) if m && m.id > 0 }
    if pkmn.firstmoves
      pkmn.firstmoves.each { |m| ret.add_first_move(m) }
    end
    if pkmn.ribbons
      pkmn.ribbons.each { |r| ret.giveRibbon(r) }
    end
    ret.cool             = pkmn.cool if pkmn.cool
    ret.beauty           = pkmn.beauty if pkmn.beauty
    ret.cute             = pkmn.cute if pkmn.cute
    ret.smart            = pkmn.smart if pkmn.smart
    ret.tough            = pkmn.tough if pkmn.tough
    ret.sheen            = pkmn.sheen if pkmn.sheen
    ret.pokerus          = pkmn.pokerus if pkmn.pokerus
    ret.name             = pkmn.name if pkmn.name != ret.speciesName
    ret.happiness        = pkmn.happiness
    ret.poke_ball        = pbBallTypeToItem(pkmn.ballused).id
    ret.markings         = pkmn.markings if pkmn.markings
    GameData::Stat.each_main do |s|
      ret.iv[s.id]       = pkmn.iv[s.id_number]
      ret.ivMaxed[s.id]  = pkmn.ivMaxed[s.id_number] if pkmn.ivMaxed
      ret.ev[s.id]       = pkmn.ev[s.id_number]
    end
    ret.obtain_method    = pkmn.obtainMode
    ret.obtain_map       = pkmn.obtainMap
    ret.obtain_text      = pkmn.obtainText
    ret.obtain_level     = pkmn.obtainLevel if pkmn.obtainLevel
    ret.hatched_map      = pkmn.hatchedMap
    ret.timeReceived     = pkmn.timeReceived
    ret.timeEggHatched   = pkmn.timeEggHatched
    if pkmn.fused
      ret.fused = PokeBattle_Pokemon.convert(pkmn.fused) if pkmn.fused.is_a?(PokeBattle_Pokemon)
      ret.fused = pkmn.fused if pkmn.fused.is_a?(Pokemon)
    end
    ret.personalID       = pkmn.personalID
    ret.hp               = pkmn.hp
    if pkmn.shadow
      ret.shadow         = pkmn.shadow
      ret.heart_gauge    = pkmn.heartgauge
      ret.hyper_mode     = pkmn.hypermode
      ret.saved_exp      = pkmn.savedexp
      if pkmn.savedev
        GameData::Stat.each_main { |s| ret.saved_ev[s.id] = pkmn.savedev[s.pbs_order] if s.pbs_order >= 0 }
      end
      ret.shadow_moves   = []
      pkmn.shadowmoves.each_with_index do |move, i|
        ret.shadow_moves[i] = GameData::Move.get(move).id if move
      end
    end
    # NOTE: Intentionally set last, as it recalculates stats.
    ret.form_simple      = pkmn.form || 0
    return ret
  end
end

class PBMove
  attr_accessor :id, :pp, :ppup

  def self.convert(move)
    ret = Pokemon::Move.new(move.id)
    ret.ppup = move.ppup
    ret.pp = move.pp
    return ret
  end
end

class PokemonMail
  attr_accessor :item, :message, :sender, :poke1, :poke2, :poke3

  def self.convert(mail)
    return mail if mail.is_a?(Mail)
    item.poke1[0] = GameData::Species.get(item.poke1[0]).id if item.poke1
    item.poke2[0] = GameData::Species.get(item.poke2[0]).id if item.poke2
    item.poke3[0] = GameData::Species.get(item.poke3[0]).id if item.poke3
    return Mail.new(mail.item, item.message, item.sender, item.poke1, item.poke2, item.poke3)
  end
end
Ruby:
def pbSaveFile(name,ver=19)
  case ver
    when 19
      location = File.join("C:/Users",System.user_name,"AppData/Roaming",name)
      return false unless File.directory?(location)
      file = File.join(location, 'Game.rxdata')
      return false unless File.file?(file)
      save_data = SaveData.read_from_file(file)
    when 18
      home = ENV['HOME'] || ENV['HOMEPATH']
      return false if home.nil?
      location = File.join(home, 'Saved Games', name)
      return false unless File.directory?(location)
      file = File.join(location, 'Game.rxdata')
      return false unless File.file?(file)
      save_data = SaveData.get_data_from_file(file).clone
      save_data = SaveData.to_hash_format(save_data) if save_data.is_a?(Array)
  end
  return save_data
end



def pbSaveTest(name,test,param=nil,ver=19)
  save = pbSaveFile(name,ver)
  result = false
  test = test.capitalize
  if save
    case test
      when "Exist"
        result = true
      when "Map"
        result = (save[:map_factory].map.map_id == param)
      when "Name"
        result = (save[:player].name == param)
      when "Switch"
        result = (save[:switches][param] == true)
      when "Variable"
        varnum = param[0]
        varval = param[1]
        if varval.is_a?(Numeric)
          result = (save[:variables][varnum] >= varval)
        else
          result = (save[:variables][varnum] == varval)
        end
      when "Party"
        party = save[:player].party
        for i in 0...party.length
          poke = party[i]
          result = true if poke.species == param
        end
      when "Seen"
        result = (save[:player].pokedex.seen?(param))
      when "Owned"
        result = (save[:player].pokedex.owned?(param))
      when "Item"
        result = (save[:bag].pbHasItem?(param))
    end
  end
  return result
end

Using the Script​

Write a conditional branch with a script check using the following:
pbSaveTest(name,test,param,ver)

Where:

name is the name of your game, just like the folder where the save is stored. Should be a string. (In quotes) Note that sometimes, special characters may be changed in the filepath- for example, : often gets changed to _, because you can't use those characters in a filepath.

test is a string (put in quotes) of one of the following words: (Capitalization doesn't matter, it should capitalize the string on its own)
  • "Exist"- Returns true if the player has a save file, false if they don't. Parameter can be whatever, but it'd be best to leave as nil for the sake of simplicity.
  • "Map" - The map ID of the save's player's location. Parameter should be an integer. (No quotes, no 0s in front of it)
  • "Name" - The name of the save's player character. Parameter should be a string. (in quotes)
  • "Switch" - The number of a Game Switch that should be turned on. Parameter should be an integer. (No quotes, no 0s in front of it)
  • "Variable" - A certain Game Variable set to a certain value. Parameter should be an array ([a,b]) where the first entry is the number of a Game Variable, and the second is what that variable should be. If it's a number, this check will also return true if the variable is greater than this number- for example, if I was checking for the variable being at 1, and my save's variable was at 2, it'd return true.
These last four checks hit a bit of a snarl when you check a v18 game, because they're checking Essentials data, and that's gone through some major overhauls over these last versions.

In v19, you can still use symbols, but the game is going to use that to get the index number. That means your games will have to match up numbers in these checks. (For example, if I added Gigantamix in Game A, I could check for that in Game B, it'd just need to have the same index number)

In v20, since index numbers are no longer used, the game won't be able to convert a symbol into the index number, so you have to provide the index number from the original game.
  • Party - Checks if the save file has a certain species in their party.
  • Seen - Returns true if the save file has seen a certain Pokémon.
  • Owned - Returns true if the save file has owned a certain Pokémon.
  • Item - Returns true if the save file has a certain item in the bag.
ver - the version of Essentials that the other game uses. (For example, if your game was only on its first version, but was made using v18 of Essentials, you'd put 18) No "v", no sub-versions, just one whole number. Can be left blank, will default to 20. (or 19 if you're using the v19 version)


From there, just use your branch to do whatever else you want in the new game! Maybe the player can receive a special Pokémon or item from their old save, or even an article of clothing if your game has customization! But nobody said you have to just give away a prize- why not use this check to start a new sidequest for the player?

Of course, this system isn't foolproof- there's nothing to stop a player from using a copy of Essentials to make a save that fits your parameters and putting it in the right folder. But for that much effort, they might as well just give themselves whatever prize you're offering to begin with.


If you feel confident with Ruby, you might be interested in using this for a bit more than just true/false statements! pbSaveFile(game, essentials version) can be used to get save data from a file in more detail! I won't list every possible feature, as it's, y'know, everything you could possibly do with a player's save data, but here's a common favorite!

Please note that, due to the issues with data restructuring mentioned earlier, scripts like these are not currently possible between v18 and v20. v18

Let's say we want to import the first Pokémon in the player's party from the save. It's just stored in the trainer's party, so we just have to do a little digging to get it!

Ruby:
save = pbSaveFile("My Game")
party = save[:player].party
poke = party[0]
pbAddPokemon(poke)
If you're transferring from v18 to v19, add poke = PokeBattle_Pokemon.convert(poke) before pbAddPokemon.

Remember, though, that you'll need to consider how one game's data will differ from another. Make sure your Pokémon aren't coming in with moves or abilities that don't exist in their new game, or you'll get a bunch of crashes. You'll probably also want to change the Pokémon's met location, since it'll be using a different set of maps now.

You could also use save[:player].party to get the party as a whole, and put it into an enemy or ally trainer! Here's an example of that in the form of an on_trainer_load handler:
Ruby:
EventHandlers.add(:on_trainer_load, :old_save_battle,
  proc { |trainer|
    save = pbSaveFile("Pokémon Essentials v20.1")
    if trainer   # An NPCTrainer object containing party/items/lose text, etc.
      newtrainer = save[:player]
      trainer.party = newtrainer.party
      trainer.name = newtrainer.name
      trainer.trainer_type = newtrainer.trainer_type
      trainer.lose_text = "..."
      trainer.id        = newtrainer.id
      oldbag = save[:bag].clone
      items = []
      for i in 0...oldbag.pockets.length
        pocket = oldbag.pockets[i]
        for j in 0...pocket.length
          item = pocket[j]
          item[1].times do |k|
            items.push(item[0])
          end
        end
      end
      trainer.items = items
    end
  }
)
This would rewrite a trainer battle to use the player and their party from Pokemon Essentials v20.1, even bringing over their items! (Hope you didn't stockpile too many Full Restores...) Lose text is set on this line:
Ruby:
      trainer.lose_text = "..."

Check the script section Game_SaveValues for all the existing save values you could check, and be sure to read Savordez's guide to the new save system for more info!

Future Goals​

  • Compatibility with multiple save file scripts.
  • I don't know if it's possible, but a test that reads another game's data for stuff like Pokémon and items could be useful.
  • To make things easier on staff, I might put together an event that checks for all current badges, so that could be used to test multiple games at once.
  • Maaaybe figure out how to read earlier versions- I'm not confident in my understanding of how earlier saves were structured, but it'd be good to try.
  • I don't think it's possible to convert v18's ID numbers into symbolic IDs for v20, but I'll put it on the list just in case.
Credits
Credits to TechSkylander1518, please!
Author
TechSkylander1518
Downloads
534
Views
4,113
First release
Last update
Rating
5.00 star(s) 1 ratings

More resources from TechSkylander1518

Latest updates

  1. Quick bugfix

    It's literally just a missing end, but it broke the v20 plugin, so just a heads up if anyone was...
  2. v20 Update

    Just a quick update for v20! There's not much interesting added (and unfortunately because of...
Back
Top