Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

RMXP Battle Animation flip/mirror system

This system could be useful for any sideview battlesystem, whether it be Trickster's ATB+CTB, Ccoa's Sideview system, or Animated Battlers.

When I refer to the Battle Animations, I refer to those animations such as the 'Hit', 'Claw', 'Fang', 'Remedy 1', 'Fire 3' or whatever. Not the sprite animations that have been worked out by scripters like Ccoa, Cybersam, etc...

The premise is this:
Most (if not all) battle animations are under the assumption that you are using the typical frontview system. So animations like the arrow ("bow") show attacks coming 'up' from the hero into the enemy battler.

It's a fair assumption that sideview systems will want battle animations that can be shown going left to right, or right to left... but no one wants to create TWO battle animations for enemies and actors.

>>>--------> <--------<<<

What I'm hoping to be made is a system that will 'flip' battle animations and adjust the 'x-position' of each frame accordingly. The 'flip' of the animation should be controlled by a true/false value in the script.
Code:
if @active_battler.is_a?(Game_Actor)
  @battle_flip = true
else
  @battle_flip = false
end
... or something to that degree. It would work well for animations such as flying arrows, fireballs, or whatever.

Additional methods to flip, such as vertical flipping (if the heroes are at the top of the screen), wouldn't be that bad an idea either. Personally, I wouldn't need it... that I know of... but I'm sure that it would be welcomed.
 
Hi.
I don't know how RMXP handles animations, but if you find it, you can use the method frames_flip_horizontal! that Trickster wrote in his library.
I think you need to do something like replacing
Code:
bitmap = RPG::Cache.animation(name, hue)
for
Code:
bitmap = RPG::Cache.animation(name, hue).dup.frames_flip_horizontal!(5)
 
Looking at the Cache.animation(name,hue)... statement that you presented... it looks to be along the right lines of what is needed (I think). But I haven't messed with the Battle Animation's graphic section at all. I would also have to know how to re-adjust the 'x' position of the individual frames (and possibly 'y' for those interested) to make a fully functional 'flipped' animation.

Presently, I'm hoping someone familiar with the 'battle animation' code can make this system that will comply with any sideview (or alternate battlers) system.
 
BUMP.

I've done some work with animations, but mostly in regards to x/y positioning and extracting values from battlesystems to do scripted animations. But the RGSS Animation class is bugging the hell outta me...

Can anyone take a shot at this?
 
Not to burst your bubble, but you could just copy every animation you have, and click Mass Modify Button, and theres a "flip around" button there.

But of course, you save like 100 animations by using your script xD
 
Moreso headache-wise for sideview systems when a skill uses one animation and if left AND right battlers need 'em. Battler on the left would need to use one skill that casts attacks to the right... and the Battler on the right would need skills that attack to the left. You'd need to duplicate the skills that use the animations as well.

And if the script can do top-down/bottom-up flipping, it could work with other animation scripts like the ones Trickster and ParaDog made where battlers can be above or under the targets.
 
Plus, the Cell Batch button would only work if every single frame was flipped or unflipped. I could probably do this, but I'd need access to the built-in classes, and no matter how much I google, the only database is apparently on Creation Asylum. Which is down right now >_<. And I have no idea when it might be back up. If anyone has a database of all the built-in classes, I'd love to have it :D
 
Well duplicate and invert the animations could be very annoying, but there's no need to duplicate skills, to be more simple have sure that every skill/item/hit that actors and enemies learn/use is complete, then mass copy\paste all of your animations and invert the desired ones, you may delete the unused ones but don't change ANY order.
At the script part, go to Scene_Battle4/make_basic_action_result and change:
Code:
@animation2_id = @active_battler.animation2_id
To:
Code:
if @active_battler.is_a?(Game_Actor)
@animation2_id = @active_battler.animation2_id
else
@animation2_id = @active_battler.animation2_id = AniL - AniR***
end
AniL - AniR***, it's the difference between the Animation to left ID and Animation to right ID or vice-versa.
kinda boring but works, you should modify the same code at the make_skill_action_result and make_item_action_result methods in the same scene.

Edit: Hmmm NVM then, if PirateFaafy may do it, so let's wait him/her to make it.^^
 
