#=============================================================================== # Data box for regular battles #=============================================================================== class PokemonDataBox < SpriteWrapper attr_reader :battler attr_accessor :selected attr_reader :animatingHP attr_reader :animatingExp # Time in seconds to fully fill the Exp bar (from empty). EXP_BAR_FILL_TIME = 1.75 # Maximum time in seconds to make a change to the HP bar. HP_BAR_CHANGE_TIME = 1.0 STATUS_ICON_HEIGHT = 16 # NAME_BASE_COLOR = Color.new(72,72,72) # NAME_SHADOW_COLOR = Color.new(184,184,184) # MALE_BASE_COLOR = Color.new(48,96,216) # MALE_SHADOW_COLOR = NAME_SHADOW_COLOR # FEMALE_BASE_COLOR = Color.new(248,88,40) # FEMALE_SHADOW_COLOR = NAME_SHADOW_COLOR NAME_BASE_COLOR = Color.new(255,255,255) NAME_SHADOW_COLOR = Color.new(32,32,32) MALE_BASE_COLOR = Color.new(48,96,216) MALE_SHADOW_COLOR = NAME_SHADOW_COLOR FEMALE_BASE_COLOR = Color.new(248,88,40) FEMALE_SHADOW_COLOR = NAME_SHADOW_COLOR def initialize(battler,sideSize,viewport=nil) super(viewport) @battler = battler @sprites = {} @spriteX = 0 @spriteY = 0 @spriteBaseX = 0 @selected = 0 @frame = 0 @showHP = false # Specifically, show the HP numbers @animatingHP = false @showExp = false # Specifically, show the Exp bar @animatingExp = false @expFlash = 0 initializeDataBoxGraphic(sideSize) initializeOtherGraphics(viewport) refresh end def initializeDataBoxGraphic(sideSize) onPlayerSide = ((@battler.index%2)==0) # Get the data box graphic and set whether the HP numbers/Exp bar are shown if sideSize==1 # One Pokémon on side, use the regular dara box BG bgFilename = ["Graphics/Pictures/Battle/databox_normal", "Graphics/Pictures/Battle/databox_normal_foe"][@battler.index%2] if onPlayerSide @showHP = true @showExp = true end else # Multiple Pokémon on side, use the thin dara box BG bgFilename = ["Graphics/Pictures/Battle/databox_thin", "Graphics/Pictures/Battle/databox_thin_foe"][@battler.index%2] end @databoxBitmap = AnimatedBitmap.new(bgFilename) # Determine the co-ordinates of the data box and the left edge padding width if onPlayerSide @spriteX = Graphics.width - 244 @spriteY = Graphics.height - 192 @spriteBaseX = 34 else @spriteX = 8 #-16 @spriteY = 36 @spriteBaseX = 8 #16 end case sideSize when 2 @spriteX += [-12, 12, 0, 0][@battler.index] @spriteY += [-20, -34, 34, 20][@battler.index] when 3 @spriteX += [-12, 12, -6, 6, 0, 0][@battler.index] @spriteY += [-42, -46, 4, 0, 50, 46][@battler.index] end end def initializeOtherGraphics(viewport) # Create other bitmaps @numbersBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/icon_numbers")) @hpBarBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/overlay_hp")) @expBarBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/overlay_exp")) # Create sprite to draw HP numbers on @hpNumbers = BitmapSprite.new(124,16,viewport) pbSetSmallFont(@hpNumbers.bitmap) @sprites["hpNumbers"] = @hpNumbers # Create sprite wrapper that displays HP bar @hpBar = SpriteWrapper.new(viewport) @hpBar.bitmap = @hpBarBitmap.bitmap @hpBar.src_rect.height = @hpBarBitmap.height/3 @sprites["hpBar"] = @hpBar # Create sprite wrapper that displays Exp bar @expBar = SpriteWrapper.new(viewport) @expBar.bitmap = @expBarBitmap.bitmap @sprites["expBar"] = @expBar # Create sprite wrapper that displays everything except the above @contents = BitmapWrapper.new(@databoxBitmap.width,@databoxBitmap.height) self.bitmap = @contents self.visible = false self.z = 150+((@battler.index)/2)*5 pbSetSystemFont(self.bitmap) end def dispose pbDisposeSpriteHash(@sprites) @databoxBitmap.dispose @numbersBitmap.dispose @hpBarBitmap.dispose @expBarBitmap.dispose @contents.dispose super end def x=(value) super @hpBar.x = value+@spriteBaseX+12#102 @expBar.x = value+@spriteBaseX+24 @hpNumbers.x = value+@spriteBaseX+80 end def y=(value) super @hpBar.y = value+40 @expBar.y = value+64 @hpNumbers.y = value+52 end def z=(value) super @hpBar.z = value+1 @expBar.z = value+1 @hpNumbers.z = value+2 end def opacity=(value) super for i in @sprites i[1].opacity = value if !i[1].disposed? end end def visible=(value) super for i in @sprites i[1].visible = value if !i[1].disposed? end @expBar.visible = (value && @showExp) end def color=(value) super for i in @sprites i[1].color = value if !i[1].disposed? end end def battler=(b) @battler = b self.visible = (@battler && !@battler.fainted?) end def hp return (@animatingHP) ? @currentHP : @battler.hp end def exp_fraction return (@animatingExp) ? @currentExp.to_f/@rangeExp : @battler.pokemon.exp_fraction end def animateHP(oldHP,newHP,rangeHP) @currentHP = oldHP @endHP = newHP @rangeHP = rangeHP # NOTE: A change in HP takes the same amount of time to animate, no matter # how big a change it is. @hpIncPerFrame = (newHP-oldHP).abs/(HP_BAR_CHANGE_TIME*Graphics.frame_rate) # minInc is the smallest amount that HP is allowed to change per frame. # This avoids a tiny change in HP still taking HP_BAR_CHANGE_TIME seconds. minInc = (rangeHP*4)/(@hpBarBitmap.width*HP_BAR_CHANGE_TIME*Graphics.frame_rate) @hpIncPerFrame = minInc if @hpIncPerFrame116 textPos.push([@battler.name,@spriteBaseX+8-nameOffset,0,false,NAME_BASE_COLOR,NAME_SHADOW_COLOR]) # Draw Pokémon's gender symbol case @battler.displayGender when 0 # Male textPos.push([_INTL("♂"),@spriteBaseX+126,0,false,MALE_BASE_COLOR,MALE_SHADOW_COLOR]) when 1 # Female textPos.push([_INTL("♀"),@spriteBaseX+126,0,false,FEMALE_BASE_COLOR,FEMALE_SHADOW_COLOR]) end pbDrawTextPositions(self.bitmap,textPos) # Draw Pokémon's level imagePos.push(["Graphics/Pictures/Battle/overlay_lv",@spriteBaseX+140,16]) if !$game_switches[SWITCH_NO_LEVELS_MODE] pbDrawNumber(@battler.level,self.bitmap,@spriteBaseX+162,16) if !$game_switches[SWITCH_NO_LEVELS_MODE] # Draw shiny icon if @battler.shiny? shinyX = (@battler.opposes?(0)) ? 206 : -6 # Foe's/player's pokeRadarShiny= !@battler.pokemon.debugShiny? && !@battler.pokemon.naturalShiny? addShinyStarsToGraphicsArray(imagePos,@spriteBaseX+shinyX,35, @battler.pokemon.bodyShiny?,@battler.pokemon.headShiny?,@battler.pokemon.debugShiny?, pokeRadarShiny) end # Draw Mega Evolution/Primal Reversion icon if @battler.mega? imagePos.push(["Graphics/Pictures/Battle/icon_mega",@spriteBaseX+8,34]) elsif @battler.primal? primalX = (@battler.opposes?) ? 208 : -28 # Foe's/player's if @battler.isSpecies?(:KYOGRE) imagePos.push(["Graphics/Pictures/Battle/icon_primal_Kyogre",@spriteBaseX+primalX,4]) elsif @battler.isSpecies?(:GROUDON) imagePos.push(["Graphics/Pictures/Battle/icon_primal_Groudon",@spriteBaseX+primalX,4]) end end # Draw owned icon (foe Pokémon only) if @battler.owned? && @battler.opposes?(0) imagePos.push(["Graphics/Pictures/Battle/icon_own",@spriteBaseX-8,42]) end # Draw status icon if @battler.status != :NONE s = GameData::Status.get(@battler.status).id_number if s == :POISON && @battler.statusCount > 0 # Badly poisoned s = GameData::Status::DATA.keys.length / 2 end imagePos.push(["Graphics/Pictures/Battle/icon_statuses",@spriteBaseX+24,56, 0,(s-1)*STATUS_ICON_HEIGHT,-1,STATUS_ICON_HEIGHT]) end pbDrawImagePositions(self.bitmap,imagePos) refreshHP refreshExp end def refreshHP @hpNumbers.bitmap.clear return if !@battler.pokemon # Show HP numbers if @showHP pbDrawNumber(self.hp,@hpNumbers.bitmap,54,2,1) pbDrawNumber(-1,@hpNumbers.bitmap,54,2) # / char pbDrawNumber(@battler.totalhp,@hpNumbers.bitmap,70,2) end # Resize HP bar w = 0 if self.hp>0 w = @hpBarBitmap.width.to_f*self.hp/@battler.totalhp w = 1 if w<1 # NOTE: The line below snaps the bar's width to the nearest 2 pixels, to # fit in with the rest of the graphics which are doubled in size. w = ((w/2.0).round)*2 end @hpBar.src_rect.width = w hpColor = 0 # Green bar hpColor = 1 if self.hp<=@battler.totalhp/2 # Yellow bar hpColor = 2 if self.hp<=@battler.totalhp/4 # Red bar @hpBar.src_rect.y = hpColor*@hpBarBitmap.height/3 end def refreshExp return if !@showExp return if @battler.level >= 100 w = exp_fraction * @expBarBitmap.width # NOTE: The line below snaps the bar's width to the nearest 2 pixels, to # fit in with the rest of the graphics which are doubled in size. w = ((w/2).round)*2 @expBar.src_rect.width = w end def updateHPAnimation return if !@animatingHP if @currentHP<@endHP # Gaining HP @currentHP += @hpIncPerFrame @currentHP = @endHP if @currentHP>=@endHP elsif @currentHP>@endHP # Losing HP @currentHP -= @hpIncPerFrame @currentHP = @endHP if @currentHP<=@endHP end # Refresh the HP bar/numbers refreshHP @animatingHP = false if @currentHP==@endHP end def updateExpAnimation return if !@animatingExp if !@showExp # Not showing the Exp bar, no need to waste time animating it @currentExp = @endExp @animatingExp = false return end if @currentExp<@endExp # Gaining Exp @currentExp += @expIncPerFrame @currentExp = @endExp if @currentExp>=@endExp elsif @currentExp>@endExp # Losing Exp @currentExp -= @expIncPerFrame @currentExp = @endExp if @currentExp<=@endExp end # Refresh the Exp bar refreshExp return if @currentExp!=@endExp # Exp bar still has more to animate # Exp bar is completely filled, level up with a flash and sound effect if @currentExp>=@rangeExp if @expFlash==0 pbSEStop @expFlash = Graphics.frame_rate/5 pbSEPlay("Pkmn exp full") self.flash(Color.new(64,200,248,192),@expFlash) for i in @sprites i[1].flash(Color.new(64,200,248,192),@expFlash) if !i[1].disposed? end else @expFlash -= 1 @animatingExp = false if @expFlash==0 end else pbSEStop # Exp bar has finished filling, end animation @animatingExp = false end end QUARTER_ANIM_PERIOD = Graphics.frame_rate*3/20 def updatePositions(frameCounter) self.x = @spriteX self.y = @spriteY # Data box bobbing while Pokémon is selected if @selected==1 || @selected==2 # Choosing commands/targeted or damaged case (frameCounter/QUARTER_ANIM_PERIOD).floor when 1 then self.y = @spriteY-2 when 3 then self.y = @spriteY+2 end end end def update(frameCounter=0) super() # Animate HP bar updateHPAnimation # Animate Exp bar updateExpAnimation # Update coordinates of the data box updatePositions(frameCounter) pbUpdateSpriteHash(@sprites) end end #=============================================================================== # Splash bar to announce a triggered ability #=============================================================================== class AbilitySplashBar < SpriteWrapper attr_reader :battler TEXT_BASE_COLOR = Color.new(0,0,0) TEXT_SHADOW_COLOR = Color.new(248,248,248) def initialize(side,viewport=nil) super(viewport) @side = side @battler = nil # Create sprite wrapper that displays background graphic @bgBitmap = AnimatedBitmap.new(_INTL("Graphics/Pictures/Battle/ability_bar")) @bgSprite = SpriteWrapper.new(viewport) @bgSprite.bitmap = @bgBitmap.bitmap @bgSprite.src_rect.y = (side==0) ? 0 : @bgBitmap.height/2 @bgSprite.src_rect.height = @bgBitmap.height/2 # Create bitmap that displays the text @contents = BitmapWrapper.new(@bgBitmap.width,@bgBitmap.height/2) self.bitmap = @contents pbSetSystemFont(self.bitmap) # Position the bar self.x = (side==0) ? -Graphics.width/2 : Graphics.width self.y = (side==0) ? 180 : 80 self.z = 120 self.visible = false end def dispose @bgSprite.dispose @bgBitmap.dispose @contents.dispose super end def x=(value) super @bgSprite.x = value end def y=(value) super @bgSprite.y = value end def z=(value) super @bgSprite.z = value-1 end def opacity=(value) super @bgSprite.opacity = value end def visible=(value) super @bgSprite.visible = value end def color=(value) super @bgSprite.color = value end def battler=(value) @battler = value refresh end def refresh self.bitmap.clear return if !@battler textPos = [] textX = (@side==0) ? 10 : self.bitmap.width-8 # Draw Pokémon's name textPos.push([_INTL("{1}'s",@battler.name),textX,-4,@side==1, TEXT_BASE_COLOR,TEXT_SHADOW_COLOR,true]) # Draw Pokémon's ability textPos.push([@battler.abilityName,textX,26,@side==1, TEXT_BASE_COLOR,TEXT_SHADOW_COLOR,true]) pbDrawTextPositions(self.bitmap,textPos) end def update super @bgSprite.update end end #=============================================================================== # Pokémon sprite (used in battle) #=============================================================================== class PokemonBattlerSprite < RPG::Sprite attr_reader :pkmn attr_accessor :index attr_accessor :selected attr_reader :sideSize def initialize(viewport,sideSize,index,battleAnimations) super(viewport) @pkmn = nil @sideSize = sideSize @index = index @battleAnimations = battleAnimations # @selected: 0 = not selected, 1 = choosing action bobbing for this Pokémon, # 2 = flashing when targeted @selected = 0 @frame = 0 @updating = false @spriteX = 0 # Actual x coordinate @spriteY = 0 # Actual y coordinate @spriteXExtra = 0 # Offset due to "bobbing" animation @spriteYExtra = 0 # Offset due to "bobbing" animation @_iconBitmap = nil self.visible = false @back=false end def dispose @_iconBitmap.dispose if @_iconBitmap @_iconBitmap = nil self.bitmap = nil if !self.disposed? super end def x; return @spriteX; end def y; return @spriteY; end def x=(value) @spriteX = value self.mirror=true if @back super(value+@spriteXExtra) end def y=(value) @spriteY = value self.mirror=true if @back super(value+@spriteYExtra) end def width; return (self.bitmap) ? self.bitmap.width : 0; end def height; return (self.bitmap) ? self.bitmap.height : 0; end def visible=(value) @spriteVisible = value if !@updating # For selection/targeting flashing super end # Set sprite's origin to bottom middle def pbSetOrigin return if !@_iconBitmap self.mirror=true if @back self.ox = @_iconBitmap.width/2 self.oy = @_iconBitmap.height end def pbSetPosition return if !@_iconBitmap pbSetOrigin if (@index%2)==0 self.z = 50+5*@index/2 else self.z = 50-5*(@index+1)/2 end # Set original position p = PokeBattle_SceneConstants.pbBattlerPosition(@index,@sideSize) @spriteX = p[0] @spriteY = p[1] # Apply metrics @pkmn.species_data.apply_metrics_to_sprite(self, @index) end def setPokemonBitmap(pkmn,back=false) @back = back self.mirror=true if @back @pkmn = pkmn @_iconBitmap.dispose if @_iconBitmap @_iconBitmap = GameData::Species.sprite_bitmap_from_pokemon(@pkmn, back) if @back @_iconBitmap.scale_bitmap(Settings::BACKRPSPRITE_SCALE) else @_iconBitmap.scale_bitmap(Settings::FRONTSPRITE_SCALE) end self.bitmap = (@_iconBitmap) ? @_iconBitmap.bitmap : nil pbSetPosition end # This method plays the battle entrance animation of a Pokémon. By default # this is just playing the Pokémon's cry, but you can expand on it. The # recommendation is to create a PictureEx animation and push it into # the @battleAnimations array. def pbPlayIntroAnimation(pictureEx=nil) @pkmn.play_cry if @pkmn end QUARTER_ANIM_PERIOD = Graphics.frame_rate*3/20 SIXTH_ANIM_PERIOD = Graphics.frame_rate*2/20 def update(frameCounter=0) return if !@_iconBitmap @updating = true # Update bitmap @_iconBitmap.update self.bitmap = @_iconBitmap.bitmap # Pokémon sprite bobbing while Pokémon is selected @spriteYExtra = 0 if @selected==1 # When choosing commands for this Pokémon case (frameCounter/QUARTER_ANIM_PERIOD).floor when 1 then @spriteYExtra = 2 when 3 then @spriteYExtra = -2 end end self.x = self.x self.y = self.y self.visible = @spriteVisible # Pokémon sprite blinking when targeted if @selected==2 && @spriteVisible case (frameCounter/SIXTH_ANIM_PERIOD).floor when 2, 5 then self.visible = false else self.visible = true end end @updating = false end end #=============================================================================== # Shadow sprite for Pokémon (used in battle) #=============================================================================== class PokemonBattlerShadowSprite < RPG::Sprite attr_reader :pkmn attr_accessor :index attr_accessor :selected def initialize(viewport,sideSize,index) super(viewport) @pkmn = nil @sideSize = sideSize @index = index @_iconBitmap = nil self.visible = false end def dispose @_iconBitmap.dispose if @_iconBitmap @_iconBitmap = nil self.bitmap = nil if !self.disposed? super end def width; return (self.bitmap) ? self.bitmap.width : 0; end def height; return (self.bitmap) ? self.bitmap.height : 0; end # Set sprite's origin to centre def pbSetOrigin return if !@_iconBitmap self.ox = @_iconBitmap.width/2 self.oy = @_iconBitmap.height/2 end def pbSetPosition return if !@_iconBitmap pbSetOrigin self.z = 3 # Set original position p = PokeBattle_SceneConstants.pbBattlerPosition(@index,@sideSize) self.x = p[0] self.y = p[1] # Apply metrics @pkmn.species_data.apply_metrics_to_sprite(self, @index, true) end def setPokemonBitmap(pkmn) @pkmn = pkmn @_iconBitmap.dispose if @_iconBitmap @_iconBitmap = GameData::Species.shadow_bitmap_from_pokemon(@pkmn) self.bitmap = (@_iconBitmap) ? @_iconBitmap.bitmap : nil pbSetPosition end def update(frameCounter=0) return if !@_iconBitmap # Update bitmap @_iconBitmap.update self.bitmap = @_iconBitmap.bitmap end end