Vendily

SOS Battles v18

Sep 3rd, 2020 (edited)
519
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #===============================================================================
  2. # SOS Battles - By Vendily [v18]
  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 aditional
  24. #  edit to "def pbGainEVsOne" in PokeBattle_Battle
  25. #  under:
  26. #    # Double EV gain because of Pokérus
  27. #    if pkmn.pokerusStage>=1   # Infected or cured
  28. #      evYield.collect! { |a| a*2 }
  29. #    end
  30. #  put:
  31. #    if @sosbattle
  32. #      evYield.collect! { |a| a*2 }
  33. #    end
  34. #
  35. # If you wish to implement special called allies, edit the array returned by
  36. #  pbSpecialSOSMons. By default, it just passes through the regular array used.
  37. # The method also takes the calling battler, if you wish to check its properties.
  38. #===============================================================================
  39. # * Hash containing base species call rates
  40. # * Hash containing species called allies
  41. # * Switch id to enable/disable SOS battles. Set to <1 to not check.
  42. #===============================================================================
  43. begin
  44. PluginManager.register({
  45.   :name    => "SOS Battles",
  46.   :version => "1.1",
  47.   :link    => "https://reliccastle.com/resources/444/",
  48.   :credits => "Vendily"
  49. })
  50. rescue
  51.   raise "This script only funtions in v18."
  52. end
  53. SOS_CALL_RATES={:BULBASAUR=>100}
  54. SOS_CALL_MONS={}
  55. NO_SOS_BATTLES = -1
  56.  
  57. class PokeBattle_Battle
  58.   attr_accessor :adrenalineorb
  59.   attr_accessor :lastturncalled
  60.   attr_accessor :lastturnanswered
  61.   attr_accessor :soschain
  62.   attr_accessor :sosbattle
  63.  
  64.   def soschain
  65.     return @soschain || 0
  66.   end
  67.  
  68.   def pbSpecialSOSMons(caller,mons)
  69.     return mons
  70.   end
  71.  
  72.  
  73.   def pbCallForHelp(caller)
  74.     cspecies=getConstantName(PBSpecies,caller.species).to_sym
  75.     rate=SOS_CALL_RATES[cspecies] || 0
  76.     return if rate==0 # should never trigger anyways but you never know.
  77.     pbDisplay(_INTL("{1} called for help!", caller.pbThis))
  78.     rate*=4 # base rate
  79.     rate=rate.to_f # don't want to lose decimal points
  80.     intimidate=false
  81.     caller.eachOpposing{ |b|
  82.       if b.hasWorkingAbility(:INTIMIDATE) ||
  83.          b.hasWorkingAbility(:UNNERVE) ||
  84.          b.hasWorkingAbility(:PRESSURE)
  85.         intimidate=true
  86.         break
  87.       end
  88.     }
  89.     rate*=1.2 if intimidate
  90.     if @lastturncalled==@turnCount-1
  91.       rate*=1.5
  92.     end
  93.     if !@lastturnanswered
  94.       rate*=3.0
  95.     end
  96.     rate=rate.round # rounding it off.
  97.     pbDisplayPaused(_INTL("... ... ..."))
  98.     idxOther = -1
  99.     case pbSideSize(caller.index)
  100.     when 1
  101.       idxOther=3
  102.       # change battle size
  103.       @sideSizes[1]=2
  104.     when 2
  105.       idxOther = (caller.index+2)%4
  106.     end
  107.     if idxOther>=0 && pbRandom(100)<rate
  108.       @lastturnanswered=true
  109.       mons=SOS_CALL_MONS[cspecies] || [caller.species]
  110.       mons=pbSpecialSOSMons(caller,mons)
  111.       mon=mons[pbRandom(mons.length)]
  112.       alevel=caller.level-1
  113.       alevel=1 if alevel<1
  114.       ally=pbGenerateSOSPokemon(getID(PBSpecies,mon),alevel)
  115.       if @battlers[idxOther].nil?
  116.         pbCreateBattler(idxOther,ally,@party2.length)
  117.       else
  118.         @battlers[idxOther].pbInitialize(ally,@party2.length)
  119.       end
  120.       @scene.pbSOSJoin(idxOther,ally)
  121.       pbDisplay(_INTL("{1} appeared!",@battlers[idxOther].pbThis))
  122.       # prevent cheap shot
  123.       @battlers[idxOther].lastRoundMoved=@turnCount
  124.       # required to gain exp and to do "switch in" effects, like Spikes
  125.       pbOnActiveOne(@battlers[idxOther])
  126.       @party2.push(ally)
  127.       @party2order.push(@party2order.length)
  128.     else
  129.       @lastturnanswered=false
  130.       pbDisplay(_INTL("Its help didn't appear!"))
  131.     end
  132.     @lastturncalled=@turnCount
  133.   end
  134.  
  135.   def pbGenerateSOSPokemon(species,level)
  136.     genwildpoke = PokeBattle_Pokemon.new(species,level,$Trainer)
  137.     items = genwildpoke.wildHoldItems
  138.     firstpoke = @battlers[0]
  139.     chances = [50,5,1]
  140.     chances = [60,20,5] if firstpoke.hasWorkingAbility(:COMPOUNDEYES)
  141.     itemrnd = rand(100)
  142.     if itemrnd<chances[0] || (items[0]==items[1] && items[1]==items[2])
  143.       genwildpoke.setItem(items[0])
  144.     elsif itemrnd<(chances[0]+chances[1])
  145.       genwildpoke.setItem(items[1])
  146.     elsif itemrnd<(chances[0]+chances[1]+chances[2])
  147.       genwildpoke.setItem(items[2])
  148.     end
  149.     if hasConst?(PBItems,:SHINYCHARM) && $PokemonBag.pbHasItem?(:SHINYCHARM)
  150.       for i in 0...2   # 3 times as likely
  151.         break if genwildpoke.isShiny?
  152.         genwildpoke.personalID = rand(65536)|(rand(65536)<<16)
  153.       end
  154.     end
  155.     chain=self.soschain
  156.     shinychain=(chain/10)
  157.     shinychain-=1 if chain%10==0
  158.     if shinychain>0
  159.       for i in 0...shinychain
  160.         break if genwildpoke.isShiny?
  161.         genwildpoke.personalID = rand(65536)|(rand(65536)<<16)
  162.       end
  163.     end
  164.     ivchain=(chain/10)
  165.     ivchain+=1 if chain>=5
  166.     ivs=(0..5).to_a
  167.     ivs.shuffle!
  168.     if ivchain>0
  169.       for i in 0...ivchain
  170.         break if ivs.length==0
  171.         iv=ivs.shift
  172.         genwildpoke.ivs[iv]=31
  173.       end
  174.     end
  175.     hachain=(chain/10)
  176.     if hachain>0
  177.       genwildpoke.setAbility(2) if pbRandom(100)<hachain*5
  178.     end
  179.     if rand(65536)<POKERUS_CHANCE
  180.       genwildpoke.givePokerus
  181.     end
  182.     if firstpoke.hasWorkingAbility(:CUTECHARM) && !genwildpoke.isSingleGendered?
  183.       if firstpoke.gender==0
  184.         (rand(3)<2) ? genwildpoke.makeFemale : genwildpoke.makeMale
  185.       elsif firstpoke.gender==1
  186.         (rand(3)<2) ? genwildpoke.makeMale : genwildpoke.makeFemale
  187.       end
  188.     elsif firstpoke.hasWorkingAbility(:SYNCHRONIZE)
  189.       genwildpoke.setNature(firstpoke.nature) if rand(10)<5
  190.     end
  191.     Events.onWildPokemonCreate.trigger(nil,genwildpoke)
  192.     return genwildpoke
  193.   end
  194.  
  195. end
  196.  
  197. class PokeBattle_Battler
  198.   def pbCanCall?
  199.     return false if NO_SOS_BATTLES>0 &&  $game_switches[NO_SOS_BATTLES]
  200.     # only wild battles
  201.     return false if @battle.trainerBattle?
  202.     # only wild mons
  203.     return false if !opposes?
  204.     # can't call in triple+ battles (don't want to figure out where the battler needs to be)
  205.     return false if @battle.pbSideSize(@index)>=3
  206.     # can't call if partner already in
  207.     allies=@battle.battlers.select {|b| b && !b.fainted? && !b.opposes?(@index) && b.index!=@index}
  208.     return false if allies.length>0
  209.     # just to be safe
  210.     return false if self.fainted?
  211.     # no call if status
  212.     return false if self.status!=0
  213.     # no call if multiturn attack
  214.     return false if usingMultiTurnAttack?
  215.     species=getConstantName(PBSpecies,self.species).to_sym
  216.     rate=SOS_CALL_RATES[species] || 0
  217.     # not a species that calls
  218.     return false if rate==0
  219.     rate*=3 if self.hp>(self.totalhp/4) && self.hp<=(self.totalhp/2)
  220.     rate*=5 if self.hp<=(self.totalhp/4)
  221.     rate*=2 if @battle.adrenalineorb
  222.     return @battle.pbRandom(100)<rate
  223.   end
  224.  
  225.   def pbProcessTurn(choice,tryFlee=true)
  226.     return false if fainted?
  227.     # Wild roaming Pokémon always flee if possible
  228.     if tryFlee && @battle.wildBattle? && opposes? &&
  229.        @battle.rules["alwaysflee"] && @battle.pbCanRun?(@index)
  230.       pbBeginTurn(choice)
  231.       @battle.pbDisplay(_INTL("{1} fled from battle!",pbThis)) { pbSEPlay("Battle flee") }
  232.       @battle.decision = 3
  233.       pbEndTurn(choice)
  234.       return true
  235.     end
  236.     # Shift with the battler next to this one
  237.     if choice[0]==:Shift
  238.       idxOther = -1
  239.       case @battle.pbSideSize(@index)
  240.       when 2
  241.         idxOther = (@index+2)%4
  242.       when 3
  243.         if @index!=2 && @index!=3   # If not in middle spot already
  244.           idxOther = ((@index%2)==0) ? 2 : 3
  245.         end
  246.       end
  247.       if idxOther>=0
  248.         @battle.pbSwapBattlers(@index,idxOther)
  249.         case @battle.pbSideSize(@index)
  250.         when 2
  251.           @battle.pbDisplay(_INTL("{1} moved across!",pbThis))
  252.         when 3
  253.           @battle.pbDisplay(_INTL("{1} moved to the center!",pbThis))
  254.         end
  255.       end
  256.       pbBeginTurn(choice)
  257.       pbCancelMoves
  258.       @lastRoundMoved = @battle.turnCount   # Done something this round
  259.       return true
  260.     end
  261.     if pbCanCall?
  262.       pbCancelMoves
  263.       @battle.pbCallForHelp(self)
  264.       @lastRoundMoved = @battle.turnCount
  265.       pbEndTurn(choice)
  266.       return true
  267.     end
  268.     # If this battler's action for this round wasn't "use a move"
  269.     if choice[0]!=:UseMove
  270.       # Clean up effects that end at battler's turn
  271.       pbBeginTurn(choice)
  272.       pbEndTurn(choice)
  273.       return false
  274.     end
  275.     # Turn is skipped if Pursuit was used during switch
  276.     if @effects[PBEffects::Pursuit]
  277.       @effects[PBEffects::Pursuit] = false
  278.       pbCancelMoves
  279.       pbEndTurn(choice)
  280.       @battle.pbJudge
  281.       return false
  282.     end
  283.     # Use the move
  284.     PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}")
  285.     PBDebug.logonerr{
  286.       pbUseMove(choice,choice[2]==@battle.struggle)
  287.     }
  288.     @battle.pbJudge
  289.     # Update priority order
  290. #    @battle.pbCalculatePriority if NEWEST_BATTLE_MECHANICS
  291.     return true
  292.   end
  293. end
  294.  
  295. class PokeBattle_Scene
  296.   def pbSOSJoin(battlerindex,pkmn)
  297.     pbRefresh
  298.     sendOutAnims=[]
  299.     adjustAnims=[]
  300.     setupbox=false
  301.     if !@sprites["dataBox_#{battlerindex}"]
  302.       @sprites["dataBox_#{battlerindex}"] = PokemonDataBox.new(@battle.battlers[battlerindex],
  303.           @battle.pbSideSize(battlerindex),@viewport)
  304.       setupbox=true
  305.       @sprites["targetWindow"].dispose
  306.       @sprites["targetWindow"] = TargetMenuDisplay.new(@viewport,200,@battle.sideSizes)
  307.       @sprites["targetWindow"].visible=false
  308.       pbCreatePokemonSprite(battlerindex)
  309.       @battle.battlers[battlerindex].eachAlly{|b|
  310.         adjustAnims.push([DataBoxDisappearAnimation.new(@sprites,@viewport,b.index),b])
  311.       }
  312.     end
  313.     pkmn = @battle.battlers[battlerindex].effects[PBEffects::Illusion] || pkmn
  314.     pbChangePokemon(battlerindex,pkmn)
  315.     sendOutAnim = SOSJoinAnimation.new(@sprites,@viewport,
  316.         @battle.pbGetOwnerIndexFromBattlerIndex(battlerindex)+1,
  317.         @battle.battlers[battlerindex])
  318.     dataBoxAnim = DataBoxAppearAnimation.new(@sprites,@viewport,battlerindex)
  319.     sendOutAnims.push([sendOutAnim,dataBoxAnim,false])
  320.     # Play all animations
  321.     loop do
  322.       adjustAnims.each do |a|
  323.         next if a[0].animDone?
  324.         a[0].update
  325.       end
  326.       pbUpdate
  327.       break if !adjustAnims.any? {|a| !a[0].animDone?}
  328.     end
  329.     # delete and remake sprites
  330.     adjustAnims.each {|a|
  331.       @sprites["dataBox_#{a[1].index}"].dispose
  332.       @sprites["dataBox_#{a[1].index}"] = PokemonDataBox.new(a[1],
  333.           @battle.pbSideSize(a[1].index),@viewport)
  334.     }
  335.     # have to remake here, because I have to destroy and remake the databox
  336.     # and that breaks the reference link.
  337.     if setupbox
  338.       @battle.battlers[battlerindex].eachAlly{|b|
  339.         sendanim=SOSAdjustAnimation.new(@sprites,@viewport,
  340.           @battle.pbGetOwnerIndexFromBattlerIndex(b.index)+1,b)
  341.         dataanim=DataBoxAppearAnimation.new(@sprites,@viewport,b.index)
  342.         sendOutAnims.push([sendanim,dataanim,false,b])
  343.       }
  344.     end
  345.     loop do
  346.       sendOutAnims.each do |a|
  347.         next if a[2]
  348.         a[0].update
  349.         a[1].update if a[0].animDone?
  350.         a[2] = true if a[1].animDone?
  351.       end
  352.       pbUpdate
  353.       break if !sendOutAnims.any? { |a| !a[2] }
  354.     end
  355.     adjustAnims.each {|a| a[0].dispose}
  356.     sendOutAnims.each { |a| a[0].dispose; a[1].dispose }
  357.     # Play shininess animations for shiny Pokémon
  358.     if @battle.showAnims && @battle.battlers[battlerindex].shiny?
  359.       pbCommonAnimation("Shiny",@battle.battlers[battlerindex])
  360.     end
  361.   end
  362. end
  363.  
  364. class SOSJoinAnimation < PokeBattle_Animation
  365.   include PokeBattle_BallAnimationMixin
  366.  
  367.   def initialize(sprites,viewport,idxTrainer,battler)
  368.     @idxTrainer     = idxTrainer
  369.     @battler        = battler
  370.     sprites["pokemon_#{battler.index}"].visible = false
  371.     @shadowVisible = sprites["shadow_#{battler.index}"].visible
  372.     sprites["shadow_#{battler.index}"].visible = false
  373.     super(sprites,viewport)
  374.   end
  375.  
  376.   def createProcesses
  377.     batSprite = @sprites["pokemon_#{@battler.index}"]
  378.     shaSprite = @sprites["shadow_#{@battler.index}"]
  379.     # Calculate the Poké Ball graphic to use
  380.     # Calculate the color to turn the battler sprite
  381.     col = Tone.new(0,0,0,248)
  382.     # Calculate start and end coordinates for battler sprite movement
  383.     batSprite.src_rect.height=batSprite.bitmap.height
  384.     battlerX = batSprite.x
  385.     battlerY = batSprite.y
  386.     delay = 0
  387.     # Set up battler sprite
  388.     battler = addSprite(batSprite,PictureOrigin::Bottom)
  389.     battler.setXY(0,battlerX,battlerY)
  390.     battler.setTone(0,col)
  391.     # Battler animation
  392.     battler.setVisible(delay,true)
  393.     battler.setOpacity(delay,255)
  394.     # NOTE: As soon as the battler sprite finishes zooming, and just as it
  395.     #       starts changing its tone to normal, it plays its intro animation.
  396.     col.gray = 0
  397.     battler.moveTone(delay+5,10,col,[batSprite,:pbPlayIntroAnimation])
  398.     if @shadowVisible
  399.       # Set up shadow sprite
  400.       shadow = addSprite(shaSprite,PictureOrigin::Center)
  401.       shadow.setOpacity(0,0)
  402.       # Shadow animation
  403.       shadow.setVisible(delay,@shadowVisible)
  404.       shadow.moveOpacity(delay+5,10,255)
  405.     end
  406.   end
  407. end
  408.  
  409. class PokemonBattlerSprite
  410.   attr_accessor   :sideSize
  411. end
  412. class PokemonBattlerShadowSprite
  413.   attr_accessor   :sideSize
  414. end
  415.  
  416. class SOSAdjustAnimation < PokeBattle_Animation
  417.   include PokeBattle_BallAnimationMixin
  418.  
  419.   def initialize(sprites,viewport,idxTrainer,battler)
  420.     @idxTrainer     = idxTrainer
  421.     @battler        = battler
  422.     @shadowVisible = sprites["shadow_#{battler.index}"].visible
  423.     super(sprites,viewport)
  424.   end
  425.  
  426.   def createProcesses
  427.     batSprite = @sprites["pokemon_#{@battler.index}"]
  428.     shaSprite = @sprites["shadow_#{@battler.index}"]
  429.     batSprite.sideSize=@battler.battle.pbSideSize(@battler.index)
  430.     shaSprite.sideSize=@battler.battle.pbSideSize(@battler.index)
  431.     batSprite.pbSetPosition
  432.     shaSprite.pbSetPosition if @shadowVisible
  433.     battler = addSprite(batSprite,PictureOrigin::Bottom)
  434.   end
  435. end
  436.  
  437. ItemHandlers::CanUseInBattle.add(:ADRENALINEORB,proc { |item,pokemon,battler,move,firstAction,battle,scene,showMessages|
  438.   if battle.adrenalineorb
  439.     scene.pbDisplay(_INTL("It won't have any effect.")) if showMessages
  440.     next false
  441.   end
  442.   next true
  443. })
  444.  
  445. ItemHandlers::UseInBattle.add(:ADRENALINEORB,proc { |item,battler,battle|
  446.   battle.adrenalineorb=true
  447.   battle.pbDisplayPaused(_INTL("The {1} makes the wild Pokémon nervous!",PBItems.getName(item)))
  448. })
RAW Paste Data