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

Defining trainer Pok?mon's EVs

DoesntKnowHowToPlay

Tiny Umbrella with Lots and Lots of Good
265
Posts
12
Years
    • Seen Feb 24, 2024
    Defining trainer Pokémon's EVs

    I doubt anyone who knows what they're doing would have a hard time with this, but I'm led to believe this is harder than it looks so I'm going to post it anyway; it's quite possible I've forgotten something important.

    Replace the set of definitions and first function at the top of "PTrainer_NPCTrainers" with this:

    Code:
    TPSPECIES   = 0
    TPLEVEL     = 1
    TPITEM      = 2
    TPMOVE1     = 3
    TPMOVE2     = 4
    TPMOVE3     = 5
    TPMOVE4     = 6
    TPABILITY   = 7
    TPGENDER    = 8
    TPFORM      = 9
    TPSHINY     = 10
    TPNATURE    = 11
    TPIV        = 12
    TPHAPPINESS = 13
    TPNAME      = 14
    TPSHADOW    = 15
    TPBALL      = 16
    TPHPEV      = 17
    TPATKEV     = 18
    TPDEFEV     = 19
    TPSPEEDEV   = 20
    TPSATKEV    = 21
    TPSDEFEV    = 22
    TPDEFAULTS = [0,10,0,0,0,0,0,nil,nil,0,false,nil,10,70,nil,false,0,0,0,0,0,0,0]
    
    def pbLoadTrainer(trainerid,trainername,partyid=0)
      if trainerid.is_a?(String) || trainerid.is_a?(Symbol)
        if !hasConst?(PBTrainers,trainerid)
          raise _INTL("Trainer type does not exist ({1}, {2}, ID {3})",trainerid,trainername,partyid)
        end
        trainerid=getID(PBTrainers,trainerid)
      end
      success=false
      items=[]
      party=[]
      opponent=nil
      trainers=load_data("Data/trainers.dat")
      for trainer in trainers
        name=trainer[1]
        thistrainerid=trainer[0]
        thispartyid=trainer[4]
        next if trainerid!=thistrainerid || name!=trainername || partyid!=thispartyid
        items=trainer[2].clone
        name=pbGetMessageFromHash(MessageTypes::TrainerNames,name)
        for i in RIVALNAMES
          if isConst?(trainerid,PBTrainers,i[0]) && $game_variables[i[1]]!=0
            name=$game_variables[i[1]]
          end
        end
        opponent=PokeBattle_Trainer.new(name,thistrainerid)
        opponent.setForeignID($Trainer) if $Trainer
        for poke in trainer[3]
          species=poke[TPSPECIES]
          level=poke[TPLEVEL]
          pokemon=PokeBattle_Pokemon.new(species,level,opponent)
          pokemon.formNoCall=poke[TPFORM]
          pokemon.resetMoves
          pokemon.setItem(poke[TPITEM])
          if poke[TPMOVE1]>0 || poke[TPMOVE2]>0 || poke[TPMOVE3]>0 || poke[TPMOVE4]>0
            k=0
            for move in [TPMOVE1,TPMOVE2,TPMOVE3,TPMOVE4]
              pokemon.moves[k]=PBMove.new(poke[move])
              k+=1
            end
            pokemon.moves.compact!
          end
          pokemon.setAbility(poke[TPABILITY])
          pokemon.setGender(poke[TPGENDER])
          if poke[TPSHINY]   # if this is a shiny Pokémon
            pokemon.makeShiny
          else
            pokemon.makeNotShiny
          end
          pokemon.setNature(poke[TPNATURE])
          iv=poke[TPIV]
          for i in 0...6
            pokemon.iv[i]=iv&0x1F
          end
          pokemon.happiness=poke[TPHAPPINESS]
          pokemon.name=poke[TPNAME] if poke[TPNAME] && poke[TPNAME]!=""
          if poke[TPSHADOW]   # if this is a Shadow Pokémon
            pokemon.makeShadow rescue nil
            pokemon.pbUpdateShadowMoves(true) rescue nil
            pokemon.makeNotShiny
          end
          pokemon.ballused=poke[TPBALL]
          pokemon.ev[0]=poke[TPHPEV]
          pokemon.ev[1]=poke[TPATKEV]
          pokemon.ev[2]=poke[TPDEFEV]
          pokemon.ev[3]=poke[TPSPEEDEV]
          pokemon.ev[4]=poke[TPSATKEV]
          pokemon.ev[5]=poke[TPSDEFEV]
          pokemon.calcStats
          party.push(pokemon)
        end
        success=true
        break
      end
      return success ? [opponent,items,party] : nil
    end
    Then open the "Compilers" script and replace function pbCompileTrainers with the following:

    Code:
    def pbCompileTrainers
      # Trainer types
      records=[]
      trainernames=[]
      count=0
      maxValue=0
      pbCompilerEachPreppedLine("PBS/trainertypes.txt"){|line,lineno|
         record=pbGetCsvRecord(line,lineno,[0,"unsUSSSeUs", # ID can be 0
            nil,nil,nil,nil,nil,nil,nil,{
            ""=>2,"Male"=>0,"M"=>0,"0"=>0,"Female"=>1,"F"=>1,"1"=>1,"Mixed"=>2,"X"=>2,"2"=>2
            },nil,nil]
         )
         if record[3] && (record[3]<0 || record[3]>255)
           raise _INTL("Bad money amount (must be from 0 through 255)\r\n{1}",FileLineData.linereport)
         end
         record[3]=30 if !record[3]
         if record[8] && (record[8]<0 || record[8]>255)
           raise _INTL("Bad skill value (must be from 0 through 255)\r\n{1}",FileLineData.linereport)
         end
         record[8]=record[3] if !record[8]
         record[9]="" if !record[9]
         trainernames[record[0]]=record[2]
         if records[record[0]]
           raise _INTL("Two trainer types ({1} and {2}) have the same ID ({3}), which is not allowed.\r\n{4}",
              records[record[0]][1],record[1],record[0],FileLineData.linereport)
         end
         records[record[0]]=record
         maxValue=[maxValue,record[0]].max
      }
      count=records.compact.length
      MessageTypes.setMessages(MessageTypes::TrainerTypes,trainernames)
      code="class PBTrainers\r\n"
      for rec in records
        next if !rec
        code+="#{rec[1]}=#{rec[0]}\r\n"
      end
      code+="\r\ndef PBTrainers.getName(id)\r\nreturn pbGetMessage(MessageTypes::TrainerTypes,id)\r\nend"
      code+="\r\ndef PBTrainers.getCount\r\nreturn #{count}\r\nend"
      code+="\r\ndef PBTrainers.maxValue\r\nreturn #{maxValue}\r\nend\r\nend"
      eval(code)
      pbAddScript(code,"PBTrainers")
      File.open("Data/trainertypes.dat","wb"){|f|
         Marshal.dump(records,f)
      }
      # Individual trainers
      lines=[]
      linenos=[]
      lineno=1
      File.open("PBS/trainers.txt","rb"){|f|
         FileLineData.file="PBS/trainers.txt"
         f.each_line {|line|
            if lineno==1 && line[0]==0xEF && line[1]==0xBB && line[2]==0xBF
              line=line[3,line.length-3]
            end
            line=prepline(line)
            if line!=""
              lines.push(line)
              linenos.push(lineno)
            end
            lineno+=1
         }
      }
      nameoffset=0
      trainers=[]
      trainernames.clear
      i=0; loop do break unless i<lines.length
        FileLineData.setLine(lines[i],linenos[i])
        trainername=parseTrainer(lines[i])
        FileLineData.setLine(lines[i+1],linenos[i+1])
        nameline=strsplit(lines[i+1],/\s*,\s*/)
        name=nameline[0]
        raise _INTL("Trainer name too long\r\n{1}",FileLineData.linereport) if name.length>=0x10000
        trainernames.push(name)
        partyid=0
        if nameline[1] && nameline[1]!=""
          raise _INTL("Expected a number for the trainer battle ID\r\n{1}",FileLineData.linereport) if !nameline[1][/^\d+$/]
          partyid=nameline[1].to_i
        end
        FileLineData.setLine(lines[i+2],linenos[i+2])
        items=strsplit(lines[i+2],/\s*,\s*/)
        items[0].gsub!(/^\s+/,"")   # Number of Pokémon
        raise _INTL("Expected a number for the number of Pokémon\r\n{1}",FileLineData.linereport) if !items[0][/^\d+$/]
        numpoke=items[0].to_i
        realitems=[]
        for j in 1...items.length   # Items held by Trainer
          realitems.push(parseItem(items[j])) if items[j] && items[j]!=""
        end
        pkmn=[]
        for j in 0...numpoke
          FileLineData.setLine(lines[i+j+3],linenos[i+j+3])
          poke=strsplit(lines[i+j+3],/\s*,\s*/)
          begin
            # Species
            poke[TPSPECIES]=parseSpecies(poke[TPSPECIES])
          rescue
            raise _INTL("Expected a species name: {1}\r\n{2}",poke[0],FileLineData.linereport)
          end
          # Level
          poke[TPLEVEL]=poke[TPLEVEL].to_i
          raise _INTL("Bad level: {1} (must be from 1-{2})\r\n{3}",poke[TPLEVEL],
            PBExperience::MAXLEVEL,FileLineData.linereport) if poke[TPLEVEL]<=0 || poke[TPLEVEL]>PBExperience::MAXLEVEL
          # Held item
          if !poke[TPITEM] || poke[TPITEM]==""
            poke[TPITEM]=TPDEFAULTS[TPITEM]
          else
            poke[TPITEM]=parseItem(poke[TPITEM])
          end
          # Moves
          moves=[]
          for j in [TPMOVE1,TPMOVE2,TPMOVE3,TPMOVE4]
            moves.push(parseMove(poke[j])) if poke[j] && poke[j]!=""
          end
          for j in 0...4
            index=[TPMOVE1,TPMOVE2,TPMOVE3,TPMOVE4][j]
            if moves[j] && moves[j]!=0
              poke[index]=moves[j]
            else
              poke[index]=TPDEFAULTS[index]
            end
          end
          # Ability
          if !poke[TPABILITY] || poke[TPABILITY]==""
            poke[TPABILITY]=TPDEFAULTS[TPABILITY]
          else
            poke[TPABILITY]=poke[TPABILITY].to_i
            raise _INTL("Bad abilityflag: {1} (must be 0 or 1 or 2-5)\r\n{2}",poke[TPABILITY],FileLineData.linereport) if poke[TPABILITY]<0 || poke[TPABILITY]>5
          end
          # Gender
          if !poke[TPGENDER] || poke[TPGENDER]==""
            poke[TPGENDER]=TPDEFAULTS[TPGENDER]
          else
            if poke[TPGENDER]=="M"
              poke[TPGENDER]=0
            elsif poke[TPGENDER]=="F"
              poke[TPGENDER]=1
            else
              poke[TPGENDER]=poke[TPGENDER].to_i
              raise _INTL("Bad genderflag: {1} (must be M or F, or 0 or 1)\r\n{2}",poke[TPGENDER],FileLineData.linereport) if poke[TPGENDER]<0 || poke[TPGENDER]>1
            end
          end
          # Form
          if !poke[TPFORM] || poke[TPFORM]==""
            poke[TPFORM]=TPDEFAULTS[TPFORM]
          else
            poke[TPFORM]=poke[TPFORM].to_i
            raise _INTL("Bad form: {1} (must be 0 or greater)\r\n{2}",poke[TPFORM],FileLineData.linereport) if poke[TPFORM]<0
          end
          # Shiny
          if !poke[TPSHINY] || poke[TPSHINY]==""
            poke[TPSHINY]=TPDEFAULTS[TPSHINY]
          elsif poke[TPSHINY]=="shiny"
            poke[TPSHINY]=true
          else
            poke[TPSHINY]=csvBoolean!(poke[TPSHINY].clone)
          end
          # Nature
          if !poke[TPNATURE] || poke[TPNATURE]==""
            poke[TPNATURE]=TPDEFAULTS[TPNATURE]
          else
            poke[TPNATURE]=parseNature(poke[TPNATURE])
          end
          # IVs
          if !poke[TPIV] || poke[TPIV]==""
            poke[TPIV]=TPDEFAULTS[TPIV]
          else
            poke[TPIV]=poke[TPIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPIV],FileLineData.linereport) if poke[TPIV]<0 || poke[TPIV]>31
          end
          # Happiness
          if !poke[TPHAPPINESS] || poke[TPHAPPINESS]==""
            poke[TPHAPPINESS]=TPDEFAULTS[TPHAPPINESS]
          else
            poke[TPHAPPINESS]=poke[TPHAPPINESS].to_i
            raise _INTL("Bad happiness: {1} (must be from 0-255)\r\n{2}",poke[TPHAPPINESS],FileLineData.linereport) if poke[TPHAPPINESS]<0 || poke[TPHAPPINESS]>255
          end
          # Nickname
          if !poke[TPNAME] || poke[TPNAME]==""
            poke[TPNAME]=TPDEFAULTS[TPNAME]
          else
            poke[TPNAME]=poke[TPNAME].to_s
            raise _INTL("Bad nickname: {1} (must be 1-20 characters)\r\n{2}",poke[TPNAME],FileLineData.linereport) if (poke[TPNAME].to_s).length>20
          end
          # Shadow
          if !poke[TPSHADOW] || poke[TPSHADOW]==""
            poke[TPSHADOW]=TPDEFAULTS[TPSHADOW]
          else
            poke[TPSHADOW]=csvBoolean!(poke[TPSHADOW].clone)
          end
          # Ball
          if !poke[TPBALL] || poke[TPBALL]==""
            poke[TPBALL]=TPDEFAULTS[TPBALL]
          else
            poke[TPBALL]=poke[TPBALL].to_i
            raise _INTL("Bad form: {1} (must be 0 or greater)\r\n{2}",poke[TPBALL],FileLineData.linereport) if poke[TPBALL]<0
          end
          # EVs
          # HP
          if !poke[TPHPEV] || poke[TPHPEV]==""
            poke[TPHPEV]=TPDEFAULTS[TPHPEV]
          else
            poke[TPHPEV]=poke[TPHPEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPHPEV],FileLineData.linereport) if poke[TPHPEV]<0 || poke[TPHPEV]>255
          end
          # Atk
          if !poke[TPATKEV] || poke[TPATKEV]==""
            poke[TPATKEV]=TPDEFAULTS[TPATKEV]
          else
            poke[TPATKEV]=poke[TPATKEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPATKEV],FileLineData.linereport) if poke[TPATKEV]<0 || poke[TPATKEV]>255
          end
          # Def
          if !poke[TPDEFEV] || poke[TPDEFEV]==""
            poke[TPDEFEV]=TPDEFAULTS[TPDEFEV]
          else
            poke[TPDEFEV]=poke[TPDEFEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPDEFEV],FileLineData.linereport) if poke[TPDEFEV]<0 || poke[TPDEFEV]>255
          end
          # Speed
          if !poke[TPSPEEDEV] || poke[TPSPEEDEV]==""
            poke[TPSPEEDEV]=TPDEFAULTS[TPSPEEDEV]
          else
            poke[TPSPEEDEV]=poke[TPSPEEDEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPSPEEDEV],FileLineData.linereport) if poke[TPSPEEDEV]<0 || poke[TPSPEEDEV]>255
          end
          # Sp.Atk
          if !poke[TPSATKEV] || poke[TPSATKEV]==""
            poke[TPSATKEV]=TPDEFAULTS[TPSATKEV]
          else
            poke[TPSATKEV]=poke[TPSATKEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPSATKEV],FileLineData.linereport) if poke[TPSATKEV]<0 || poke[TPSATKEV]>255
          end
          # Sp.Def
          if !poke[TPSDEFEV] || poke[TPSDEFEV]==""
            poke[TPSDEFEV]=TPDEFAULTS[TPSDEFEV]
          else
            poke[TPSDEFEV]=poke[TPSDEFEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPSDEFEV],FileLineData.linereport) if poke[TPSDEFEV]<0 || poke[TPSDEFEV]>255
          end
          pkmn.push(poke)
        end
        i+=3+numpoke
        MessageTypes.setMessagesAsHash(MessageTypes::TrainerNames,trainernames)
        trainers.push([trainername,name,realitems,pkmn,partyid])
        nameoffset+=name.length
      end
      save_data(trainers,"Data/trainers.dat")
    end
    These will allow you to give custom EV spreads to trainers. The EVs come after everything else, and in the usual order for Essentials (speed/satk/sdef), so you'd define a trainer's mon like this for a 252 Atk/Speed mon:

    Code:
    GYARADOS,50,CHOICEBAND,AQUATAIL,EARTHQUAKE,STONEEDGE,BOUNCE,0,F,,,ADAMANT,31,,kogasa,,,0,252,0,252,0,0
    As with the other fields, you don't have to give every trainer's pokemon specially defined EVs, or even extend commas that far. They'll default to 0 in every EV instead of the weird inflated formula vanilla Essentials uses, though.
     

    Derxwna Kapsyla

    Derxwna "The Badman" Kapsyla
    437
    Posts
    12
    Years
  • You picked up Pokemon Essentials less than 5 hours ago and this what you do with it.

    Honestly I can't say I'm surprised, and I'm probably not the only one who's wanted to see a script like this for a long time now! I probably won't be adding it to Faith & Prayer Version (Unless I decide to rebalance the entire game), but I'm sure someone out there will want this.
     
    7
    Posts
    14
    Years
    • Seen Aug 7, 2016
    This is very interesting and helpful!
    I'm looking to put something like this in my game, so I'm glad I found a post about it. I will def check it out once I get to that point!

    Woo!
     
    199
    Posts
    14
    Years
    • Seen Jul 6, 2022
    Great!!
    I think you could do something similar with Ivs (for Gyro Ball, Trick Room teams and Hidden Power) the problem is to change all trainers in Trainers.txt to be compatible, unless you can do so:

    Code:
    GYARADOS,50,CHOICEBAND,AQUATAIL,EARTHQUAKE,STONEEDGE,BOUNCE,0,F,,,ADAMANT,31,,kogasa,,,0,252,0,252,0,0,[B]31,31,31,31,31[/B]
    Leaving the first 31(or other number 0-31) like HP IV, while the five last 31(or other number 0-31) are Atk, Def, SATK, SDef, Speed
    But I do not know what should be changed in the script
     

    Arkadious

    Developer of Fragmentum
    50
    Posts
    8
    Years
  • Hi :) Just wondering if this still works in Essentials 16.2
    I keep getting interpreter errors whenever I try to start a trainer battle, regardless of whether the print version is used or not.

    Spoiler:
     

    Derxwna Kapsyla

    Derxwna "The Badman" Kapsyla
    437
    Posts
    12
    Years
  • I added the script to 16.2 when updating my game, and so far everything's been working fine? It required a few reworking iirc but, that's a simple work of comparing changes between v16, v16.2, and the script
     

    WolfPP

    Spriter/ Pixel Artist
    1,309
    Posts
    5
    Years
  • Excuse me, sorry to necropost but i edited to put IV's too.

    in 'PTrainer_NPCTrainers' replace to:
    Code:
    TPSPECIES   = 0
    TPLEVEL     = 1
    TPITEM      = 2
    TPMOVE1     = 3
    TPMOVE2     = 4
    TPMOVE3     = 5
    TPMOVE4     = 6
    TPABILITY   = 7
    TPGENDER    = 8
    TPFORM      = 9
    TPSHINY     = 10
    TPNATURE    = 11
    TPIV        = 12
    TPHAPPINESS = 13
    TPNAME      = 14
    TPSHADOW    = 15
    TPBALL      = 16
    TPHPEV      = 17
    TPATKEV     = 18
    TPDEFEV     = 19
    TPSPEEDEV   = 20
    TPSATKEV    = 21
    TPSDEFEV    = 22
    TPHPIV      = 23
    TPATKIV     = 24
    TPDEFIV     = 25
    TPSPEEDIV   = 26
    TPSATKIV    = 27
    TPSDEFIV    = 28
    TPDEFAULTS = [0,10,0,0,0,0,0,nil,nil,0,false,nil,10,70,nil,false,0,0,0,0,0,0,0,0,0,0,0,0,0]
    
    def pbLoadTrainer(trainerid,trainername,partyid=0)
      if trainerid.is_a?(String) || trainerid.is_a?(Symbol)
        if !hasConst?(PBTrainers,trainerid)
          raise _INTL("Trainer type does not exist ({1}, {2}, ID {3})",trainerid,trainername,partyid)
        end
        trainerid=getID(PBTrainers,trainerid)
      end
      success=false
      items=[]
      party=[]
      opponent=nil
      trainers=load_data("Data/trainers.dat")
      for trainer in trainers
        name=trainer[1]
        thistrainerid=trainer[0]
        thispartyid=trainer[4]
        next if trainerid!=thistrainerid || name!=trainername || partyid!=thispartyid
        items=trainer[2].clone
        name=pbGetMessageFromHash(MessageTypes::TrainerNames,name)
        for i in RIVALNAMES
          if isConst?(trainerid,PBTrainers,i[0]) && $game_variables[i[1]]!=0
            name=$game_variables[i[1]]
          end
        end
        opponent=PokeBattle_Trainer.new(name,thistrainerid)
        opponent.setForeignID($Trainer) if $Trainer
        for poke in trainer[3]
          species=poke[TPSPECIES]
          level=poke[TPLEVEL]
          pokemon=PokeBattle_Pokemon.new(species,level,opponent)
          pokemon.formNoCall=poke[TPFORM]
          pokemon.resetMoves
          pokemon.setItem(poke[TPITEM])
          if poke[TPMOVE1]>0 || poke[TPMOVE2]>0 || poke[TPMOVE3]>0 || poke[TPMOVE4]>0
            k=0
            for move in [TPMOVE1,TPMOVE2,TPMOVE3,TPMOVE4]
              pokemon.moves[k]=PBMove.new(poke[move])
              k+=1
            end
            pokemon.moves.compact!
          end
          pokemon.setAbility(poke[TPABILITY])
          pokemon.setGender(poke[TPGENDER])
          if poke[TPSHINY]   # if this is a shiny Pokémon
            pokemon.makeShiny
          else
            pokemon.makeNotShiny
          end
          pokemon.setNature(poke[TPNATURE])
          iv=poke[TPIV]
          for i in 0...6
            pokemon.iv[i]=iv&0x1F
          end
          pokemon.happiness=poke[TPHAPPINESS]
          pokemon.name=poke[TPNAME] if poke[TPNAME] && poke[TPNAME]!=""
          if poke[TPSHADOW]   # if this is a Shadow Pokémon
            pokemon.makeShadow rescue nil
            pokemon.pbUpdateShadowMoves(true) rescue nil
            pokemon.makeNotShiny
          end
          pokemon.ballused=poke[TPBALL]
          pokemon.ev[0]=poke[TPHPEV]
          pokemon.ev[1]=poke[TPATKEV]
          pokemon.ev[2]=poke[TPDEFEV]
          pokemon.ev[3]=poke[TPSPEEDEV]
          pokemon.ev[4]=poke[TPSATKEV]
          pokemon.ev[5]=poke[TPSDEFEV]
          pokemon.iv[0]=poke[TPHPIV]
          pokemon.iv[1]=poke[TPATKIV]
          pokemon.iv[2]=poke[TPDEFIV]
          pokemon.iv[3]=poke[TPSPEEDIV]
          pokemon.iv[4]=poke[TPSATKIV]
          pokemon.iv[5]=poke[TPSDEFIV]
          pokemon.calcStats
          party.push(pokemon)
        end
        success=true
        break
      end
      return success ? [opponent,items,party] : nil
    end

    in 'def pbCompileTrainers' replace to:

    Code:
    def pbCompileTrainers
      # Trainer types
      records=[]
      trainernames=[]
      count=0
      maxValue=0
      pbCompilerEachPreppedLine("PBS/trainertypes.txt"){|line,lineno|
         record=pbGetCsvRecord(line,lineno,[0,"unsUSSSeUs", # ID can be 0
            nil,nil,nil,nil,nil,nil,nil,{
            ""=>2,"Male"=>0,"M"=>0,"0"=>0,"Female"=>1,"F"=>1,"1"=>1,"Mixed"=>2,"X"=>2,"2"=>2
            },nil,nil]
         )
         if record[3] && (record[3]<0 || record[3]>255)
           raise _INTL("Bad money amount (must be from 0 through 255)\r\n{1}",FileLineData.linereport)
         end
         record[3]=30 if !record[3]
         if record[8] && (record[8]<0 || record[8]>255)
           raise _INTL("Bad skill value (must be from 0 through 255)\r\n{1}",FileLineData.linereport)
         end
         record[8]=record[3] if !record[8]
         record[9]="" if !record[9]
         trainernames[record[0]]=record[2]
         if records[record[0]]
           raise _INTL("Two trainer types ({1} and {2}) have the same ID ({3}), which is not allowed.\r\n{4}",
              records[record[0]][1],record[1],record[0],FileLineData.linereport)
         end
         records[record[0]]=record
         maxValue=[maxValue,record[0]].max
      }
      count=records.compact.length
      MessageTypes.setMessages(MessageTypes::TrainerTypes,trainernames)
      code="class PBTrainers\r\n"
      for rec in records
        next if !rec
        code+="#{rec[1]}=#{rec[0]}\r\n"
      end
      code+="\r\ndef PBTrainers.getName(id)\r\nreturn pbGetMessage(MessageTypes::TrainerTypes,id)\r\nend"
      code+="\r\ndef PBTrainers.getCount\r\nreturn #{count}\r\nend"
      code+="\r\ndef PBTrainers.maxValue\r\nreturn #{maxValue}\r\nend\r\nend"
      eval(code)
      pbAddScript(code,"PBTrainers")
      File.open("Data/trainertypes.dat","wb"){|f|
         Marshal.dump(records,f)
      }
      # Individual trainers
      lines=[]
      linenos=[]
      lineno=1
      File.open("PBS/trainers.txt","rb"){|f|
         FileLineData.file="PBS/trainers.txt"
         f.each_line {|line|
            if lineno==1 && line[0]==0xEF && line[1]==0xBB && line[2]==0xBF
              line=line[3,line.length-3]
            end
            line=prepline(line)
            if line!=""
              lines.push(line)
              linenos.push(lineno)
            end
            lineno+=1
         }
      }
      nameoffset=0
      trainers=[]
      trainernames.clear
      i=0; loop do break unless i<lines.length
        FileLineData.setLine(lines[i],linenos[i])
        trainername=parseTrainer(lines[i])
        FileLineData.setLine(lines[i+1],linenos[i+1])
        nameline=strsplit(lines[i+1],/\s*,\s*/)
        name=nameline[0]
        raise _INTL("Trainer name too long\r\n{1}",FileLineData.linereport) if name.length>=0x10000
        trainernames.push(name)
        partyid=0
        if nameline[1] && nameline[1]!=""
          raise _INTL("Expected a number for the trainer battle ID\r\n{1}",FileLineData.linereport) if !nameline[1][/^\d+$/]
          partyid=nameline[1].to_i
        end
        FileLineData.setLine(lines[i+2],linenos[i+2])
        items=strsplit(lines[i+2],/\s*,\s*/)
        items[0].gsub!(/^\s+/,"")   # Number of Pokémon
        raise _INTL("Expected a number for the number of Pokémon\r\n{1}",FileLineData.linereport) if !items[0][/^\d+$/]
        numpoke=items[0].to_i
        realitems=[]
        for j in 1...items.length   # Items held by Trainer
          realitems.push(parseItem(items[j])) if items[j] && items[j]!=""
        end
        pkmn=[]
        for j in 0...numpoke
          FileLineData.setLine(lines[i+j+3],linenos[i+j+3])
          poke=strsplit(lines[i+j+3],/\s*,\s*/)
          begin
            # Species
            poke[TPSPECIES]=parseSpecies(poke[TPSPECIES])
          rescue
            raise _INTL("Expected a species name: {1}\r\n{2}",poke[0],FileLineData.linereport)
          end
          # Level
          poke[TPLEVEL]=poke[TPLEVEL].to_i
          raise _INTL("Bad level: {1} (must be from 1-{2})\r\n{3}",poke[TPLEVEL],
            PBExperience::MAXLEVEL,FileLineData.linereport) if poke[TPLEVEL]<=0 || poke[TPLEVEL]>PBExperience::MAXLEVEL
          # Held item
          if !poke[TPITEM] || poke[TPITEM]==""
            poke[TPITEM]=TPDEFAULTS[TPITEM]
          else
            poke[TPITEM]=parseItem(poke[TPITEM])
          end
          # Moves
          moves=[]
          for j in [TPMOVE1,TPMOVE2,TPMOVE3,TPMOVE4]
            moves.push(parseMove(poke[j])) if poke[j] && poke[j]!=""
          end
          for j in 0...4
            index=[TPMOVE1,TPMOVE2,TPMOVE3,TPMOVE4][j]
            if moves[j] && moves[j]!=0
              poke[index]=moves[j]
            else
              poke[index]=TPDEFAULTS[index]
            end
          end
          # Ability
          if !poke[TPABILITY] || poke[TPABILITY]==""
            poke[TPABILITY]=TPDEFAULTS[TPABILITY]
          else
            poke[TPABILITY]=poke[TPABILITY].to_i
            raise _INTL("Bad abilityflag: {1} (must be 0 or 1 or 2-5)\r\n{2}",poke[TPABILITY],FileLineData.linereport) if poke[TPABILITY]<0 || poke[TPABILITY]>5
          end
          # Gender
          if !poke[TPGENDER] || poke[TPGENDER]==""
            poke[TPGENDER]=TPDEFAULTS[TPGENDER]
          else
            if poke[TPGENDER]=="M"
              poke[TPGENDER]=0
            elsif poke[TPGENDER]=="F"
              poke[TPGENDER]=1
            else
              poke[TPGENDER]=poke[TPGENDER].to_i
              raise _INTL("Bad genderflag: {1} (must be M or F, or 0 or 1)\r\n{2}",poke[TPGENDER],FileLineData.linereport) if poke[TPGENDER]<0 || poke[TPGENDER]>1
            end
          end
          # Form
          if !poke[TPFORM] || poke[TPFORM]==""
            poke[TPFORM]=TPDEFAULTS[TPFORM]
          else
            poke[TPFORM]=poke[TPFORM].to_i
            raise _INTL("Bad form: {1} (must be 0 or greater)\r\n{2}",poke[TPFORM],FileLineData.linereport) if poke[TPFORM]<0
          end
          # Shiny
          if !poke[TPSHINY] || poke[TPSHINY]==""
            poke[TPSHINY]=TPDEFAULTS[TPSHINY]
          elsif poke[TPSHINY]=="shiny"
            poke[TPSHINY]=true
          else
            poke[TPSHINY]=csvBoolean!(poke[TPSHINY].clone)
          end
          # Nature
          if !poke[TPNATURE] || poke[TPNATURE]==""
            poke[TPNATURE]=TPDEFAULTS[TPNATURE]
          else
            poke[TPNATURE]=parseNature(poke[TPNATURE])
          end
          # IVs
          if !poke[TPIV] || poke[TPIV]==""
            poke[TPIV]=TPDEFAULTS[TPIV]
          else
            poke[TPIV]=poke[TPIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPIV],FileLineData.linereport) if poke[TPIV]<0 || poke[TPIV]>31
          end
          # Happiness
          if !poke[TPHAPPINESS] || poke[TPHAPPINESS]==""
            poke[TPHAPPINESS]=TPDEFAULTS[TPHAPPINESS]
          else
            poke[TPHAPPINESS]=poke[TPHAPPINESS].to_i
            raise _INTL("Bad happiness: {1} (must be from 0-255)\r\n{2}",poke[TPHAPPINESS],FileLineData.linereport) if poke[TPHAPPINESS]<0 || poke[TPHAPPINESS]>255
          end
          # Nickname
          if !poke[TPNAME] || poke[TPNAME]==""
            poke[TPNAME]=TPDEFAULTS[TPNAME]
          else
            poke[TPNAME]=poke[TPNAME].to_s
            raise _INTL("Bad nickname: {1} (must be 1-20 characters)\r\n{2}",poke[TPNAME],FileLineData.linereport) if (poke[TPNAME].to_s).length>20
          end
          # Shadow
          if !poke[TPSHADOW] || poke[TPSHADOW]==""
            poke[TPSHADOW]=TPDEFAULTS[TPSHADOW]
          else
            poke[TPSHADOW]=csvBoolean!(poke[TPSHADOW].clone)
          end
          # Ball
          if !poke[TPBALL] || poke[TPBALL]==""
            poke[TPBALL]=TPDEFAULTS[TPBALL]
          else
            poke[TPBALL]=poke[TPBALL].to_i
            raise _INTL("Bad form: {1} (must be 0 or greater)\r\n{2}",poke[TPBALL],FileLineData.linereport) if poke[TPBALL]<0
          end
          # EVs
          # HP
          if !poke[TPHPEV] || poke[TPHPEV]==""
            poke[TPHPEV]=TPDEFAULTS[TPHPEV]
          else
            poke[TPHPEV]=poke[TPHPEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPHPEV],FileLineData.linereport) if poke[TPHPEV]<0 || poke[TPHPEV]>255
          end
          # Atk
          if !poke[TPATKEV] || poke[TPATKEV]==""
            poke[TPATKEV]=TPDEFAULTS[TPATKEV]
          else
            poke[TPATKEV]=poke[TPATKEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPATKEV],FileLineData.linereport) if poke[TPATKEV]<0 || poke[TPATKEV]>255
          end
          # Def
          if !poke[TPDEFEV] || poke[TPDEFEV]==""
            poke[TPDEFEV]=TPDEFAULTS[TPDEFEV]
          else
            poke[TPDEFEV]=poke[TPDEFEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPDEFEV],FileLineData.linereport) if poke[TPDEFEV]<0 || poke[TPDEFEV]>255
          end
          # Speed
          if !poke[TPSPEEDEV] || poke[TPSPEEDEV]==""
            poke[TPSPEEDEV]=TPDEFAULTS[TPSPEEDEV]
          else
            poke[TPSPEEDEV]=poke[TPSPEEDEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPSPEEDEV],FileLineData.linereport) if poke[TPSPEEDEV]<0 || poke[TPSPEEDEV]>255
          end
          # Sp.Atk
          if !poke[TPSATKEV] || poke[TPSATKEV]==""
            poke[TPSATKEV]=TPDEFAULTS[TPSATKEV]
          else
            poke[TPSATKEV]=poke[TPSATKEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPSATKEV],FileLineData.linereport) if poke[TPSATKEV]<0 || poke[TPSATKEV]>255
          end
          # Sp.Def
          if !poke[TPSDEFEV] || poke[TPSDEFEV]==""
            poke[TPSDEFEV]=TPDEFAULTS[TPSDEFEV]
          else
            poke[TPSDEFEV]=poke[TPSDEFEV].to_i
            raise _INTL("Bad EV: {1} (must be from 0-255)\r\n{2}",poke[TPSDEFEV],FileLineData.linereport) if poke[TPSDEFEV]<0 || poke[TPSDEFEV]>255
          end
    #
          # IVs
          # HP
          if !poke[TPHPIV] || poke[TPHPIV]==""
            poke[TPHPIV]=TPDEFAULTS[TPHPIV]
          else
            poke[TPHPIV]=poke[TPHPIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPHPIV],FileLineData.linereport) if poke[TPHPIV]<0 || poke[TPHPIV]>31
          end
          # Atk
          if !poke[TPATKIV] || poke[TPATKIV]==""
            poke[TPATKIV]=TPDEFAULTS[TPATKIV]
          else
            poke[TPATKIV]=poke[TPATKIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPATKIV],FileLineData.linereport) if poke[TPATKIV]<0 || poke[TPATKIV]>31
          end
          # Def
          if !poke[TPDEFIV] || poke[TPDEFIV]==""
            poke[TPDEFIV]=TPDEFAULTS[TPDEFIV]
          else
            poke[TPDEFIV]=poke[TPDEFIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPDEFIV],FileLineData.linereport) if poke[TPDEFIV]<0 || poke[TPDEFIV]>31
          end
          # Speed
          if !poke[TPSPEEDIV] || poke[TPSPEEDIV]==""
            poke[TPSPEEDIV]=TPDEFAULTS[TPSPEEDIV]
          else
            poke[TPSPEEDIV]=poke[TPSPEEDIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPSPEEDIV],FileLineData.linereport) if poke[TPSPEEDIV]<0 || poke[TPSPEEDIV]>31
          end
          # Sp.Atk
          if !poke[TPSATKIV] || poke[TPSATKIV]==""
            poke[TPSATKIV]=TPDEFAULTS[TPSATKIV]
          else
            poke[TPSATKIV]=poke[TPSATKIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPSATKIV],FileLineData.linereport) if poke[TPSATKIV]<0 || poke[TPSATKIV]>31
          end
          # Sp.Def
          if !poke[TPSDEFIV] || poke[TPSDEFIV]==""
            poke[TPSDEFIV]=TPDEFAULTS[TPSDEFIV]
          else
            poke[TPSDEFIV]=poke[TPSDEFIV].to_i
            raise _INTL("Bad IV: {1} (must be from 0-31)\r\n{2}",poke[TPSDEFIV],FileLineData.linereport) if poke[TPSDEFIV]<0 || poke[TPSDEFIV]>31
          end
    #
          pkmn.push(poke)
        end
        i+=3+numpoke
        MessageTypes.setMessagesAsHash(MessageTypes::TrainerNames,trainernames)
        trainers.push([trainername,name,realitems,pkmn,partyid])
        nameoffset+=name.length
      end
      save_data(trainers,"Data/trainers.dat")
    end

    Example in trainer.txt:
    # See GYARADOS for example: EV - HP,ATK,DEF,SPEED,SATK,SDEF; IV - HP,ATK,DEF,SPEED,STAK,SDEF
    GYARADOS,50,GYARADOSITE,AQUATAIL,EARTHQUAKE,STONEEDGE,BOUNCE,0,F,,,ADAMANT,31,,Kogasa,,,4,252,0,252,0,0,31,31,31,31,31,31

    Ty ty!
     
    Back
    Top