Truth be told, the animation handeling is very difficult in rmxp (I found this out a few days making a multiple animations system).

It would almost completely require you create alternate animations and frames. But you don't have to do this via scripts. You may only have to do this for the images, as you can just alias the animation_name to add something like '_flipped' to it.

Otherwise you would have to modify the rpg::sprite methods because that is where all the bitmap loading takes place.
 
Yeah, that's kinda what I was thinking of. It would check if a certain boolean was true, then the cell's .mirror function would become !cell.mirror. But doing it immediately after loading probably sounds better :D, and the vertical thing would be a bit more complicated; .angle would be cell.angle + 180 % 360 and .mirror would be inverted again, which might be hard to do without editing the cells directly. Those were some lightning quick responses though O.O
 
Well, there may be a way to flip vertically with the Methods and Classes Library by Trickster, but I've only REAL limited experience with the Animation class...

And there's the whole reposition of the 'cells' x/y position thing that hugopkmn pointed out as well. Flipping doesn't do any good if the images are still playing out in the same left/right positions.
 
That's true, but I think Trickster's Method 'n Class library method allows you to divide the image-to-be-flipped into cells, only flipping the individual parts :D I just noticed, though, that you'd also have to make x -x(negative x) in order for it to work correctly, but after I can get the flip, that should be pie :thumb:
 
I have minor experiences with animations, the fartest thing I've done til now was to change the background for an animation(via script obviously).
e.g.
Actor A casts Ultima.
The Background gradually fades out until it's all black.
Ultima's animation is executed.
The Background gradually fades in.


I also have made something like FFVI's holy(the animated background) but it was zOMFG laggy so I quitted it.
Back to the thread, I believe that to invert an animation sprite isn't that hard, if you can flip the sprite, to invert its coords is easier, just multiply the x/y values by -1 is enough.
Edit: I'm not sure, but I think it may be handled at the rpg::Animations module or at least get the core infos for the animation inversion, actually I didn't touch this module yet but I'm curious to learn about it, does any1 know where can I get it?
 
