[XP] Game_Animation with Eval Support

Started by Wecoc, August 18, 2014, 07:51:06 pm

Previous topic - Next topic

Wecoc

August 18, 2014, 07:51:06 pm Last Edit: August 19, 2014, 01:14:00 pm by Wecoc
Animations with Eval
Authors: Wecoc
Version: 1.2
Type: Animation Add-on
Key Term: Misc Add-on



Introduction

By default on animations you can assign a sound, screen flash, target flash or delete the target in a specific frame.

This script allows to add eval, ie "Call Script" in the animations.
With this you will be able to do things not possible until now.
I also added some shortcuts to make the eval a bit more easy, they are inside the module Skill_Shortcuts and you can create more there.
So basically you can add inside an animation whatever a Script Call supports. On Features I will list only the default 'shortcut' functions made on the script.



Features


  • SE play (with no limits on volume and frequence)

  • Screen flash, Target flash and Target erase (now you can use variables for parameters)

  • Screen Shake

  • Screen Tone (with memorize / restore functions)

  • Sprite x / y displacement




Demo

Animations with Eval 1.2



Script

#------------------------------------------------------------------------------
# Game_Animation with Eval Support
# Version: 1.2
# Author: Wecoc
#------------------------------------------------------------------------------
# o DESCRIPTION
#
# By default on animations you can assign a sound, screen flash, target flash
# or delete the target in a specific frame.
#
# This script allows to add eval, ie "Call Script" in the animations.
# With this you will be able to do things not possible until now.
# I also added some shortcuts to make the eval a bit more easy, they are
# inside the module Skill_Shortcuts and you can create more there.
#
# With this new version of the script, all the changes you do will be saved.
# So you can assign all the evals in the first map and they will be kept fixed.
#
#------------------------------------------------------------------------------
# o COMPATIBILITY
#
# CAUTION - If you use other scripts there may be two types of incompatibilities
#
# 1) Aliased methods
#
#     - RPG::Sprite :update_animation (alias), :update_loop_animation (alias)
#     - Scene_Title :command_new_game (alias), :battle_test (alias)
#     - Scene_Save :write_save_data (alias)
#     - Scene_Load :read_save_data (alias)
#     - Sprite_Character :update (alias)
#     - Sprite_Battler :update (alias)
#
# 2) $data_animations
#
#     Although $data_animations still works it will do without the evals.
#     To call an animation with its included evals you have to use
#     $game_animations in all the not default scripts.
#
#------------------------------------------------------------------------------
# o INSTRUCTIONS
#
# How to set a new eval:
# $game_animations[id].add_effect(frame, 'text', condition)
#  text - Code to eval
#  condition - 0 - Always, 1 - Hit, 2 - Miss ; optional, 0 by default
#
# / Example: $game_animations[1].add_effect(5, '$game_switches[1] = true', 1)
#
# How to clear all the added evals of an animation
# $game_animations[id].clear_effects
#------------------------------------------------------------------------------
# o SHORTCUTS
#
# Shortcuts are defined inside Skill_Shortcuts and you can add all you want.
# This is what will be used within the above described as 'text' but actually
# you can use them in any eval.
#==============================================================================

#==============================================================================
# Skill_Shortcuts
#==============================================================================

module Skill_Shortcuts

  # SE play
  def se_play(name, volume, pitch)
    Audio.se_play("Audio/SE/" + name, volume, pitch)
  end

  # Screen Flash
  def flash_screen(color, time)
    self.viewport.flash(color, time)
  end

  # (Use flash method for target flash)

  # Erase Target
  def hide(time)
    self.flash(nil, time)
  end

  # Shake Screen (I don't like how it looks in battle)
  def shake(power, speed, duration)
    $game_screen.start_shake(power, speed, duration)
  end

  # Memorize the current screen tone
  def get_tone
    @screen_tone = $game_screen.tone.clone
  end

  # Change the screen tone
  def set_tone(tone, time)
    $game_screen.start_tone_change(tone, time)
  end

  # Restore the memorized screen tone
  def restore_tone(time)
    @screen_tone ||= Tone.new(0, 0, 0, 0)
    $game_screen.start_tone_change(@screen_tone, time)
  end

  # Change the screen tone (it depends on the actual tone)
  def change_tone(tone, time)
    @screen_tone ||= Tone.new(0, 0, 0, 0)
    new_tone = @screen_tone.clone
    new_tone.red += tone.red
    new_tone.green += tone.green
    new_tone.blue += tone.blue
    new_tone.gray += tone.gray
    $game_screen.start_tone_change(new_tone, time)
  end

  # Change the x of the sprite
  def change_ani_x(new_x, duration=0)
    return @ani_x = new_x if duration == 0
    @ani_x_new = new_x
    @ani_x_duration = duration
  end

  # Change the y of the sprite
  def change_ani_y(new_y, duration=0)
    return @ani_y = new_y if duration == 0
    @ani_y_new = new_y
    @ani_y_duration = duration
  end
