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

[Scripting Question] Extra Type Effectivenss

Pokeminer20

PDM20, Coder of Chaos!
412
Posts
10
Years
  • utilizing this (https://www.pokecommunity.com/showthread.php?t=391589) tutorial and common sense, I've experimented in attempting an all 18 type pokemon. I've tried 18 types but it causes an overflow issue with the byte data and downgraded to 15 (16 might be the highest, as 17 also overflows). I'm basically stuck on step 5, but with my extra 12 types. this is purely for curiosity reasons and just to say I achieved it, but I can't common sense the code to work with all types and my latest attempt hasn't worked either. (I'm testing with a 15 type Arceus that has ground type as type #5 and electric attacks hit it.) this is my current code.

    Code:
    class PBTypes
      @@TypeData = nil
    
      def PBTypes.loadTypeData
        if !@@TypeData
          @@TypeData = load_data("Data/types.dat")
          @@TypeData[0].freeze
          @@TypeData[1].freeze
          @@TypeData[2].freeze
          @@TypeData[3].freeze
          @@TypeData[4].freeze
          @@TypeData[5].freeze
          @@TypeData[6].freeze
          @@TypeData[7].freeze
          @@TypeData[8].freeze
          @@TypeData[9].freeze
          @@TypeData[10].freeze
          @@TypeData[11].freeze
          @@TypeData[12].freeze
          @@TypeData[13].freeze
          @@TypeData[14].freeze
          @@TypeData.freeze
        end
        return @@TypeData
      end
    
      def PBTypes.isPseudoType?(type)
        return PBTypes.loadTypeData()[0].include?(type)
      end
    
      def PBTypes.isSpecialType?(type)
        return PBTypes.loadTypeData()[1].include?(type)
      end
    
      def PBTypes.getEffectiveness(attackType,opponentType)
        return 2 if !opponentType || opponentType<0
        return PBTypes.loadTypeData()[2][attackType*(PBTypes.maxValue+1)+opponentType]
      end
    
      def PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2=nil,opponentType3=nil,opponentType4=nil,opponentType5=nil,opponentType6=nil,opponentType7=nil,opponentType8=nil,opponentType9=nil,opponentType10=nil,opponentType11=nil,opponentType12=nil,opponentType13=nil,opponentType14=nil,opponentType15=nil)
        mod1  = PBTypes.getEffectiveness(attackType,opponentType1)
        mod2  = 2
        mod3  = 2
        mod4  = 2
        mod5  = 2
        mod6  = 2
        mod7  = 2
        mod8  = 2
        mod9  = 2
        mod10 = 2
        mod11 = 2
        mod12 = 2
        mod13 = 2
        mod14 = 2
        mod15 = 2
        if opponentType2!=nil && opponentType2>=0 && opponentType1!=opponentType2
          mod2 = PBTypes.getEffectiveness(attackType,opponentType2)
        end
        if opponentType3!=nil && opponentType3>=0 &&
           opponentType1!=opponentType3 && opponentType2!=opponentType3
          mod3 = PBTypes.getEffectiveness(attackType,opponentType3)
        end
        if opponentType4!=nil && opponentType4>=0 &&
           opponentType1!=opponentType4 && opponentType2!=opponentType4 && 
           opponentType3!=opponentType4
          mod4 = PBTypes.getEffectiveness(attackType,opponentType4)
        end
        if opponentType5!=nil && opponentType5>=0 &&
           opponentType1!=opponentType5 && opponentType2!=opponentType5 && 
           opponentType3!=opponentType5 && opponentType4!=opponentType5
          mod5 = PBTypes.getEffectiveness(attackType,opponentType5)
        end
        if opponentType6!=nil && opponentType6>=0 &&
           opponentType1!=opponentType6 && opponentType2!=opponentType6 && 
           opponentType3!=opponentType6 && opponentType4!=opponentType6 && 
           opponentType5!=opponentType6
          mod6 = PBTypes.getEffectiveness(attackType,opponentType6)
        end
        if opponentType7!=nil && opponentType7>=0 &&
           opponentType1!=opponentType7 && opponentType2!=opponentType7 && 
           opponentType3!=opponentType7 && opponentType4!=opponentType7 && 
           opponentType5!=opponentType7 && opponentType6!=opponentType7
          mod7 = PBTypes.getEffectiveness(attackType,opponentType7)
        end
        if opponentType8!=nil && opponentType8>=0 &&
           opponentType1!=opponentType8 && opponentType2!=opponentType8 && 
           opponentType3!=opponentType8 && opponentType4!=opponentType8 && 
           opponentType5!=opponentType8 && opponentType6!=opponentType8 && 
           opponentType7!=opponentType8
          mod8 = PBTypes.getEffectiveness(attackType,opponentType8)
        end
        if opponentType9!=nil && opponentType9>=0 &&
           opponentType1!=opponentType9 && opponentType2!=opponentType9 && 
           opponentType3!=opponentType9 && opponentType4!=opponentType9 && 
           opponentType5!=opponentType9 && opponentType6!=opponentType9 && 
           opponentType7!=opponentType9 && opponentType8!=opponentType9
          mod9 = PBTypes.getEffectiveness(attackType,opponentType9)
        end
        if opponentType10!=nil && opponentType10>=0 &&
           opponentType1!=opponentType10 && opponentType2!=opponentType10 && 
           opponentType3!=opponentType10 && opponentType4!=opponentType10 && 
           opponentType5!=opponentType10 && opponentType6!=opponentType10 && 
           opponentType7!=opponentType10 && opponentType8!=opponentType10 && 
           opponentType9!=opponentType10
          mod10 = PBTypes.getEffectiveness(attackType,opponentType10)
        end
        if opponentType11!=nil && opponentType11>=0 &&
           opponentType1!=opponentType11 && opponentType2!=opponentType11 && 
           opponentType3!=opponentType11 && opponentType4!=opponentType11 && 
           opponentType5!=opponentType11 && opponentType6!=opponentType11 && 
           opponentType7!=opponentType11 && opponentType8!=opponentType11 && 
           opponentType9!=opponentType11 && opponentType10!=opponentType11
          mod11 = PBTypes.getEffectiveness(attackType,opponentType11)
        end
        if opponentType12!=nil && opponentType12>=0 &&
           opponentType1!=opponentType12 && opponentType2!=opponentType12 && 
           opponentType3!=opponentType12 && opponentType4!=opponentType12 && 
           opponentType5!=opponentType12 && opponentType6!=opponentType12 && 
           opponentType7!=opponentType12 && opponentType8!=opponentType12 && 
           opponentType9!=opponentType12 && opponentType10!=opponentType12 && 
           opponentType11!=opponentType12
          mod12 = PBTypes.getEffectiveness(attackType,opponentType12)
        end
        if opponentType13!=nil && opponentType13>=0 &&
           opponentType1!=opponentType13 && opponentType2!=opponentType13 && 
           opponentType3!=opponentType13 && opponentType4!=opponentType13 && 
           opponentType5!=opponentType13 && opponentType6!=opponentType13 && 
           opponentType7!=opponentType13 && opponentType8!=opponentType13 && 
           opponentType9!=opponentType13 && opponentType10!=opponentType13 && 
           opponentType11!=opponentType13 && opponentType12!=opponentType13
          mod13 = PBTypes.getEffectiveness(attackType,opponentType13)
        end
        if opponentType14!=nil && opponentType14>=0 &&
           opponentType1!=opponentType14 && opponentType2!=opponentType14 && 
           opponentType3!=opponentType14 && opponentType4!=opponentType14 && 
           opponentType5!=opponentType14 && opponentType6!=opponentType14 && 
           opponentType7!=opponentType14 && opponentType8!=opponentType14 && 
           opponentType9!=opponentType14 && opponentType10!=opponentType14 && 
           opponentType11!=opponentType14 && opponentType12!=opponentType14 && 
           opponentType13!=opponentType14
          mod14 = PBTypes.getEffectiveness(attackType,opponentType14)
        end
        if opponentType15!=nil && opponentType15>=0 &&
           opponentType1!=opponentType15 && opponentType2!=opponentType15 && 
           opponentType3!=opponentType15 && opponentType4!=opponentType15 && 
           opponentType5!=opponentType15 && opponentType6!=opponentType15 && 
           opponentType7!=opponentType15 && opponentType8!=opponentType15 && 
           opponentType9!=opponentType15 && opponentType10!=opponentType15 && 
           opponentType11!=opponentType15 && opponentType12!=opponentType15 && 
           opponentType13!=opponentType15 && opponentType14!=opponentType15
          mod15 = PBTypes.getEffectiveness(attackType,opponentType15)
        end
        return (mod1*mod2*mod3*mod4*mod5*mod6*mod7*mod8*mod9*mod10*mod11*mod12*mod13*mod14*mod15)
      end
    
      def PBTypes.isIneffective?(attackType,opponentType1,opponentType2=nil,opponentType3=nil,opponentType4=nil,opponentType5=nil,opponentType6=nil,opponentType7=nil,opponentType8=nil,opponentType9=nil,opponentType10=nil,opponentType11=nil,opponentType12=nil,opponentType13=nil,opponentType14=nil,opponentType15=nil)
        e = PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2,opponentType3,opponentType4,opponentType5,opponentType6,opponentType7,opponentType8,opponentType9,opponentType10,opponentType11,opponentType12,opponentType13,opponentType14,opponentType15)
        return e==0
      end
    
      def PBTypes.isNotVeryEffective?(attackType,opponentType1,opponentType2=nil,opponentType3=nil,opponentType4=nil,opponentType5=nil,opponentType6=nil,opponentType7=nil,opponentType8=nil,opponentType9=nil,opponentType10=nil,opponentType11=nil,opponentType12=nil,opponentType13=nil,opponentType14=nil,opponentType15=nil)
        e = PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2,opponentType3,opponentType4,opponentType5,opponentType6,opponentType7,opponentType8,opponentType9,opponentType10,opponentType11,opponentType12,opponentType13,opponentType14,opponentType15)
        return e>0 && e<8
      end
    
      def PBTypes.isNormalEffective?(attackType,opponentType1,opponentType2=nil,opponentType3=nil,opponentType4=nil,opponentType5=nil,opponentType6=nil,opponentType7=nil,opponentType8=nil,opponentType9=nil,opponentType10=nil,opponentType11=nil,opponentType12=nil,opponentType13=nil,opponentType14=nil,opponentType15=nil)
        e = PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2,opponentType3,opponentType4,opponentType5,opponentType6,opponentType7,opponentType8,opponentType9,opponentType10,opponentType11,opponentType12,opponentType13,opponentType14,opponentType15)
        return e==8
      end
    
      def PBTypes.isSuperEffective?(attackType,opponentType1,opponentType2=nil,opponentType3=nil,opponentType4=nil,opponentType5=nil,opponentType6=nil,opponentType7=nil,opponentType8=nil,opponentType9=nil,opponentType10=nil,opponentType11=nil,opponentType12=nil,opponentType13=nil,opponentType14=nil,opponentType15=nil)
        e = PBTypes.getCombinedEffectiveness(attackType,opponentType1,opponentType2,opponentType3,opponentType4,opponentType5,opponentType6,opponentType7,opponentType8,opponentType9,opponentType10,opponentType11,opponentType12,opponentType13,opponentType14,opponentType15)
        return e>8
      end
    end

    besides length, anything looking scuffed?
     
    Last edited:

    Pokeminer20

    PDM20, Coder of Chaos!
    412
    Posts
    10
    Years
  • Upon looking more on google, and locating more attempts to add anymore than 3 typings, it seems link the only think these add-ons have in common is no matter what, type effectiveness doesn't work for any of them. I was able to locate the original "Step 5" thread and even with a downloadable copy of essentials with this mod, type 3 effectiveness still doesn't work, be it how the formula acts, or if it needs a complete overhaul, upon giving a Pokemon ground type and fighting an electric type, those electric moves still do damage while in the type 3 position. seems like most type 3 dreams are crushed, but I shall locate a method that works.
     
    233
    Posts
    5
    Years
    • Seen Oct 9, 2023
    While the script you've got here is a start, you also need to consider that each function you gave more parameters to now needs to be edited in every single section of the battle code... so, you are right in that the battle system would need a major overhaul to support more than 2 types + the extra third type effect in battle. Even just adding functionality for a third type (so up to 4 types in battle) requires lots of minute changes all over the battle code, so with 18 types... o_O you'd definitely want to use an array to store all the possible types for each Pokemon/battler at that point, but that would also completely change how you work with types in the battle code in general. Heh, now I might take a crack at it...
     
    Last edited:

    Pokeminer20

    PDM20, Coder of Chaos!
    412
    Posts
    10
    Years
  • the solution has been found. by looking at all the post of the original thread I linked here, the poster uploaded this https://www.pokecommunity.com/showthread.php?t=379877 which is basically step 5, but in more detailed. in post #7, the one helping the OP states modifying def def pbTypeModifier in the PokeBattle_Move section, and def pbHasType in PokeBattle_Battler. as I have yet to fully mod def pbTypeModifier, I shall work on it more(to work with all my alterations) but will post the pbHasType? mod here.


    Code:
      def pbHasType?(type)
        ret=false
        if type.is_a?(Symbol) || type.is_a?(String)
          ret=isConst?(self.type1,PBTypes,type.to_sym) ||
              isConst?(self.type2,PBTypes,type.to_sym) ||
              isConst?(self.type3,PBTypes,type.to_sym) ||
              isConst?(self.type4,PBTypes,type.to_sym) ||
              isConst?(self.type5,PBTypes,type.to_sym) ||
              isConst?(self.type6,PBTypes,type.to_sym) ||
              isConst?(self.type7,PBTypes,type.to_sym) ||
              isConst?(self.type8,PBTypes,type.to_sym) ||
              isConst?(self.type9,PBTypes,type.to_sym) ||
              isConst?(self.type10,PBTypes,type.to_sym) ||
              isConst?(self.type11,PBTypes,type.to_sym) ||
              isConst?(self.type12,PBTypes,type.to_sym) ||
              isConst?(self.type13,PBTypes,type.to_sym) ||
              isConst?(self.type14,PBTypes,type.to_sym) ||
              isConst?(self.type15,PBTypes,type.to_sym)
        else
          ret=(self.type1==type || self.type2==type || self.type3==type || self.type4==type || self.type5==type || self.type6==type || self.type7==type || self.type8==type || self.type9==type || self.type10==type ||self.type11==type || self.type12==type || self.type13==type || self.type14==type || self.type15==type)
        end
        return ret
      end
     
    Last edited:
    233
    Posts
    5
    Years
    • Seen Oct 9, 2023
    That solution is better, just need to keep in mind two additional things. One is that any type conversion abilities/moves, or really anything that manually handles types needs to be edited (ex. Protean, Conversion, Transform, etc.).
    The other is that when the battle code calculates the type modifier, the "neutral effectiveness" value is set to 2 to the power of NUMTYPES (the max number of types a Pokemon in battle can have), which is 8 in Essentials v16.2+ because of dual-types + the extra type given by moves like Forest's Curse, so everywhere that 8 appears in the battle code needs to be replaced with 2 to the power of how many ever types you want a battler to have at most. In the case of OP it would be 2^15 (because a battle can have at most 15 types). So I think with the solutions OP posted + these additional things, having multiple types should work perfectly.
     
    Last edited:

    Pokeminer20

    PDM20, Coder of Chaos!
    412
    Posts
    10
    Years
  • That solution is better, just need to keep in mind two additional things. One is that any type conversion abilities/moves, or really anything that manually handles types needs to be edited (ex. Protean, Conversion, Transform, etc.).
    The other is that when the battle code calculates the type modifier, the "neutral effectiveness" value is set to 2 to the power of NUMTYPES (the max number of types a Pokemon in battle can have), which is 8 in Essentials v16.2+ because of dual-types + the extra type given by moves like Forest's Curse, so everywhere that 8 appears in the battle code needs to be replaced with 2 to the power of how many ever types you want a battler to have at most. In the case of OP it would be 2^15 (because a battle can have at most 15 types). So I think with the solutions OP posted + these additional things, having multiple types should work perfectly.

    even replacing those instances of 8 causes a new issue I'll call "the Hyper Effectiveness" Bug. case in point, the game uses a method called "typemod' that determines how effective a move is. typemod 0 is immune, typemod 0-3 is not very, and typemod 4 and 8 are super effective. there is another call called @skill, but I haven't a clue what it does, but it probably correlates to the type chart somehow. anyway, upon modifying the formula to what I altered just after post #4, the new formula, formerly being mod1*mod2*mod3, now is mod1*mod2*mod3...mod15*mod16*modex(modex being the effect typing that forest curse like moves now use.) here's my current code.

    Code:
      def pbTypeModifier(type,attacker,opponent)
        return 131072 if type<0
        return 131072 if isConst?(type,PBTypes,:GROUND) && opponent.pbHasType?(:FLYING) &&
                    opponent.hasWorkingItem(:IRONBALL) && !USENEWBATTLEMECHANICS
        atype=type # attack type
        otype1=opponent.type1
        otype2=opponent.type2
        otype3=opponent.type3
        otype4=opponent.type4
        otype5=opponent.type5
        otype6=opponent.type6
        otype7=opponent.type7
        otype8=opponent.type8
        otype9=opponent.type9
        otype10=opponent.type10
        otype11=opponent.type11
        otype12=opponent.type12
        otype13=opponent.type13
        otype14=opponent.type14
        otype15=opponent.type15
        otype16=opponent.type16
        otypeex=opponent.effects[PBEffects::TypeExtra] || -1
        # Roost is the same
        # Get effectivenesses
        mod1=PBTypes.getEffectiveness(atype,otype1)
        mod2=(otype1==otype2) ? 2 : PBTypes.getEffectiveness(atype,otype2)
        mod3=(otype2==otype3) ? 2 : PBTypes.getEffectiveness(atype,otype3)
        mod4=(otype3==otype4) ? 2 : PBTypes.getEffectiveness(atype,otype4)
        mod5=(otype4==otype5) ? 2 : PBTypes.getEffectiveness(atype,otype5)
        mod6=(otype5==otype6) ? 2 : PBTypes.getEffectiveness(atype,otype6)
        mod7=(otype6==otype7) ? 2 : PBTypes.getEffectiveness(atype,otype7)
        mod8=(otype7==otype8) ? 2 : PBTypes.getEffectiveness(atype,otype8)
        mod9=(otype8==otype9) ? 2 : PBTypes.getEffectiveness(atype,otype9)
        mod10=(otype9==otype10) ? 2 : PBTypes.getEffectiveness(atype,otype10)
        mod11=(otype10==otype11) ? 2 : PBTypes.getEffectiveness(atype,otype11)
        mod12=(otype11==otype12) ? 2 : PBTypes.getEffectiveness(atype,otype12)
        mod13=(otype12==otype13) ? 2 : PBTypes.getEffectiveness(atype,otype13)
        mod14=(otype13==otype14) ? 2 : PBTypes.getEffectiveness(atype,otype14)
        mod15=(otype14==otype15) ? 2 : PBTypes.getEffectiveness(atype,otype15)
        mod16=(otype15==otype16) ? 2 : PBTypes.getEffectiveness(atype,otype16)
        modex=(otypeex<0 || otype1==otypeex || otype2==otypeex || otype3==otypeex || otype4==otypeex || otype5==otypeex || otype6==otypeex || otype7==otypeex || otype8==otypeex || otype9==otypeex || otype10==otypeex || otype11==otypeex || otype12==otypeex || otype13==otypeex || otype14==otypeex || otype15==otypeex || otype16==otypeex) ? 2 : PBTypes.getEffectiveness(atype,otypeex)
    #RINGTARGET to last end is the same
        return mod1*mod2*mod3*mod4*mod5*mod5*mod6*mod7*mod8*mod10*mod11*mod12*mod13*mod14*mod15*mod16*modex
      end
    my working theory is that 2 is used as neutral damage, which when set to 4 causes super effective damage. I'm unsure how to do it but if I knew how to rework the damage calculations or conditional these effectiveness, I might be able to perfect this (setting each instance of 2 in mon2 - modex results in not effective, but and 2's present will add another effecticveness layer)
     

    Pokeminer20

    PDM20, Coder of Chaos!
    412
    Posts
    10
    Years
  • of course after posting that I find the solution. after doing everything above, locate
    Code:
    damage=(damage*typemod/8.0).round
    and replace it with
    Code:
    damage=(damage*typemod/65536.0).round
    and mechanically, everything is fine. however, there's more to modify, because since it now sees 65536 as pure neutral, 8+ now is barely effective, but will say 'super effective' and to fix that, locate "def pbEffectMessages" and alter it accordingly to whatever you need.

    for a basic example, I'd recomend using This:
    Code:
      def pbEffectMessages(attacker,opponent,ignoretype=false,alltargets=nil)
        if opponent.damagestate.critical
          if alltargets && alltargets.length>1
            @battle.pbDisplay(_INTL("A critical hit on {1}!",opponent.pbThis(true)))
          else
            @battle.pbDisplay(_INTL("A critical hit!"))
          end
        end
        if !pbIsMultiHit && attacker.effects[PBEffects::ParentalBond]==0
          if opponent.damagestate.typemod>131072
            if alltargets && alltargets.length>1
              @battle.pbDisplay(_INTL("It's super effective on {1}!",opponent.pbThis(true)))
            else
              @battle.pbDisplay(_INTL("It's super effective!"))
            end
          elsif opponent.damagestate.typemod>=1 && opponent.damagestate.typemod<32768
            if alltargets && alltargets.length>1
              @battle.pbDisplay(_INTL("It's not very effective on {1}...",opponent.pbThis(true)))
            else
              @battle.pbDisplay(_INTL("It's not very effective..."))
            end
          end
        end
     
    1,403
    Posts
    10
    Years
    • Seen Apr 29, 2024
    I would strongly recommend trying to use a sensible floating point representation of typemod (i.e. 1 = neutral, > 1 = super-effective, < 1 = resisted; so you just multiply it directly onto the number instead of /8 first) rather than keeping the 2/4/8/16/32 thing if you're going to make changes to that part of the code.

    You'll still have to modify all the places that check for effectiveness, but at least you'll be able to use sensible numbers that the next person to read your code will understand.

    I have no idea why Essentials hasn't changed to this obvious solution yet…
     
    Back
    Top