Vendily

SOS Battles

Aug 21st, 2020 (edited)
5,210
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #===============================================================================
  2. # SOS Battles - By Vendily [v17]
  3. #===============================================================================
  4. # This script adds in SOS Battles, the added mechanic from Alola, where
  5. #  Pokémon have a chance of calling for aid while in battle.
  6. # This version of the script does not implement special weather based encounters
  7. #  but does have a method that can be edited to do so.
  8. # The script also defines the manually used effect of the Adrenaline Orb item.
  9. #===============================================================================
  10. # To use it, you must add species that are able to SOS call in the SOS_CALL_RATES
  11. #  hash. The key is the species symbol, and the value is the rate in percentage
  12. #  the mon will call at.
  13. #   AKA. :BULBASAUR=>100 is a valid entry
  14. #
  15. # Optionally, you may add species to SOS_CALL_MONS, the species that
  16. #  the mon will call for. Add more entries of the same species to make them more
  17. #  likely. The Key is the species symbol, and the value is an array of species.
  18. #   AKA. :BULBASAUR=>[:BULBASAUR,:BULBASAUR,:BULBASAUR,:IVYSAUR,:IVYSAUR,:VENUSAUR]
  19. #   is a valid entry.
  20. # If you don't have an entry for a calling species, it will call another mon of
  21. #  the same species as itself.
  22. #
  23. # To implement the doubled EV points in an SOS battle, there is an additional
  24. #  edit to "def pbGainExpOne" in PokeBattle_Battle
  25. #  under:
  26. #      evgain*=2 if thispoke.pokerusStage>=1 # Infected or cured
  27. #  put:
  28. #      evgain*=2 if @sosbattle
  29. #
  30. # If you wish to implement special called allies, edit the array returned by
  31. #  pbSpecialSOSMons. By default, it just passes through the regular array used.
  32. # The method also takes the calling battler, if you wish to check its properties.
  33. #===============================================================================
  34. # * Hash containing base species call rates
  35. # * Hash containing species called allies
  36. # * Switch id to enable/disable SOS battles. Set to <1 to not check.
  37. #===============================================================================
  38.  
  39. SOS_CALL_RATES={}
  40. SOS_CALL_MONS={}
  41. NO_SOS_BATTLES = -1
  42.  
  43. class PokeBattle_Battle
  44.   attr_accessor :adrenalineorb
  45.   attr_accessor :lastturncalled
  46.   attr_accessor :lastturnanswered
  47.   attr_accessor :soschain
  48.   attr_accessor :sosbattle
  49.   attr_accessor :wasdoublebattle
  50.  
  51.   def soschain
  52.     return @soschain || 0
  53.   end
  54.  
  55.   def pbSpecialSOSMons(caller,mons)
  56.     return mons
  57.   end
  58.  
  59.  
  60.   def pbCallForHelp(index,caller)
  61.     cspecies=getConstantName(PBSpecies,caller.species).to_sym
  62.     rate=SOS_CALL_RATES[cspecies] || 0
  63.     return if rate==0 # should never trigger anyways but you never know.
  64.     pbDisplay(_INTL("{1} called for help!", caller.pbThis))
  65.     rate*=4 # base rate
  66.     rate=rate.to_f # don't want to lose decimal points
  67.     pbattler=@battlers[0]
  68.     if pbattler.hasWorkingAbility(:INTIMIDATE) ||
  69.        pbattler.hasWorkingAbility(:UNNERVE) ||
  70.        pbattler.hasWorkingAbility(:PRESSURE)
  71.       rate*=1.2
  72.     end
  73.     if @lastturncalled==@turncount-1
  74.       rate*=1.5
  75.     end
  76.     if !@lastturnanswered
  77.       rate*=3.0
  78.     end
  79.     rate=rate.round # rounding it off.
  80.     pbDisplayBrief(_INTL("... ... ..."))
  81.     if pbRandom(100)<rate
  82.       @lastturnanswered=true
  83.       @lastturncalled=@turncount
  84.       mons=SOS_CALL_MONS[cspecies] || [caller.species]
  85.       mons=pbSpecialSOSMons(caller,mons)
  86.       mon=mons[pbRandom(mons.length)]
  87.       alevel=caller.level-1
  88.       alevel=1 if alevel<1
  89.       ally=pbGenerateSOSPokemon(getID(PBSpecies,mon),alevel)
  90.       @battlers[index].pbInitialize(ally,(index>>1),false)
  91.       @wasdoublebattle=@doublebattle if @wasdoublebattle.nil?
  92.       @doublebattle=true if !@doublebattle
  93.       @sosbattle=true
  94.       @scene.pbSOSJoin(index,ally)
  95.       pbDisplayBrief(_INTL("{1} appeared!",@battlers[index].pbThis))
  96.       caller.pbPartner.effects[PBEffects::SkipTurn]=true
  97.       caller.effects[PBEffects::SkipTurn]=true
  98.       @party2.push(ally)
  99.     else
  100.       @lastturnanswered=false
  101.       pbDisplay(_INTL("Its help didn't appear!"))
  102.     end
  103.   end
  104.  
  105.   def pbGenerateSOSPokemon(species,level)
  106.     genwildpoke = PokeBattle_Pokemon.new(species,level,$Trainer)
  107.     items = genwildpoke.wildHoldItems
  108.     firstpoke = @battlers[0]
  109.     chances = [50,5,1]
  110.     chances = [60,20,5] if firstpoke.hasWorkingAbility(:COMPOUNDEYES)
  111.     itemrnd = rand(100)
  112.     if itemrnd<chances[0] || (items[0]==items[1] && items[1]==items[2])
  113.       genwildpoke.setItem(items[0])
  114.     elsif itemrnd<(chances[0]+chances[1])
  115.       genwildpoke.setItem(items[1])
  116.     elsif itemrnd<(chances[0]+chances[1]+chances[2])
  117.       genwildpoke.setItem(items[2])
  118.     end
  119.     if hasConst?(PBItems,:SHINYCHARM) && $PokemonBag.pbHasItem?(:SHINYCHARM)
  120.       for i in 0...2   # 3 times as likely
  121.         break if genwildpoke.isShiny?
  122.         genwildpoke.personalID = rand(65536)|(rand(65536)<<16)
  123.       end
  124.     end
  125.     chain=self.soschain
  126.     shinychain=(chain/10)
  127.     shinychain-=1 if chain%10==0
  128.     if shinychain>0
  129.       for i in 0...shinychain
  130.         break if genwildpoke.isShiny?
  131.         genwildpoke.personalID = rand(65536)|(rand(65536)<<16)
  132.       end
  133.     end
  134.     ivchain=(chain/10)
  135.     ivchain+=1 if chain>=5
  136.     ivs=(0..5).to_a
  137.     ivs.shuffle!
  138.     if ivchain>0
  139.       for i in 0...ivchain
  140.         break if ivs.length==0
  141.         iv=ivs.shift
  142.         genwildpoke.ivs[iv]=31
  143.       end
  144.     end
  145.     hachain=(chain/10)
  146.     if hachain>0
  147.       genwildpoke.setAbility(2) if pbRandom(100)<hachain*5
  148.     end
  149.     if rand(65536)<POKERUSCHANCE
  150.       genwildpoke.givePokerus
  151.     end
  152.     if firstpoke.hasWorkingAbility(:CUTECHARM) && !genwildpoke.isSingleGendered?
  153.       if firstpoke.gender==0
  154.         (rand(3)<2) ? genwildpoke.makeFemale : genwildpoke.makeMale
  155.       elsif firstpoke.gender==1
  156.         (rand(3)<2) ? genwildpoke.makeMale : genwildpoke.makeFemale
  157.       end
  158.     elsif firstpoke.hasWorkingAbility(:SYNCHRONIZE)
  159.       genwildpoke.setNature(firstpoke.nature) if rand(10)<5
  160.     end
  161.     Events.onWildPokemonCreate.trigger(nil,genwildpoke)
  162.     return genwildpoke
  163.   end
  164.  
  165.   def pbSwitch(favorDraws=false)
  166.     if !favorDraws
  167.       return if @decision>0
  168.     else
  169.       return if @decision==5
  170.     end
  171.     pbJudge()
  172.     return if @decision>0
  173.     firstbattlerhp=@battlers[0].hp
  174.     switched=[]
  175.     for index in 0...4
  176.       next if !@doublebattle && pbIsDoubleBattler?(index)
  177.       next if @battlers[index] && !@battlers[index].fainted?
  178.       next if !pbCanChooseNonActive?(index)
  179.       if !pbOwnedByPlayer?(index)
  180.         if !pbIsOpposing?(index) || (@opponent && pbIsOpposing?(index))
  181.           newenemy=pbSwitchInBetween(index,false,false)
  182.           newenemyname=newenemy
  183.           if newenemy>=0 && isConst?(pbParty(index)[newenemy].ability,PBAbilities,:ILLUSION)
  184.             newenemyname=pbGetLastPokeInTeam(index)
  185.           end
  186.           opponent=pbGetOwner(index)
  187.           if !@doublebattle && firstbattlerhp>0 && @shiftStyle && @opponent &&
  188.               @internalbattle && pbCanChooseNonActive?(0) && pbIsOpposing?(index) &&
  189.               @battlers[0].effects[PBEffects::Outrage]==0
  190.             pbDisplayPaused(_INTL("{1} is about to send in {2}.",opponent.fullname,pbParty(index)[newenemyname].name))
  191.             if pbDisplayConfirm(_INTL("Will {1} change Pokémon?",self.pbPlayer.name))
  192.               newpoke=pbSwitchPlayer(0,true,true)
  193.               if newpoke>=0
  194.                 newpokename=newpoke
  195.                 if isConst?(@party1[newpoke].ability,PBAbilities,:ILLUSION)
  196.                   newpokename=pbGetLastPokeInTeam(0)
  197.                 end
  198.                 pbDisplayBrief(_INTL("{1}, that's enough! Come back!",@battlers[0].name))
  199.                 pbRecallAndReplace(0,newpoke,newpokename)
  200.                 switched.push(0)
  201.               end
  202.             end
  203.           end
  204.           pbRecallAndReplace(index,newenemy,newenemyname,false,false)
  205.           switched.push(index)
  206.         end
  207.       elsif @opponent
  208.         newpoke=pbSwitchInBetween(index,true,false)
  209.         newpokename=newpoke
  210.         if isConst?(@party1[newpoke].ability,PBAbilities,:ILLUSION)
  211.           newpokename=pbGetLastPokeInTeam(index)
  212.         end
  213.         pbRecallAndReplace(index,newpoke,newpokename)
  214.         switched.push(index)
  215.       else
  216.         next if @sosbattle && !@wasdoublebattle
  217.         switch=false
  218.         if !pbDisplayConfirm(_INTL("Use next Pokémon?"))
  219.           switch=(pbRun(index,true)<=0)
  220.         else
  221.           switch=true
  222.         end
  223.         if switch
  224.           newpoke=pbSwitchInBetween(index,true,false)
  225.           newpokename=newpoke
  226.           if isConst?(@party1[newpoke].ability,PBAbilities,:ILLUSION)
  227.             newpokename=pbGetLastPokeInTeam(index)
  228.           end
  229.           pbRecallAndReplace(index,newpoke,newpokename)
  230.           switched.push(index)
  231.         end
  232.       end
  233.     end
  234.     if switched.length>0
  235.       priority=pbPriority
  236.       for i in priority
  237.         i.pbAbilitiesOnSwitchIn(true) if switched.include?(i.index)
  238.       end
  239.     end
  240.   end
  241.  
  242. end
  243.  
  244. class PokeBattle_Battler
  245.   def pbCanCall?
  246.     return false if NO_SOS_BATTLES>0 &&  $game_switches[NO_SOS_BATTLES]
  247.     # only wild battles
  248.     return false if @opponent
  249.     # only wild mons
  250.     return false if !@battle.pbIsOpposing?(self.index)
  251.     # can't call if partner already in
  252.     return false if !self.pbPartner.fainted?
  253.     # just to be safe
  254.     return false if self.fainted?
  255.     # no call if status
  256.     return false if self.status!=0
  257.     # no call if multiturn attack
  258.     return false if self.effects[PBEffects::TwoTurnAttack]>0
  259.     return false if self.effects[PBEffects::HyperBeam]>0
  260.     return false if self.effects[PBEffects::Rollout]>0
  261.     return false if self.effects[PBEffects::Outrage]>0
  262.     return false if self.effects[PBEffects::Uproar]>0
  263.     return false if self.effects[PBEffects::Bide]>0
  264.     species=getConstantName(PBSpecies,self.species).to_sym
  265.     rate=SOS_CALL_RATES[species] || 0
  266.     # not a species that calls
  267.     return false if rate==0
  268.     rate*=3 if self.hp>(self.totalhp/4) && self.hp<=(self.totalhp/2)
  269.     rate*=5 if self.hp<=(self.totalhp/4)
  270.     rate*=2 if @battle.adrenalineorb
  271.     return @battle.pbRandom(100)<rate
  272.   end
  273.  
  274.   def pbProcessTurn(choice)
  275.     # Can't use a move if fainted
  276.     return false if fainted?
  277.     # Wild roaming Pokémon always flee if possible
  278.     if !@battle.opponent && @battle.pbIsOpposing?(self.index) &&
  279.        @battle.rules["alwaysflee"] && @battle.pbCanRun?(self.index)
  280.       pbBeginTurn(choice)
  281.       @battle.pbDisplay(_INTL("{1} fled!",self.pbThis))
  282.       @battle.decision=3
  283.       pbEndTurn(choice)
  284.       PBDebug.log("[Escape] #{pbThis} fled")
  285.       return true
  286.     end
  287.     if pbCanCall?
  288.       pbCancelMoves
  289.       @battle.pbCallForHelp(self.pbPartner.index,self)
  290.       pbEndTurn(choice)
  291.       return true
  292.     end
  293.     # If this battler's action for this round wasn't "use a move"
  294.     if choice[0]!=1
  295.       # Clean up effects that end at battler's turn
  296.       pbBeginTurn(choice)
  297.       pbEndTurn(choice)
  298.       return false
  299.     end
  300.     # Turn is skipped if Pursuit was used during switch
  301.     if @effects[PBEffects::Pursuit]
  302.       @effects[PBEffects::Pursuit]=false
  303.       pbCancelMoves
  304.       pbEndTurn(choice)
  305.       @battle.pbJudge #      @battle.pbSwitch
  306.       return false
  307.     end
  308.     # Use the move
  309. #   @battle.pbDisplayPaused("Before: [#{@lastMoveUsedSketch},#{@lastMoveUsed}]")
  310.     PBDebug.log("#{pbThis} used #{choice[2].name}")
  311.     PBDebug.logonerr{
  312.        pbUseMove(choice,choice[2]==@battle.struggle)
  313.     }
  314. #   @battle.pbDisplayPaused("After: [#{@lastMoveUsedSketch},#{@lastMoveUsed}]")
  315.     return true
  316.   end
  317. end
  318.  
  319. class PokeBattle_Scene
  320.   def pbSelectBattler(index,selectmode=1)
  321.     numwindows=@battle.doublebattle ? 4 : 2
  322.     for i in 0...numwindows
  323.       next if @battle.sosbattle && !@battle.wasdoublebattle && i == 2
  324.       sprite=@sprites["battlebox#{i}"]
  325.       sprite.selected=(i==index) ? selectmode : 0
  326.       sprite=@sprites["pokemon#{i}"]
  327.       sprite.selected=(i==index) ? selectmode : 0
  328.     end
  329.   end
  330.  
  331.   def pbUpdateSelected(index)
  332.     numwindows=@battle.doublebattle ? 4 : 2
  333.     for i in 0...numwindows
  334.       next if @battle.sosbattle && !@battle.wasdoublebattle && (i == 2)
  335.       if i==index
  336.         @sprites["battlebox#{i}"].selected=2
  337.         @sprites["pokemon#{i}"].selected=2
  338.       else
  339.         @sprites["battlebox#{i}"].selected=0
  340.         @sprites["pokemon#{i}"].selected=0
  341.       end
  342.     end
  343.     pbFrameUpdate
  344.   end
  345.  
  346.   def pbSOSJoin(battlerindex,pkmn)
  347.     @briefmessage=false
  348.     frame=0
  349.     if !@sprites["pokemon#{battlerindex}"]
  350.       @sprites["pokemon#{battlerindex}"]=PokemonBattlerSprite.new(@battle.doublebattle,battlerindex,@viewport)
  351.       @sprites["pokemon#{battlerindex}"].z=(battlerindex==3)? 11 : 26
  352.       if battlerindex&1==1
  353.         pbAddSprite("shadow#{battlerindex}",0,0,"Graphics/Pictures/Battle/object_shadow",@viewport)
  354.       else
  355.         @sprites["shadow#{battlerindex}"]=IconSprite.new(0,0,@viewport)
  356.       end
  357.       @sprites["shadow#{battlerindex}"].z=3
  358.       @sprites["shadow#{battlerindex}"].visible=false
  359.     end
  360.     @sprites["pokemon#{battlerindex}"].setPokemonBitmap(pkmn,false)
  361.     sendout=SOSJoinAnimation.new(@sprites["pokemon#{battlerindex}"],
  362.        @sprites,@battle.battlers[battlerindex],@battle.doublebattle,@battle)
  363.     partnerindex=-1
  364.     if !@sprites["battlebox#{battlerindex}"]
  365.       partnerindex=(battlerindex&1)|((battlerindex&2)^2)
  366.       @sprites["battlebox#{battlerindex}"]=PokemonDataBox.new(@battle.battlers[battlerindex],@battle.doublebattle,@viewport)
  367.       @sprites["battlebox#{partnerindex}"].dispose
  368.       @sprites["battlebox#{partnerindex}"]=PokemonDataBox.new(@battle.battlers[partnerindex],@battle.doublebattle,@viewport)
  369.     end
  370.     loop do
  371.       frame+=1    
  372.       if frame==1
  373.         @sprites["battlebox#{battlerindex}"].appear
  374.         if partnerindex>=0
  375.           @sprites["battlebox#{partnerindex}"].appear
  376.         end
  377.       end
  378.       if frame>=6
  379.         sendout.update
  380.       end
  381.       pbGraphicsUpdate
  382.       pbInputUpdate
  383.       pbFrameUpdate
  384.       break if sendout.animdone? &&
  385.          !@sprites["battlebox#{battlerindex}"].appearing
  386.     end
  387.     if @battle.battlers[battlerindex].isShiny? && @battle.battlescene
  388.       pbCommonAnimation("Shiny",@battle.battlers[battlerindex],nil)
  389.     end
  390.     sendout.dispose
  391.     pbRefresh
  392.   end
  393. end
  394.  
  395. class SOSJoinAnimation
  396.   def initialize(sprite,spritehash,pkmn,doublebattle,battle)
  397.     @disposed=false
  398.     @PokemonBattlerSprite=sprite
  399.     @PokemonBattlerSprite.visible=false
  400.     @PokemonBattlerSprite.src_rect.height=@PokemonBattlerSprite.bitmap.height
  401.     @PokemonBattlerSprite.oy=0
  402.     @PokemonBattlerSprite.tone=Tone.new(0,0,0,248)
  403.     if doublebattle
  404.       @spritex=PokeBattle_SceneConstants::FOEBATTLERD1_X if pkmn.index==1
  405.       @spritex=PokeBattle_SceneConstants::FOEBATTLERD2_X if pkmn.index==3
  406.     else
  407.       @spritex=PokeBattle_SceneConstants::FOEBATTLER_X
  408.     end
  409.     @spritey=adjustBattleSpriteY(sprite,pkmn.species,pkmn.index)
  410.     if doublebattle
  411.       @spritey+=PokeBattle_SceneConstants::FOEBATTLERD1_Y if pkmn.index==1
  412.       @spritey+=PokeBattle_SceneConstants::FOEBATTLERD2_Y if pkmn.index==3
  413.     else
  414.       @spritey+=PokeBattle_SceneConstants::FOEBATTLER_Y
  415.     end
  416.    
  417.     @spritehash=spritehash
  418.     @pkmn=pkmn
  419.     @shadowX=@spritex
  420.     @shadowY=@spritey/2
  421.     if @spritehash["shadow#{@pkmn.index}"] && @spritehash["shadow#{@pkmn.index}"].bitmap!=nil
  422.       @shadowX-=@spritehash["shadow#{@pkmn.index}"].bitmap.width/2
  423.       @shadowY-=@spritehash["shadow#{@pkmn.index}"].bitmap.height/2
  424.     end
  425.     if @spritehash["pokemon#{@pkmn.pbPartner.index}"]
  426.       pindex=@pkmn.pbPartner.index
  427.       pmon=battle.battlers[pindex]
  428.       case pindex
  429.       when 1
  430.         @spritehash["pokemon#{pindex}"].x=PokeBattle_SceneConstants::FOEBATTLERD1_X
  431.         @spritehash["pokemon#{pindex}"].y=PokeBattle_SceneConstants::FOEBATTLERD1_Y
  432.       when 3
  433.         @spritehash["pokemon#{pindex}"].x=PokeBattle_SceneConstants::FOEBATTLERD2_X
  434.         @spritehash["pokemon#{pindex}"].y=PokeBattle_SceneConstants::FOEBATTLERD2_Y
  435.       end
  436.       @spritehash["pokemon#{pindex}"].x-=@spritehash["pokemon#{pindex}"].bitmap.width/2
  437.       @spritehash["pokemon#{pindex}"].y+=adjustBattleSpriteY(@spritehash["pokemon#{pindex}"],pmon.species,pmon.index)
  438.       if @spritehash["shadow#{@pkmn.index}"] && @spritehash["shadow#{@pkmn.index}"].bitmap!=nil
  439.         @spritehash["shadow#{@pkmn.index}"].x=@spritehash["pokemon#{pindex}"].x
  440.         @spritehash["shadow#{@pkmn.index}"].x-=@spritehash["shadow#{@pkmn.index}"].bitmap.width/2
  441.         @spritehash["shadow#{@pkmn.index}"].y=@spritehash["pokemon#{pindex}"].y/2
  442.         @spritehash["shadow#{@pkmn.index}"].y-=@spritehash["shadow#{@pkmn.index}"].bitmap.height/2
  443.       end
  444.     end
  445.     @shadowVisible=showShadow?(pkmn.species)
  446.     @animdone=false
  447.     @frame=0
  448.   end
  449.  
  450.   def disposed?
  451.     return @disposed
  452.   end
  453.  
  454.   def animdone?
  455.     return @animdone
  456.   end
  457.  
  458.   def dispose
  459.     return if disposed?
  460.     @disposed=true
  461.   end
  462.  
  463.   def update
  464.     return if disposed?
  465.     @frame+=1
  466.     if @frame==4
  467.       if @spritehash["shadow#{@pkmn.index}"]
  468.         @spritehash["shadow#{@pkmn.index}"].x=@shadowX
  469.         @spritehash["shadow#{@pkmn.index}"].y=@shadowY
  470.         @spritehash["shadow#{@pkmn.index}"].visible=@shadowVisible
  471.       end
  472.       @PokemonBattlerSprite.visible=true
  473.       pbSpriteSetCenter(@PokemonBattlerSprite,@spritex,@spritey)
  474.       @PokemonBattlerSprite.y=@spritey
  475.       if @pkmn.pokemon
  476.         pbPlayCry(@pkmn.pokemon)
  477.       else
  478.         pbPlayCrySpecies(@pkmn.species,@pkmn.form)
  479.       end
  480.     end
  481.     if @frame>8 && @frame<=24
  482.       tone=(16-@frame)*32
  483.       @PokemonBattlerSprite.tone=Tone.new(0,0,0,tone)
  484.     end
  485.     if @PokemonBattlerSprite.tone.gray<=0
  486.       @animdone=true
  487.     end
  488.   end
  489. end
  490.  
  491. ItemHandlers::BattleUseOnBattler.add(:ADRENALINEORB,proc{|item,battler,scene|
  492.    battle=battler.battle
  493.    if battle.adrenalineorb
  494.      scene.pbDisplay(_INTL("Already used that."))
  495.      return false
  496.    end
  497.    playername=battle.pbPlayer.name
  498.    scene.pbDisplay(_INTL("{1} used the {2}.",playername,PBItems.getName(item)))
  499.    return true
  500. })
  501.  
  502. ItemHandlers::UseInBattle.add(:ADRENALINEORB,proc{|item,battler,battle|
  503.    battle.adrenalineorb=true
  504.    battle.pbDisplayPaused(_INTL("The {1} makes the wild Pokémon nervous!",PBItems.getName(item)))
  505. })
RAW Paste Data