• 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!
  • Dawn, Gloria, Juliana, or Summer - which Pokémon protagonist is your favorite? Let us know by voting in our poll!
  • 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.

[Scripting Question] Adding a third type, working out PBTypes_Extra

  • 217
    Posts
    15
    Years
    • Seen Nov 29, 2021
    So I've decided to make Pokemon able to have three types. I've worked out how the graphics look, but my lack of coding expertise is biting me in the ass. Line 27 onwards obviously needs to be changed to reflect the new number of types, but I don't understand any of it.

    I guess I'm asking for someone to hold my hand through it.

    EDIT: Also PBEffects has a "Type3". How will this be effected?
     
    Last edited:
    Am I asking too much?

    I'll try for less, can someone explain what this code does, and what the functions mean?
    Code:
      def PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2=nil)
        if opponentType2==nil || opponentType1==opponentType2
          return PBTypes.getEffectiveness(attackType,opponentType1)*2
        else
          mod1=PBTypes.getEffectiveness(attackType,opponentType1)
          mod2=PBTypes.getEffectiveness(attackType,opponentType2)
          return (mod1*mod2)
        end
      end
     
    Code:
      def PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2=nil)
        if opponentType2==nil || opponentType1==opponentType2 [COLOR=Green]# if the opponent is a mono-type[/COLOR]
          return PBTypes.getEffectiveness(attackType,opponentType1)*2 [COLOR=Green]# return just the effectiveness value against that type[/COLOR]
        else [COLOR=Green]# if the opponent is a dual-type[/COLOR]
          mod1=PBTypes.getEffectiveness(attackType,opponentType1) [COLOR=Green]# set mod1 to the effectiveness value against the primary type[/COLOR]
          mod2=PBTypes.getEffectiveness(attackType,opponentType2) [COLOR=Green]# set mod2 [/COLOR][COLOR=Green]to the effectiveness value against the secondary type[/COLOR]
          return (mod1*mod2) [COLOR=Green]# return the two effectiveness values multiplied together[/COLOR]
        end
      end
    It is indeed possible to have a third type as I have successfully implemented it myself.
     
    Thanks! It doesn't seem much but those comments should be enough to work backwards from.
    On more question though, what would the first line become?

    At a guess I'd say "def PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2,opponentType3=nil)" but I'm probably wrong.

    So with the exception of the first line, I think this is right?
    Code:
      def PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2=nil)
        if opponentType2==nil && opponentType3==nil || opponentType1==opponentType2 && opponentType1==opponentType3
          return PBTypes.getEffectiveness(attackType,opponentType1)*2
        else if opponent opponentType3==nil || opponentType2==opponentType3
            mod1=PBTypes.getEffectiveness(attackType,opponentType1)
            mod2=PBTypes.getEffectiveness(attackType,opponentType2)
            return (mod1*mod2)
            else
            mod1=PBTypes.getEffectiveness(attackType,opponentType1)
            mod2=PBTypes.getEffectiveness(attackType,opponentType2)
            mod3=PBTypes.getEffectiveness(attackType,opponentType3)
            return (mod1*mod2*mod3)
          end
        end
      end
     
    Last edited:
    Close—what I've done is the following:
    Code:
      def PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2=nil,opponentType3=nil)
        if opponentType2==nil [COLOR=Green]# checking if the first and second types matched was redundant as the mod2 assignment will show[/COLOR]
          return PBTypes.getEffectiveness(attackType,opponentType1)*2 [COLOR=Green]# return the effectiveness value against the single type[/COLOR]
        else
          mod1=PBTypes.getEffectiveness(attackType,opponentType1)
          [COLOR=Green]# the ternary operator here checks if the primary and secondary types match and sets a flat value of 2 if they do;
          # otherwise it will set mod2 to the secondary type's effectiveness value[/COLOR]
          mod2=(opponentType1==opponentType2) ? 2 : PBTypes.getEffectiveness(attackType,opponentType2)
          [COLOR=Green]# the ternary operator here checks if the tertiary type doesn't exist and sets a flat value of 2 if the condition
          # is true; otherwise it will set mod3 to the tertiary type's effectiveness value[/COLOR]
          mod3=(opponentType3==nil) ? 2 : PBTypes.getEffectiveness(attackType,opponentType3)
          return (mod1*mod2*mod3) [COLOR=Green]# returns the three effectiveness values multiplied together[/COLOR]
        end
      end
    For this code to work properly, make sure you modify the codes below otherwise the game will say a normally-effective attack is super effective.

    Code:
      def pbTypeModifier(type,attacker,opponent) [COLOR=Green]# in PokeBattle_Move[/COLOR]
        return [COLOR=Red]8[/COLOR] if type<0 [COLOR=Green]# return normally effective if the move's type is not defined--this line
        # prevents some error messages that would end execution[/COLOR]
        return [COLOR=Red]8[/COLOR] if isConst?(type,PBTypes,:GROUND) && opponent.pbHasType?(:FLYING) &&
                    opponent.hasWorkingItem(:IRONBALL) [COLOR=Green]# return normally effective if the move's type
        # is Ground and the opponent has Flying as a type as well as an Iron Ball equipped
        # (the function calls above may differ depending on your version of Essentials--mine is v15.1)[/COLOR]
        ... [COLOR=Green]# the rest of the function
    [COLOR=Black]    return mod1*mod2[COLOR=Red]*mod3[/COLOR][/COLOR]
    [/COLOR]  end
    
      def pbTypeModMessages(type,attacker,opponent) [COLOR=Green]# in PokeBattle_Move[/COLOR]
        return [COLOR=Red]8[/COLOR] if type<0
        ... [COLOR=Green]# the rest of the function
    [/COLOR]  end
      [COLOR=Green]# the following is in PBTypes_Extra[/COLOR]
      def PBTypes.isNotVeryEffective?(attackType,opponentType1,opponentType2=nil[COLOR=Red],opponentType3=nil[/COLOR])
        e=PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2[COLOR=Red],opponentType3[/COLOR])
        return e>0 && e<[COLOR=Red]8[/COLOR]
      end
    
      def PBTypes.isNormalEffective?(attackType,opponentType1,opponentType2=nil[COLOR=Red],opponentType3=nil[/COLOR])
        e=PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2[COLOR=Red],opponentType3[/COLOR])
        return e==[COLOR=Red]8[/COLOR]
      end
    
      def PBTypes.isIneffective?(attackType,opponentType1,opponentType2=nil[COLOR=Red],opponentType3=nil[/COLOR])
        e=PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2[COLOR=Red],opponentType3[/COLOR])
        return e==0
      end
    
      def PBTypes.isSuperEffective?(attackType,opponentType1,opponentType2=nil[COLOR=Red],opponentType3=nil[/COLOR])
        e=PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2[COLOR=Red],opponentType3[/COLOR])
        return e>[COLOR=Red]8[/COLOR]
      end
    Another thing to note is the PokeBattle_Battler class needs a @type3 attribute defined. Also, modify the function calls to the PBTypes_Extra functions shown if a third type needs to be taken into account; especially for the super effective check.

    The ellipses (...) in the code are placeholder marks for unmodified code in the function.
     
    So for anticipation in PokeBattle_Battler, I changed it to this:
    Code:
       if self.hasWorkingAbility(:ANTICIPATION) && @battle.pbOwnedByPlayer?(@index) && onactive
          PBDebug.log("[Ability triggered] #{pbThis} has Anticipation")
          found=false
          for foe in [pbOpposing1,pbOpposing2]
            next if foe.isFainted?
            for j in foe.moves
              movedata=PBMoveData.new(j.id)
              eff=PBTypes.getCombinedEffectiveness(movedata.type,type1,type2,type3)
              if (movedata.basedamage>0 && eff>4) ||
                 (movedata.function==0x70 && eff>0) # OHKO
                found=true
                break
              end
            end
            break if found
          end
          @battle.pbDisplay(_INTL("{1} shuddered with anticipation!",pbThis)) if found
        end
    I'm guessing 4 needs to become 8 too?

    EDIT: I'm looking at def pbTypeModifier and it looks like half of it's already there?
    return 8 is already there, and there's lines like this:
    Code:
    otype3=opponent.effects[PBEffects::Type3] || -1

    EDIT 2:
    Code:
      def hasType?(type)
        if type.is_a?(String) || type.is_a?(Symbol)
          return isConst?(self.type1,PBTypes,type) || isConst?(self.type2,PBTypes,type)
        else
          return self.type1==type || self.type2==type
        end
      end
    Is throwing me off. I can see what it does, but I don't know how to adapt it.
     
    Last edited:
    Bingo on the first; though if you're using @effects[PBEffects::Type3] for the third type, change the code as follows:
    Code:
       if self.hasWorkingAbility(:ANTICIPATION) && @battle.pbOwnedByPlayer?(@index) && onactive
          PBDebug.log("[Ability triggered] #{pbThis} has Anticipation")
          found=false
          for foe in [pbOpposing1,pbOpposing2]
            next if foe.isFainted?
            for j in foe.moves
              movedata=PBMoveData.new(j.id)
              eff=PBTypes.getCombinedEffectiveness(movedata.type,[COLOR=Red]@[/COLOR]type1,[COLOR=Red]@[/COLOR]type2,[COLOR=Red]@effects[PBEffects::Type3][/COLOR])
              if (movedata.basedamage>0 && eff>[COLOR=Red]8[/COLOR]) ||
                 (movedata.function==0x70 && eff>0) # OHKO
                found=true
                break
              end
            end
            break if found
          end
          @battle.pbDisplay(_INTL("{1} shuddered with anticipation!",pbThis)) if found
        end
    For def pbTypeModifier, the line shown is supposed to be there if you're using PBEffects to simulate a third type. I did something different, but also effective by adding a @type3 attribute to the PokeBattle_Battler class initialised as nil and set when certain moves add a type to a dual-type Pokémon.

    As for the def hasType?(type), here's how to adapt it:
    Code:
      def hasType?(type)
        if type.is_a?(String) || type.is_a?(Symbol)
          return isConst?(self.type1,PBTypes,type) || isConst?(self.type2,PBTypes,type) [COLOR=Red]||
          isConst?(@effects[PBEffects::Type3],PBTypes,type)[/COLOR]
        else
          return self.type1==type || self.type2==type[COLOR=Red] || @effects[PBEffects::Type3]==type[/COLOR]
        end
      end
    These modifications assume that @effects[PBEffects::Type3] is set to a PBTypes value if it's not set to nil or –1.
     
    Last edited:
    Oh I don't intend on using PBEffects::Type3, I just wasn't sure if it'd break anything.

    I'm having problems still but I think it's got complex to the point I might just have to upload my files. Stuff like types not appearing graphically where they should, I'm pretty sure I've accidentally made all pokemon mono-type, stuff like that :P :/

    That'll have to wait until I'm home though.
     
    At this point, the best option would be to upload your files and see what can be done to fix it. If nothing works, I'd suggest to start with a vanilla Essentials pack and work off that.
     
    Looking at it, I kind of see what your problems are. The PokeBattle_Pokemon class's def hasType? should not check for a third type unless you plan on having triple-type Pokémon in your game. The third type conundrum is usually brought about through trying to implement Forest's Curse and Trick-or-Treat, which adds a type during battle. For that reason, most people do the third type in the PokeBattle_Battler's def pbhasType? function as well as the type effectiveness checks in PBTypes_Extra.
     
    The PokeBattle_Pokemon class's def hasType? should not check for a third type unless you plan on having triple-type Pokémon in your game.
    I wouldn't be adding a third type if I didn't intend of having triple-typed pokemon :P
    The third type conundrum is usually brought about through trying to implement Forest's Curse and Trick-or-Treat, which adds a type during battle. For that reason, most people do the third type in the PokeBattle_Battler's def pbhasType? function as well as the type effectiveness checks in PBTypes_Extra.
    Aaaand you've lost me. I was just attempting to implement the third type anywhere a second type was relevant.

    If that's what PBEffect third type was about, I just renamed it all to Type4.
     
    Last edited:
    What I'm trying to say is, there is a bit of a difference between having a third type added through the effects of Forest's Curse or Trick-or-Treat (adding a type, making mono-type Pokémon temporary dual-types and giving dual-type Pokémon a temporary third type until they switch out or faint) and having triple-type Pokémon occur either in the wild or through evolution such as obtaining a Poison/Water/Dragon-type Dragalge as an example. The latter case presents the possibility of the aforementioned moves giving those Pokémon a fourth type.
     
    So... what do I have to do to account for that?
    Or did making all references to PBEffects Type3 into PBEffects Type4 solve that for me?

    EDIT: IGNORE EVERYTHING ABOVE, IT'S OBSOLETE.

    So I started over afresh, and I've gotten it almost working. Types show up where they should, the one test Pokemon (Venomoth) shows the correct typing (Bug, Psychic, Flying). There's just two problems.
    1. Every dual type pokemon is showing "normal" in addition to their other types. At first I thought this was just cosmetic, because it didn't convey an immunity to Ghost to any of them, however...
    2. It doesn't seem to be having an effect. In the test case, Mud Slap still hits Venomoth, despite its third type being flying.

    https://1drv.ms/u/s!At-zPv0cZTn6hQaT7J0JSUibhy7v
     
    Last edited:
    I don't know why Normal pops up along with the other types for problem #1.

    Problem #2 is a bug found in PokeBattle_Move, line 363. otype3 is taking a value of –1 and therefore the pbTypeModifier function does not recognise the battler's third type. Change the line to:
    Code:
    otype3=opponent.[COLOR=Red]type3[/COLOR] || -1
    All instances of effects[PBEffects::Type3] will need to be changed to type3 for all occurrences in the PokeBattle_MoveEffects and PokeBattle_AI scripts as they may have similar problems.

    Just after line 374 in PokeBattle_Move is a possible bug I noticed—what would happen if an opposing Pokémon used Roost and Flying was the third type? A solution would be to set that type to either one of the other two types, nil or –1.
     
    Last edited:
    Problem #1 is a bug found in PokeBattle_Battler, line 288. A type value of 0 equates to the Normal type so the proper way to initialise it is to set @type3 to nil or –1 instead.
    Out of curiosity, why are @type1 and @type2 initialised to 0?
    EDIT: Setting it to -1 did not solve the problem. Maybe the problem is purely visual and just in my summary script?
    EDIT 2: Nope, definitely not visual only.
    EDIT 3: Around line 1740ish of compiler, there's something that seems to set type 2 to type 1 if it's not present, maybe that needs to be expanded to the third type too?

    Problem #2 is a bug found in PokeBattle_Move, line 363. otype3 is taking a value of –1 and therefore the pbTypeModifier function does not recognise the battler's third type. Change the line to:
    Code:
    otype3=opponent.[COLOR=Red]type3[/COLOR] || -1
    All instances of effects[PBEffects::Type3] will need to be changed to type3 for all occurrences in the PokeBattle_MoveEffects and PokeBattle_AI scripts as they may have similar problems.
    Thanks!

    Just after line 374 in PokeBattle_Move is a possible bug I noticed—what would happen if an opposing Pokémon used Roost and Flying was the third type? A solution would be to set that type to either one of the other two types, nil or –1.
    ... Good question. How would I go about implementing the -1 solution?

    For now, I'll just avoid making Flying the third type.

    Wait, does this line check both type 2 and 3? If so, why?
    Code:
          if isConst?(otype2,PBTypes,:FLYING) && isConst?(otype3,PBTypes,:FLYING)
     
    Last edited:
    The first case was a mistake on my part and line 288 was part of initialising a blank Pokémon.

    The compiler case checks and raises an error if no types have been defined for a Pokémon; otherwise it makes Type2 the same as Type1. That code in the Compiler can be changed to the following:
    Code:
            if [COLOR=Red]([/COLOR]!lastsection["Type2"] || lastsection["Type2"]==""[COLOR=Red]) &&
              (!lastsection["Type3"] || lastsection["Type3"]=="")[/COLOR]
              if !lastsection["Type1"] || lastsection["Type1"]==""
                raise _INTL("No Pokémon type is defined in section {2} (PBS/pokemon.txt)",key,sectionDisplay) if hash==requiredtypes
                next
              end
              lastsection["Type2"]=lastsection["Type1"].clone
              [COLOR=Red]lastsection["Type3"]=[/COLOR][COLOR=Red](!lastsection["Type2"] || lastsection["Type2"]=="") ? lastsection["Type1"].clone : lastsection["Type2"].clone[/COLOR]
            end
    The extra Normal type seems to only occur on dual-type Pokémon and thus the bug may be in how the program sets the third type for Pokémon with only two types. Perhaps this code would do the trick:
    Code:
              @type3   = @pokemon.type3 [COLOR=Red]|| -1[/COLOR] #NEW

    As for the Roost solution, here's the solution:
    Code:
        if isConst?(otype1,PBTypes,:FLYING) && opponent.effects[PBEffects::Roost]
          [COLOR=Green]# the following if statement checks if a Pokémon is a mono-Flying type[/COLOR]
          if isConst?(otype2,PBTypes,:FLYING) && [COLOR=Red]([/COLOR]isConst?(otype3,PBTypes,:FLYING)[COLOR=Red] ||
             otype3==-1)
    [/COLOR]        otype1=getConst(PBTypes,:NORMAL) || 0
          else
            otype1=otype2
          end
        end
        if isConst?(otype2,PBTypes,:FLYING) && opponent.effects[PBEffects::Roost]
          otype2=otype1
        [COLOR=Red]elsif isConst?(otype3,PBTypes,:FLYING) && opponent.effects[PBEffects::Roost]
          otype3=-1
    [/COLOR]    end
     
    None of the above have resolved the issue of the mysterious normal type appearing.

    Couldn't:
    Code:
    lastsection["Type3"]=(!lastsection["Type2"] || lastsection["Type2"]=="") ? lastsection["Type1"].clone : lastsection["Type2"].clone
    be condensed to:
    Code:
    lastsection["Type3"]=lastsection["Type2"].clone
    Since type2 was just set to type1?
     
    There's something in there that sets the Pokémon's third type to 0 (Normal) if the second type is different to the first. I've done what I can, but I can't find the bug that's causing this. You might have to ask one of the developers.
     
    Back
    Top