Umm I am no scripter at all but couldn't you just look up all built in classes in the help file?
Code:
module RPG
  class Sprite < ::Sprite
    @@_animations = []
    @@_reference_count = {}
    def initialize(viewport = nil)
      super(viewport)
      @_whiten_duration = 0
      @_appear_duration = 0
      @_escape_duration = 0
      @_collapse_duration = 0
      @_damage_duration = 0
      @_animation_duration = 0
      @_blink = false
    end
    def dispose
      dispose_damage
      dispose_animation
      dispose_loop_animation
      super
    end
    def whiten
      self.blend_type = 0
      self.color.set(255, 255, 255, 128)
      self.opacity = 255
      @_whiten_duration = 16
      @_appear_duration = 0
      @_escape_duration = 0
      @_collapse_duration = 0
    end
    def appear
      self.blend_type = 0
      self.color.set(0, 0, 0, 0)
      self.opacity = 0
      @_appear_duration = 16
      @_whiten_duration = 0
      @_escape_duration = 0
      @_collapse_duration = 0
    end
    def escape
      self.blend_type = 0
      self.color.set(0, 0, 0, 0)
      self.opacity = 255
      @_escape_duration = 32
      @_whiten_duration = 0
      @_appear_duration = 0
      @_collapse_duration = 0
    end
    def collapse
      self.blend_type = 1
      self.color.set(255, 64, 64, 255)
      self.opacity = 255
      @_collapse_duration = 48
      @_whiten_duration = 0
      @_appear_duration = 0
      @_escape_duration = 0
    end
    def damage(value, critical)
      dispose_damage
      if value.is_a?(Numeric)
        damage_string = value.abs.to_s
      else
        damage_string = value.to_s
      end
      bitmap = Bitmap.new(160, 48)
      bitmap.font.name = "Arial Black"
      bitmap.font.size = 32
      bitmap.font.color.set(0, 0, 0)
      bitmap.draw_text(-1, 12-1, 160, 36, damage_string, 1)
      bitmap.draw_text(+1, 12-1, 160, 36, damage_string, 1)
      bitmap.draw_text(-1, 12+1, 160, 36, damage_string, 1)
      bitmap.draw_text(+1, 12+1, 160, 36, damage_string, 1)
      if value.is_a?(Numeric) and value < 0
        bitmap.font.color.set(176, 255, 144)
      else
        bitmap.font.color.set(255, 255, 255)
      end
      bitmap.draw_text(0, 12, 160, 36, damage_string, 1)
      if critical
        bitmap.font.size = 20
        bitmap.font.color.set(0, 0, 0)
        bitmap.draw_text(-1, -1, 160, 20, "CRITICAL", 1)
        bitmap.draw_text(+1, -1, 160, 20, "CRITICAL", 1)
        bitmap.draw_text(-1, +1, 160, 20, "CRITICAL", 1)
        bitmap.draw_text(+1, +1, 160, 20, "CRITICAL", 1)
        bitmap.font.color.set(255, 255, 255)
        bitmap.draw_text(0, 0, 160, 20, "CRITICAL", 1)
      end
      @_damage_sprite = ::Sprite.new(self.viewport)
      @_damage_sprite.bitmap = bitmap
      @_damage_sprite.ox = 80
      @_damage_sprite.oy = 20
      @_damage_sprite.x = self.x
      @_damage_sprite.y = self.y - self.oy / 2
      @_damage_sprite.z = 3000
      @_damage_duration = 40
    end
    def animation(animation, hit)
      dispose_animation
      @_animation = animation
      return if @_animation == nil
      @_animation_hit = hit
      @_animation_duration = @_animation.frame_max
      animation_name = @_animation.animation_name
      animation_hue = @_animation.animation_hue
      bitmap = RPG::Cache.animation(animation_name, animation_hue)
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      @_animation_sprites = []
      if @_animation.position != 3 or not @@_animations.include?(animation)
        for i in 0..15
          sprite = ::Sprite.new(self.viewport)
          sprite.bitmap = bitmap
          sprite.visible = false
          @_animation_sprites.push(sprite)
        end
        unless @@_animations.include?(animation)
          @@_animations.push(animation)
        end
      end
      update_animation
    end
    def loop_animation(animation)
      return if animation == @_loop_animation
      dispose_loop_animation
      @_loop_animation = animation
      return if @_loop_animation == nil
      @_loop_animation_index = 0
      animation_name = @_loop_animation.animation_name
      animation_hue = @_loop_animation.animation_hue
      bitmap = RPG::Cache.animation(animation_name, animation_hue)
      if @@_reference_count.include?(bitmap)
        @@_reference_count[bitmap] += 1
      else
        @@_reference_count[bitmap] = 1
      end
      @_loop_animation_sprites = []
      for i in 0..15
        sprite = ::Sprite.new(self.viewport)
        sprite.bitmap = bitmap
        sprite.visible = false
        @_loop_animation_sprites.push(sprite)
      end
      update_loop_animation
    end
    def dispose_damage
      if @_damage_sprite != nil
        @_damage_sprite.bitmap.dispose
        @_damage_sprite.dispose
        @_damage_sprite = nil
        @_damage_duration = 0
      end
    end
    def dispose_animation
      if @_animation_sprites != nil
        sprite = @_animation_sprites[0]
        if sprite != nil
          @@_reference_count[sprite.bitmap] -= 1
          if @@_reference_count[sprite.bitmap] == 0
            sprite.bitmap.dispose
          end
        end
        for sprite in @_animation_sprites
          sprite.dispose
        end
        @_animation_sprites = nil
        @_animation = nil
      end
    end
    def dispose_loop_animation
      if @_loop_animation_sprites != nil
        sprite = @_loop_animation_sprites[0]
        if sprite != nil
          @@_reference_count[sprite.bitmap] -= 1
          if @@_reference_count[sprite.bitmap] == 0
            sprite.bitmap.dispose
          end
        end
        for sprite in @_loop_animation_sprites
          sprite.dispose
        end
        @_loop_animation_sprites = nil
        @_loop_animation = nil
      end
    end
    def blink_on
      unless @_blink
        @_blink = true
        @_blink_count = 0
      end
    end
    def blink_off
      if @_blink
        @_blink = false
        self.color.set(0, 0, 0, 0)
      end
    end
    def blink?
      @_blink
    end
    def effect?
      @_whiten_duration > 0 or
      @_appear_duration > 0 or
      @_escape_duration > 0 or
      @_collapse_duration > 0 or
      @_damage_duration > 0 or
      @_animation_duration > 0
    end
    def update
      super
      if @_whiten_duration > 0
        @_whiten_duration -= 1
        self.color.alpha = 128 - (16 - @_whiten_duration) * 10
      end
      if @_appear_duration > 0
        @_appear_duration -= 1
        self.opacity = (16 - @_appear_duration) * 16
      end
      if @_escape_duration > 0
        @_escape_duration -= 1
        self.opacity = 256 - (32 - @_escape_duration) * 10
      end
      if @_collapse_duration > 0
        @_collapse_duration -= 1
        self.opacity = 256 - (48 - @_collapse_duration) * 6
      end
      if @_damage_duration > 0
        @_damage_duration -= 1
        case @_damage_duration
        when 38..39
          @_damage_sprite.y -= 4
        when 36..37
          @_damage_sprite.y -= 2
        when 34..35
          @_damage_sprite.y += 2
        when 28..33
          @_damage_sprite.y += 4
        end
        @_damage_sprite.opacity = 256 - (12 - @_damage_duration) * 32
        if @_damage_duration == 0
          dispose_damage
        end
      end
      if @_animation != nil and (Graphics.frame_count % 2 == 0)
        @_animation_duration -= 1
        update_animation
      end
      if @_loop_animation != nil and (Graphics.frame_count % 2 == 0)
        update_loop_animation
        @_loop_animation_index += 1
        @_loop_animation_index %= @_loop_animation.frame_max
      end
      if @_blink
        @_blink_count = (@_blink_count + 1) % 32
        if @_blink_count < 16
          alpha = (16 - @_blink_count) * 6
        else
          alpha = (@_blink_count - 16) * 6
        end
        self.color.set(255, 255, 255, alpha)
      end
      @@_animations.clear
    end
    def update_animation
      if @_animation_duration > 0
        frame_index = @_animation.frame_max - @_animation_duration
        cell_data = @_animation.frames[frame_index].cell_data
        position = @_animation.position
        animation_set_sprites(@_animation_sprites, cell_data, position)
        for timing in @_animation.timings
          if timing.frame == frame_index
            animation_process_timing(timing, @_animation_hit)
          end
        end
      else
        dispose_animation
      end
    end
    def update_loop_animation
      frame_index = @_loop_animation_index
      cell_data = @_loop_animation.frames[frame_index].cell_data
      position = @_loop_animation.position
      animation_set_sprites(@_loop_animation_sprites, cell_data, position)
      for timing in @_loop_animation.timings
        if timing.frame == frame_index
          animation_process_timing(timing, true)
        end
      end
    end
    def animation_set_sprites(sprites, cell_data, position)
      for i in 0..15
        sprite = sprites[i]
        pattern = cell_data[i, 0]
        if sprite == nil or pattern == nil or pattern == -1
          sprite.visible = false if sprite != nil
          next
        end
        sprite.visible = true
        sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
        if position == 3
          if self.viewport != nil
            sprite.x = self.viewport.rect.width / 2
            sprite.y = self.viewport.rect.height - 160
          else
            sprite.x = 320
            sprite.y = 240
          end
        else
          sprite.x = self.x - self.ox + self.src_rect.width / 2
          sprite.y = self.y - self.oy + self.src_rect.height / 2
          sprite.y -= self.src_rect.height / 4 if position == 0
          sprite.y += self.src_rect.height / 4 if position == 2
        end
        sprite.x += cell_data[i, 1]
        sprite.y += cell_data[i, 2]
        sprite.z = 2000
        sprite.ox = 96
        sprite.oy = 96
        sprite.zoom_x = cell_data[i, 3] / 100.0
        sprite.zoom_y = cell_data[i, 3] / 100.0
        sprite.angle = cell_data[i, 4]
        sprite.mirror = (cell_data[i, 5] == 1)
        sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
        sprite.blend_type = cell_data[i, 7]
      end
    end
    def animation_process_timing(timing, hit)
      if (timing.condition == 0) or
         (timing.condition == 1 and hit == true) or
         (timing.condition == 2 and hit == false)
        if timing.se.name != ""
          se = timing.se
          Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)
        end
        case timing.flash_scope
        when 1
          self.flash(timing.flash_color, timing.flash_duration * 2)
        when 2
          if self.viewport != nil
            self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
          end
        when 3
          self.flash(nil, timing.flash_duration * 2)
        end
      end
    end
    def x=(x)
      sx = x - self.x
      if sx != 0
        if @_animation_sprites != nil
          for i in 0..15
            @_animation_sprites[i].x += sx
          end
        end
        if @_loop_animation_sprites != nil
          for i in 0..15
            @_loop_animation_sprites[i].x += sx
          end
        end
      end
      super
    end
    def y=(y)
      sy = y - self.y
      if sy != 0
        if @_animation_sprites != nil
          for i in 0..15
            @_animation_sprites[i].y += sy
          end
        end
        if @_loop_animation_sprites != nil
          for i in 0..15
            @_loop_animation_sprites[i].y += sy
          end
        end
      end
      super
    end
  end