end

#==============================================================================
# RPG::Animation & RPG::Sprite Edits
#==============================================================================

class RPG::Animation::Frame
  define_method(:text_array) { @text_array ||= []}
end

class RPG::Sprite < ::Sprite
 
  alias update_animation_eval_hit update_animation unless $@
  alias update_loop_animation_eval_hit update_loop_animation unless $@
 
  def update_animation(*args)
    if @_animation_duration > 0
      frame_index = @_animation.frame_max - @_animation_duration
      text_array = @_animation.frames[frame_index].text_array
      for text_pair in text_array
        text = text_pair[0]
        cond = text_pair[1]
        hit = @_animation_hit
        if (cond == 0) or (cond == 1 and hit) or (cond == 2 and !hit)
          result = eval(text)
        end
      end
    end
    update_animation_eval_hit(*args)
  end

  def update_loop_animation(*args)
    frame_index = @_loop_animation_index
    text_array = @_animation.frames[frame_index].text_array
    for text_pair in text_array
      text = text_pair[0]
      cond = text_pair[1]
      hit = @_animation_hit
      if (cond == 0) or (cond == 1 and hit) or (cond == 2 and !hit)
        class_eval(text)
      end
    end
    update_loop_animation_eval_hit(*args)
  end
end

#==============================================================================
# Game_Animation & Game_Animations ($game_animations)
#==============================================================================

class Game_Animation
 
  attr_reader :animation_id
  attr_reader :name
  attr_reader :animation_name
  attr_reader :animation_hue
  attr_reader :position
  attr_reader :frame_max
  attr_reader :timings
  attr_reader :frames
 
  def initialize(animation_id)
    setup(animation_id)
  end
 
  def setup(animation_id)
    @animation_id = animation_id
    animation = $data_animations[animation_id]
    @name = animation.name
    @animation_name = animation.animation_name
    @animation_hue = animation.animation_hue
    @position = animation.position
    @frame_max = animation.frame_max
    @timings = animation.timings
    @frames = animation.frames
  end
 
  def id
    @animation_id
  end
 
  def add_effect(frame, text, condition=0)
    return false if frame > @frame_max
    @frames[frame].text_array.push([text, condition])
    @frames[frame].text_array.uniq!
    return true
  end

  def clear_effects(frame=nil)
    frames = frame.nil? ? @frames[0...@frame_max] : [@frames[frame]]
    frames.each {|f| f.text_array.clear}
    return true
  end
end

class Game_Animations

  def [](animation_id)
    return nil if animation_id > 999 or $data_animations[animation_id] == nil
    (@data||=[])[animation_id] ||= Game_Animation.new(animation_id)
  end
end

#==============================================================================
# $game_animations load/save
#==============================================================================

class Scene_Title
 
  alias animation_fix_new_game command_new_game unless $@
  alias animation_fix_battle battle_test unless $@
 
  def command_new_game
    $game_animations = Game_Animations.new
    animation_fix_new_game
  end
 
  def battle_test
    $game_animations = Game_Animations.new
    animation_fix_battle
  end
end

class Scene_Save < Scene_File
  alias animation_fix_write write_save_data unless $@
  def write_save_data(file)
    $game_system.game_animations = $game_animations
    animation_fix_write(file)
  end
end

class Scene_Load < Scene_File
  alias animation_fix_read read_save_data unless $@
  def read_save_data(file)
    animation_fix_read(file)
    $game_animations = $game_system.game_animations || Game_Animations.new
  end
end

class Game_System
  attr_accessor :game_animations
end 
#==============================================================================
# Sprite_Character & Sprite_Battler Edits
#==============================================================================

