• 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!
  • Our weekly protagonist poll is now up! Vote for your favorite Conquest protagonist in the poll by clicking here.
  • 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] Trying to add a second Ability that turns off other Abilities, get "Stack level too deep" error

  • 4
    Posts
    8
    Years
    • Seen Apr 15, 2025
    So I'm trying to get an ability that disables other abilities to work (distinct from Neutralizing Gas because this new ability also disables held items and bonuses from stat boosts). For the Ability function, I copied the code from Neutralizing Gas for this new ability in places where Neutralizing Gas is checked for, including for the abilityActive? check.


    [PokeCommunity.com] Trying to add a second Ability that turns off other Abilities, get "Stack level too deep" error

    However, it seems like I can't have more than one @battle.pbCheckGlobalAbility check in a defined function, as it causes a SystemStackError:

    [PokeCommunity.com] Trying to add a second Ability that turns off other Abilities, get "Stack level too deep" error

    It looks like something's causing @battle.pbCheckGlobalAbility to run in an endless loop if it tries to check for more than one Ability, and I'm admittedly stumped at how to fix it... some advice would be appreciated 🙏

    Here's the code that's causing the issue as text:

    Ruby:
    def abilityActive?(ignore_fainted = false, check_ability = nil)
        echoln "this is the (Not Very Nice Person)"
        return false if fainted? && !ignore_fainted
        if Settings::MECHANICS_GENERATION >= 9
          return true if !check_ability && self.ability == :BATTLEBOND
          if @proteanTrigger && self.ability == @effects[PBEffects::OneUseAbility]
            return false if !check_ability || check_ability == self.ability
            return false if check_ability.is_a?(Array) && check_ability.include?(@ability_id)
          end
        end
        return false if @effects[PBEffects::GastroAcid]
        return false if (check_ability != :NEUTRALIZINGGAS && self.ability != :NEUTRALIZINGGAS &&
                        !activeAbilityShield?(check_ability) && @battle.pbCheckGlobalAbility(:NEUTRALIZINGGAS))
        return false if check_ability != :NIHILITY && self.ability != :NIHILITY && @battle.pbCheckGlobalAbility(:NIHILITY)
        return true
      end
     
    After a little bit of examining abilityActive? and hasActiveAbility? (Battler) and pbCheckGlobalAbility (Battle), I worked the following out:
    The code is written in that order for a reason. If the ability checked, or the user has Neut Gas, it does not even run the check for the global ability.

    Lets write the vanilla penultimate and last lines:
    Ruby:
    return false if check_ability != :NEUTRALIZINGGAS && self.ability != :NEUTRALIZINGGAS &&
                        @battle.pbCheckGlobalAbility(:NEUTRALIZINGGAS)
    return true
    in a different form
    Ruby:
    return check_ability == :NEUTRALIZINGGAS || self.ability == :NEUTRALIZINGGAS || [email protected](:NEUTRALIZINGGAS)

    Now, this should make it easier to understand. From now on, we assume that the battler checked has neither Neut Gas nor NIHILITY

    It does not go into a loop when done once because if it checks for Neut Gas, it returns true straight away, and if it doesn't, it goes:
    it will run abilityActive? for Not Neut Gas, then checks pbCheckGlobalAbility for Neut Gas, then checks hasActiveAbility? for Neut Gas, then checks abilityActive? for Neut Gas, and returns true because of the "check_ability != :NEUTRALIZINGGAS" part
    This is known as a successful recursion

    Now, what you did was have another line after the return false line that does not return true straight away
    So, our one line solution wouldn't work out too well. Instead, what happens is:

    Ruby:
    return false if check_ability != :NEUTRALIZINGGAS && self.ability != :NEUTRALIZINGGAS &&
                        @battle.pbCheckGlobalAbility(:NEUTRALIZINGGAS)
    return check_ability == :NIHILITY || self.ability == :NIHILITY|| [email protected](:NIHILITY)

    1) Checks abilityActive? for NIHILITY
    2) Checks pbCheckGlobalAbility for Neut Gas
    3) Checks hasActiveAbility? for Neut Gas
    4) Checks abilityActive? for Neut Gas
    5) check_ability is not Neut Gas. Doesn't return false. Move to next line
    6) check_ability is Neut Gas, not NIHILITY, NIHILITY is not the self.ability, so then it checks pbCheckGlobalAbility for NIHILITY
    7) Checks pbCheckGlobalAbility for NIHILITY
    8) Checks hasActiveAbility? for NIHILITY

    and we are back to Step 1)

    Oh, dear.

    So, how to fix?
    Replace the penultimate and last lines only in abilityActive? with the following:
    Ruby:
        block_abils = [:NEUTRALIZINGGAS, :NIHILITY]
        return true if block_abils.include?(check_ability) || block_abils.include?(self.ability)
        for block_abil in block_abils
          return false if @battle.pbCheckGlobalAbility(block_abil)
        end
        return true
    Let's see how this would work
    1) Checks abilityActive? for NIHILITY or Neut Gas
    2) block_abils.include?(check_ability) is true, so returns true without checking pbCheckGlobalAbility

    1) Checks abilityActive? for neither NIHILITY nor Neut Gas
    2) Neither check_ability nor self.ability contains either block_abils, so we move on to the for loop
    3) Checks pbCheckGlobalAbility for Neut Gas
    4) Checks hasActiveAbility? for Neut Gas
    5) Checks abilityActive? for Neut Gas
    6) Returns true. We move on to the next line on hasActiveAbility?
    7) Assuming that the battler does not have Neut Gas, hasActiveAbility? returns false
    8) pbCheckGlobalAbility returns false, and we go back to our original recursion
    9) Repeat Steps 3) to 8) for NIHILITY
    10) It isn't there, so we finish with a final result of true

    If any of the other battlers have one of the two abilities, it will set pbCheckGlobalAbility to true and the first function to false, but will not go in a loop. This makes it a successful recursion

    Hope this helps, and you think it is confusing, that's because it is!
    Swdfm

    PS. To add the ability shield line, do it the line before the for loop
    # return true if activeAbilityShield?(check_ability)
     
    Thank you so, so much! I'd thought it was a lost cause because I assumed the little warning that said "don't make an item that turns off abilities or else it will loop" also applied to new Abilities, but it's good to know it's possible! I'll try this out. You're a godsend!
     
    Okay, I tested it, and it worked perfectly! Thank you so much again for your help!
     
    Back
    Top