- Pokémon Essentials Version
- v20.1 ➖
(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.
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.
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)
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
}
)
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!