class Sprite_Character < RPG::Sprite
  include Skill_Shortcuts

  attr_accessor :ani_x
  attr_accessor :ani_y
 
  alias animation_fix_upd update unless $@
 
  def update(*args)
    pre_update_animation_effect
    animation_fix_upd(*args)
    update_animation_position_effect
  end
 
  def pre_update_animation_effect
    if @character && @character.animation_id != 0
      animation = $game_animations[@character.animation_id]
      animation(animation, true)
      @character.animation_id = 0
    end
  end
   
  def update_animation_position_effect
    return if @character.nil?
    if (@ani_x_duration ||= 0) >= 1
      d = @ani_x_duration
      @ani_x = ((@ani_x || 0) * (d - 1) + (@ani_x_new ||= 0)) / d
      @ani_x_duration -= 1
    end
    if (@ani_y_duration ||= 0) >= 1
      d = @ani_y_duration
      @ani_y = ((@ani_y || 0) * (d - 1) + (@ani_y_new ||= 0)) / d
      @ani_y_duration -= 1
    end
    self.x = @character.screen_x + (@ani_x ||= 0)
    self.y = @character.screen_y + (@ani_y ||= 0)
  end
end

class Sprite_Battler < RPG::Sprite
  include Skill_Shortcuts

  attr_accessor :ani_x
  attr_accessor :ani_y

  alias animation_fix_upd update unless $@
 
  def update(*args)
    pre_update_animation_effect
    animation_fix_upd(*args)
    update_animation_position_effect
  end
 
  def pre_update_animation_effect
    if @battler.nil?
      @battler_name = nil
    else     
      if @battler.damage.nil? and
        @battler.state_animation_id != @state_animation_id
        @state_animation_id = @battler.state_animation_id
        animation = $game_animations[@state_animation_id]
        loop_animation(animation)
      end
      if @battler_visible && @battler.animation_id != 0
        animation = $game_animations[@battler.animation_id]
        animation(animation, @battler.animation_hit)
        @battler.animation_id = 0
      end
    end
  end
 
  def update_animation_position_effect
    return if @battler.nil?
    if (@ani_x_duration ||= 0) >= 1
      d = @ani_x_duration
      @ani_x = ((@ani_x || 0) * (d - 1) + (@ani_x_new ||= 0)) / d
      @ani_x_duration -= 1
    end
    if (@ani_y_duration ||= 0) >= 1
      d = @ani_y_duration
      @ani_y = ((@ani_y || 0) * (d - 1) + (@ani_y_new ||= 0)) / d
      @ani_y_duration -= 1
    end
    self.x = @battler.screen_x + (@ani_x ||= 0)
    self.y = @battler.screen_y + (@ani_y ||= 0)
  end
end



Instructions

They are all in the code.


Compatibility

This part is explained in the code. It has some possible incompatibilities.


Author's Notes

Do you need help to add a new shortcut? Contact me and I will try to help.

LiTTleDRAgo

Spoiler: ShowHide
#------------------------------------------------------------------------------
# Game_Animation with Eval Support
# Version: 1.2
# Author: Wecoc
#------------------------------------------------------------------------------
# o DESCRIPTION
#
# By default on animations you can assign a sound, screen flash, target flash
# or delete the target in a specific frame.
#
# This script allows to add eval, ie "Call Script" in the animations.
# With this you will be able to do things not possible until now.
# I also added some shortcuts to make the eval a bit more easy, they are
# inside the module Skill_Shortcuts and you can create more there.
#
# With this new version of the script, all the changes you do will be saved.
# So you can assign all the evals in the first map and they will be kept fixed.
#
#------------------------------------------------------------------------------
# o COMPATIBILITY
#
# CAUTION - If you use other scripts there may be two types of incompatibilities
#
# 1) Aliased methods
#
#     - RPG::Sprite :update_animation (alias), :update_loop_animation (alias)
#     - Scene_Title :command_new_game (alias), :battle_test (alias)
#     - Scene_Save :write_save_data (alias)
#     - Scene_Load :read_save_data (alias)
#     - Sprite_Character :update (alias)
#     - Sprite_Battler :update (alias)
#
# 2) $data_animations
#
#     Although $data_animations still works it will do without the evals.
#     To call an animation with its included evals you have to use
#     $game_animations in all the not default scripts.
#
#------------------------------------------------------------------------------
# o INSTRUCTIONS
#
# How to set a new eval:
# $game_animations[id].add_effect(frame, 'text', condition)
#  text - Code to eval
#  condition - 0 - Always, 1 - Hit, 2 - Miss ; optional, 0 by default
#
# / Example: $game_animations[1].add_effect(5, '$game_switches[1] = true', 1)
#
# How to clear all the added evals of an animation
# $game_animations[id].clear_effects
#------------------------------------------------------------------------------
# o SHORTCUTS
#
# Shortcuts are defined inside Skill_Shortcuts and you can add all you want.
# This is what will be used within the above described as 'text' but actually
# you can use them in any eval.
#==============================================================================