end
I'm sorry if I'm wrong but, it was the first thing that came to my mind...
 
Not a question of the classes in the help file. It's a matter of experience at editing the animation class in a manner to alter the Battle Animations. I DID manage to make that script where you could change the volume in those animations (for config systems like Slipknot's or vgvgf's). But this, I'm sorry to say, has gotten to me...
 
I've just found the method that sets any sprite for an animation, since I'm not that familiar with mirror effect and stuff, I am quite helpless from now on, so I'll leave the rest for the pros lol.
Code:
module RPG
  class Sprite < ::Sprite
    def animation_set_sprites(sprites, cell_data, position)
      for i in 0..15
        sprite = sprites[i]
        pattern = cell_data[i, 0]
        if sprite == nil or pattern == nil or pattern == -1
          sprite.visible = false if sprite != nil
          next
        end
        sprite.visible = true
        sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
        if position == 3
          if self.viewport != nil
            sprite.x = self.viewport.rect.width / 2
            sprite.y = self.viewport.rect.height - 160
          else
            sprite.x = 320
            sprite.y = 240
          end
        else
          sprite.x = self.x - self.ox + self.src_rect.width / 2
          sprite.y = self.y - self.oy + self.src_rect.height / 2
          sprite.y -= self.src_rect.height / 4 if position == 0
          sprite.y += self.src_rect.height / 4 if position == 2
        end
        sprite.x += cell_data[i, 1]
        sprite.y += cell_data[i, 2]
        sprite.z = 2000
        sprite.ox = 96
        sprite.oy = 96
        sprite.zoom_x = cell_data[i, 3] / 100.0
        sprite.zoom_y = cell_data[i, 3] / 100.0
        [b]sprite.angle = cell_data[i, 4][/b]
        [i]sprite.mirror = (cell_data[i, 5] == 1)[/i]
        sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
        sprite.blend_type = cell_data[i, 7]
      end
    end
  end
end
PS. I'm quite sure that the bolded part of the code is the only thing to modify if you are going to invert the sprite vertically, the italiced(spelled rite?o0) one is for horizontal inversion, it's probably 1 for true and 0(Confirmed!) for false.
100% confirmed!
sprite.angle and sprite.mirror are the things to be modified!
Just an idea it's worth a try:
This script is Here!
Put it between main and the other custom scripts. I hope this is what you wanted. Enjoy =D
EDIT : Ok! Tested and worked on RMXP's BS, for side view just set the $SIDEVIEW at the 1st line to 1(one) and for default BS set it to 0(zero).
 
Darn, beat me to it ^_^ And I had just found the RPG::Sprite class in the help files, too -_- yeah, I'm slow. But that's a good script there, excellent work! You should put it in Submitted Scripts.
 
RATS!

Default battlesystem...
Code:
Script 'PleaseWork' line 37: NoMethodError
undefined method `active_battler' for #<Scene_Battle:0x15e7a78>
I just used a fresh project.':|

And it doesn't perform on the field map. O_o

On a constructive note, if you DON'T use true/false... you could pass the $sideview flag into the script to turn it on or off without script editing. Passing 'false' into a script directly will freeze it. Found that it's a fault with RGSS... Seph told me about it. Try 0 and 1.
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top