#==============================================================================
# Skill_Shortcuts
#==============================================================================

module Skill_Shortcuts

  # SE play
  def se_play(name, volume, pitch)
    Audio.se_play("Audio/SE/" + name, volume, pitch)
  end

  # Screen Flash
  def flash_screen(color, time)
    self.viewport.flash(color, time)
  end

  # (Use flash method for target flash)

  # Erase Target
  def hide(time)
    self.flash(nil, time)
  end

  # Shake Screen (I don't like how it looks in battle)
  def shake(power, speed, duration)
    $game_screen.start_shake(power, speed, duration)
  end

  # Memorize the current screen tone
  def get_tone
    @screen_tone = $game_screen.tone.clone
  end

  # Change the screen tone
  def set_tone(tone, time)
    $game_screen.start_tone_change(tone, time)
  end

  # Restore the memorized screen tone
  def restore_tone(time)
    @screen_tone ||= Tone.new(0, 0, 0, 0)
    $game_screen.start_tone_change(@screen_tone, time)
  end

  # Change the screen tone (it depends on the actual tone)
  def change_tone(tone, time)
    @screen_tone ||= Tone.new(0, 0, 0, 0)
    new_tone = @screen_tone.clone
    new_tone.red += tone.red
    new_tone.green += tone.green
    new_tone.blue += tone.blue
    new_tone.gray += tone.gray
    $game_screen.start_tone_change(new_tone, time)
  end

  # Change the x of the sprite
  def change_ani_x(new_x, duration=0)
    return @ani_x = new_x if duration == 0
    @ani_x_new = new_x
    @ani_x_duration = duration
  end

  # Change the y of the sprite
  def change_ani_y(new_y, duration=0)
    return @ani_y = new_y if duration == 0
    @ani_y_new = new_y
    @ani_y_duration = duration
  end
end

#==============================================================================
# RPG::Animation & RPG::Sprite Edits
#==============================================================================

class RPG::Animation::Frame
  define_method(:text_array) { @text_array ||= []}
end

class RPG::Sprite < ::Sprite
 
  alias update_animation_eval_hit update_animation unless $@
  alias update_loop_animation_eval_hit update_loop_animation unless $@
 
  def update_animation(*args)
    if @_animation_duration > 0
      frame_index = @_animation.frame_max - @_animation_duration
      text_array = @_animation.frames[frame_index].text_array
      for text_pair in text_array
        text = text_pair[0]
        cond = text_pair[1]
        hit = @_animation_hit
        if (cond == 0) or (cond == 1 and hit) or (cond == 2 and !hit)
          result = eval(text)
        end
      end
    end
    update_animation_eval_hit(*args)
  end

  def update_loop_animation(*args)
    frame_index = @_loop_animation_index
    text_array = @_animation.frames[frame_index].text_array
    for text_pair in text_array
      text = text_pair[0]
      cond = text_pair[1]
      hit = @_animation_hit
      if (cond == 0) or (cond == 1 and hit) or (cond == 2 and !hit)
        class_eval(text)
      end
    end
    update_loop_animation_eval_hit(*args)
  end
end

#==============================================================================
# Game_Animation & Game_Animations ($game_animations)
#==============================================================================

class Game_Animation
 
  attr_reader :animation_id
  attr_reader :name
  attr_reader :animation_name
  attr_reader :animation_hue
  attr_reader :position
  attr_reader :frame_max
  attr_reader :timings
  attr_reader :frames
 
  def initialize(animation_id)
    setup(animation_id)
  end
 
  def setup(animation_id)
    @animation_id = animation_id
    animation = $data_animations[animation_id]
    @name = animation.name
    @animation_name = animation.animation_name
    @animation_hue = animation.animation_hue
    @position = animation.position
    @frame_max = animation.frame_max
    @timings = animation.timings
    @frames = animation.frames
  end
 
  def id
    @animation_id
  end
 
  def add_effect(frame, text, condition=0)
    return false if frame > @frame_max
    @frames[frame].text_array.push([text, condition])
    @frames[frame].text_array.uniq!
    return true
  end

  def clear_effects(frame=nil)
    frames = frame.nil? ? @frames[0...@frame_max] : [@frames[frame]]
    frames.each {|f| f.text_array.clear}
    return true
  end
end

class Game_Animations

  def [](animation_id)
    return nil if animation_id > 999 or $data_animations[animation_id] == nil
    (@data||=[])[animation_id] ||= Game_Animation.new(animation_id)
  end
end

#==============================================================================
# $game_animations load/save
#==============================================================================

class Scene_Title
 
  alias animation_fix_new_game command_new_game unless $@
  alias animation_fix_battle battle_test unless $@
 
  def command_new_game
    $game_animations = Game_Animations.new
    animation_fix_new_game
  end
 
  def battle_test
    $game_animations = Game_Animations.new
    animation_fix_battle
  end
end

class Scene_Save < Scene_File
  alias animation_fix_write write_save_data unless $@
  def write_save_data(file)
    $game_system.game_animations = $game_animations
    animation_fix_write(file)
  end
end

class Scene_Load < Scene_File
  alias animation_fix_read read_save_data unless $@
  def read_save_data(file)
    animation_fix_read(file)
    $game_animations = $game_system.game_animations || Game_Animations.new
  end
end

class Game_System
  attr_accessor :game_animations
end 
#==============================================================================
# Sprite_Character & Sprite_Battler Edits
#==============================================================================

class Sprite_Character < RPG::Sprite
  include Skill_Shortcuts

  attr_accessor :ani_x
  attr_accessor :ani_y
 
  alias animation_fix_upd update unless $@
 
  def update(*args)
    pre_update_animation_effect
    animation_fix_upd(*args)
    update_animation_position_effect
  end
 
  def pre_update_animation_effect
    if @character && @character.animation_id != 0
      animation = $game_animations[@character.animation_id]
      animation(animation, true)
      @character.animation_id = 0
    end
  end
   
  def update_animation_position_effect
    return if @character.nil?
    if (@ani_x_duration ||= 0) >= 1
      d = @ani_x_duration
      @ani_x = ((@ani_x || 0) * (d - 1) + (@ani_x_new ||= 0)) / d
      @ani_x_duration -= 1
    end
    if (@ani_y_duration ||= 0) >= 1
      d = @ani_y_duration
      @ani_y = ((@ani_y || 0) * (d - 1) + (@ani_y_new ||= 0)) / d
      @ani_y_duration -= 1
    end
    self.x = @character.screen_x + (@ani_x ||= 0)
    self.y = @character.screen_y + (@ani_y ||= 0)
  end
end

class Sprite_Battler < RPG::Sprite
  include Skill_Shortcuts

  attr_accessor :ani_x
  attr_accessor :ani_y

  alias animation_fix_upd update unless $@
 
  def update(*args)
    pre_update_animation_effect
    animation_fix_upd(*args)
    update_animation_position_effect
  end
 
  def pre_update_animation_effect
    if @battler.nil?
      @battler_name = nil
    else     
      if @battler.damage.nil? and
        @battler.state_animation_id != @state_animation_id
        @state_animation_id = @battler.state_animation_id
        animation = $game_animations[@state_animation_id]
        loop_animation(animation)
      end
      if @battler_visible && @battler.animation_id != 0
        animation = $game_animations[@battler.animation_id]
        animation(animation, @battler.animation_hit)
        @battler.animation_id = 0
      end
    end
  end
 
  def update_animation_position_effect
    return if @battler.nil?
    if (@ani_x_duration ||= 0) >= 1
      d = @ani_x_duration
      @ani_x = ((@ani_x || 0) * (d - 1) + (@ani_x_new ||= 0)) / d
      @ani_x_duration -= 1
    end
    if (@ani_y_duration ||= 0) >= 1
      d = @ani_y_duration
      @ani_y = ((@ani_y || 0) * (d - 1) + (@ani_y_new ||= 0)) / d
      @ani_y_duration -= 1
    end
    self.x = @battler.screen_x + (@ani_x ||= 0)
    self.y = @battler.screen_y + (@ani_y ||= 0)
  end
end

Wecoc

Thank you for fixing the alias! I usually have problems to alias :update methods because they contain a huge loop do, but it was not the case so I have no excuse xD

I edited my post with your script, it has less incompatibilities and less code lines.
The demo was also updated.

I love eval implementations, hehehe.

Wingard

Heyo, i decided to check this out and in Demo after interracting with the armored guy 2 times quickly (second animation was initiated before the first one ended playing) the screen got darker for good. Talking to him again few times can give us the constant pitch black screen. Is this supposed to be like that?  :)

Wecoc

Ok it's because when you talk with him he sets the current screen tone (dark because the animation) as the last target tone, so calculates the tones wrong... It's a bug on the demo, but it will not cause problems on default battle animations nor default events with a wait counter after the animation. If I could, I would fix that definitely but I don't know how it's supposed to be corrected.

Thank you for your reply.