#------------------------------------------------------------------------------#
# Galv's Timed Button Attacks
#------------------------------------------------------------------------------#
# For: RPGMAKER VX ACE
# Version 1.7
#------------------------------------------------------------------------------#
# 2013-04-25 - Version 1.7 - Added compatibility with Yanfly's cast animation
# 2013-04-17 - Version 1.6 - Fixed a pretty big bug with ending battle.
# 2013-04-17 - Version 1.5 - Fixed evaded popup appearing in Yanfly's engine
# - when hitting using 'guarantee hit' option. Added
# - fix for if battle ends early while indicator live
# 2013-04-08 - Version 1.4 - Fixed bug with defend indicator not showing on
# - correct actor.
# 2013-03-15 - Version 1.3 - Fixed animation option
# 2013-03-15 - Version 1.2 - Fixed a bug with not pressing button at all
# 2013-03-15 - Version 1.1 - added option to turn off battle log messages
# - disabled hit indicator when actor is confused
# - fixed yanfly battle engine compatability bug
# 2013-03-11 - Version 1.0 - release
#------------------------------------------------------------------------------#
# Adds a graphical indicator to selected attacks that allows the player to
# press a button at a certain time for the attack to do extra damage or apply
# a state. Different attacks can use different buttons, graphics and have
# varied speeds and difficulty.
#
# NOTES: Put this script below any battle scripts. Tested in default battle
# system as well as Yanfly's default battle script.
#
# MORE NOTES: If the indicator disappears too soon, increase the frames of the
# skill's animation in the Database Animations tab.
#
#------------------------------------------------------------------------------#
#------------------------------------------------------------------------------#
# NOTE TAGS for SKILLS or ITEMS
#------------------------------------------------------------------------------#
#
# <btnpress> # Enables the btn press with flash and SE when successful
# OR
# <btnpress n> # Enables the btn press and plays animation n when successful
# # This animation cancels the current skill's animation.
#
# # NOTE: Only one of these is required for a skill.
#
#------------------------------------------------------------------------------#
# NOTE TAGS for SKILLS or ITEMS (Leaving any of these out will use defaults)
#------------------------------------------------------------------------------#
#
# <btn x> # Use a different button to press (default :X)
# <btnmsg x> # Use a different message number when hit successful
#
# <btnmultiply x> # Multiply the damage/healing by x (default 2)
#
# <btnstates x,x,x> # Apply states with id x when timing is hit.
# <btnrstates x,x,x> # Remove states with id x when timing is hit.
#
# <btnopt a,b,c,d> # a = start time (delay before indicator moves)
# # b = target time (when button should be pressed)
# # c = indicator image number to use
# # d = difficulty number. Higher is easier to hit
#
# # The indicator will appear at the start time (a) and shrink down to meet
# # the target circle at the target time (b) when the player should press
# # the button.
# # DEFAULTS: a = 0 b = 31 c = 1 d = 0
#------------------------------------------------------------------------------#
# EXAMPLES:
#
# <btnpress 4> # Enables script and uses animation 4 on success
# <btn Y> # Use the :Y ("s" on the keyboard) button instead
# <btnmultiply 3> # 3 x damage
# <btnstates 2,3> # Applies states 2 and 3
# <btnrstates 5,6,7> # Removes states 5, 6 and 7
# <btnopt 10,61,2,0> # Indicator runs from 10-61 frames using image 2
# # and no change in difficulty
#
#------------------------------------------------------------------------------#
#------------------------------------------------------------------------------#
# NOTE TAGS for ACTORS or EQUIPS - These determine a defending timed hit
#------------------------------------------------------------------------------#
#
# <btnpress> # Enables an actor or equip defending timed hit.
# OR
# <btnpress n> # Enables def timed hit and plays animation n when successful
# # Only use this if you are using a sideview battle script.
# # Only works with battle scripts that show animations on actors
#
# # NOTE: Only one of these is required for an equip or actor.
#
#------------------------------------------------------------------------------#
# Optional NOTE TAGS for ACTORS or EQUIPS (Leaving any out will use defaults)
#------------------------------------------------------------------------------#
# <btn x> # Use a different button to press (default :Z)
# <btnmsg x> # Use a different message number when hit successful
#
# <btnmultiply x> # Multiply the damage/healing by x (default 0.5)
#
# <btnstates x,x,x> # Apply states with id x when timing is hit.
# <btnrstates x,x,x> # Remove states with id x when timing is hit.
#
# <btnopt a,b,c,d> # a = start time (delay before indicator moves)
# # DEFAULTS: a = 0 b = 31 c = 3 d = 0
#
#------------------------------------------------------------------------------#
($imported ||= {})["Galv_BtnPress_Hit"] = true
module Galv_BtnAtk
#------------------------------------------------------------------------------#
# SETUP OPTIONS
#------------------------------------------------------------------------------#
#---------------#
# PREFERENCES #
#---------------#
GUARANTEE_HIT = false # If player hits the timing, the attack cannot miss
SHOW_AS_CRIT = true # Displays hits on enemy as critical hits.
ATK_MULTIPLIER = 1.5 # Default damage/healing multipliers if the timing is
DEF_MULTIPLIER = 0.5 # successful for attacking and defending.
DIFFICULTY = 3 # The higher this number, the further away from the
# center circle will score a success. 0 is extremely
# difficult, the higher this number the easier.
DISABLE_SWITCH = 194 # Turn swith ON to disable this
ATK_BTN = :X # Default attack button to press. :X is "a"
DEF_BTN = :Z # Default defend button to press. :Z is "d"
Y_OFFSET = -50 # 0 is positioned at bottom of a battler.
Y_FRONT_OFFSET = 0 # x and y offset for targeting your party when using a
X_FRONT_OFFSET = 0 # battle system with no actor x,y locations. By default
# it centers at the top of the battle status window.
#---------------#
# VOCAB #
#---------------#
BATTLE_LOG_TXT = true # Display battle log text (below) true or false
# Displaying battle messages slows down multi-hit
# skills in some battle systems.
# Below is a list of battlelog vocab that you can add to and use for timed hit
TXT = [ # don't touch
"A perfect hit!", # 0) Default attacking timed hit success
"Super effective!", # 1) Default allied spell timed hit success
"A great block!", # 2) Default defending timed hit success
"CUSTOM TEST!", # 3) When <btnmsg 3> notetag
#"Another one", # 4) Add as many as you need
#"Another one", # 5) Add as many as you need
#"Another one", # 6) Add as many as you need
#"Another one", # 7) Add as many as you need
] # don't touch
#---------------#
# GRAPHICS #
#---------------#
TARGET_IMG = "hit_target" # The inner target that the indicator meets
INDICATOR_IMG = "hit_indicator" # The indicator that shrinks to the target
# NOTE: These are the base image names for the graphics. The actual files will
# have a number appended to them. By default this number is 1, so the file
# names by default would be: "hit_target1.png", "hit_indicator1.png"
# This number can be changed using the option notetag so different skills can
# use a different graphic. Images go in /Graphics/System/ folder.
COLOR_HIT = [0, 255, 0] # [R,G,B] indicator turns this color when success
COLOR_MISS = [255, 0, 0] # [R,G,B] indicator turns this color when fail
#---------------#
# SOUNDS #
#---------------#
SE_HIT = ["Flash1",100,100] # "SE_Name", volume, pitch when hit timing
SE_MISS = ["Buzzer1",85,100] # "SE_Name", volume, pitch when missed timing
#------------------------------------------------------------------------------#
# END SETUP OPTIONS
#------------------------------------------------------------------------------#
end
class RPG::BaseItem
def btnpress
if @btnpress.nil?
if @note =~ /<btnpress>/i
@btnpress = 0
elsif @note =~ /<btnpress[ ](.*)>/i
@btnpress = $1.to_i
else
@btnpress = -1
end
end
@btnpress
end
def btnmultiply
if @btnmultiply.nil?
if @note =~ /<btnmultiply[ ](.*)>/i
@btnmultiply = $1.to_f
else
if self.is_a?(RPG::Actor) || self.is_a?(RPG::EquipItem)
@btnmultiply = Galv_BtnAtk::DEF_MULTIPLIER
else
@btnmultiply = Galv_BtnAtk::ATK_MULTIPLIER
end
end
end
@btnmultiply
end
def btn
if @btn.nil?
if @note =~ /<btn[ ](.*)>/i
@btn = $1.to_sym
else
if self.is_a?(RPG::Actor) || self.is_a?(RPG::EquipItem)
@btn = Galv_BtnAtk::DEF_BTN
else
@btn = Galv_BtnAtk::ATK_BTN
end
end
end
@btn
end
def btnmsg
if @btnmsg.nil?
if @note =~ /<btnmsg[ ](.*)>/i
@btnmsg = $1.to_i
else
@btnmsg = nil
end
end
@btnmsg
end
def btnstates
if @btnstates.nil?
if @note =~ /<btnstates[ ](.*)>/i
@btnstates = $1.to_s.split(",").map {|i| i.to_i}
else
@btnstates = []
end
end
@btnstates
end
def btnrstates
if @btnrstates.nil?
if @note =~ /<btnrstates[ ](.*)>/i
@btnrstates = $1.to_s.split(",").map {|i| i.to_i}
else
@btnrstates = []
end
end
@btnrstates
end
def btnopt
if @btnopt.nil?
if @note =~ /<btnopt[ ](.*)>/i
@btnopt = $1.to_s.split(",").map {|i| i.to_i}
else
if self.is_a?(RPG::Actor) || self.is_a?(RPG::EquipItem)
@btnopt = [0,31,3,0]
else
@btnopt = [0,31,1,0]
end
end
end
@btnopt
end
end # RPG::BaseItem
class Game_Temp
attr_accessor :btncrit
attr_accessor :btndata
alias galv_btnhit_gt_initialize initialize
def initialize
galv_btnhit_gt_initialize
@btncrit = false
@btndata = [2,nil] # [multipler,vocab]
end
end # Game_Temp
class Game_Actor < Game_Battler
if !$imported["YEA-BattleEngine"]
attr_accessor :screen_x
attr_accessor :screen_y
end
end # Game_Actor < Game_Battler
class Game_ActionResult
# Cannot miss if hit button at right time
alias galv_btnhit_gar_hit? hit?
def hit?
if $game_temp.btncrit && Galv_BtnAtk::GUARANTEE_HIT
@missed = false
@evaded = false
@used
else
galv_btnhit_gar_hit?
end
end
# Modify damage if button is hit and make it show critical
alias galv_btnhit_gar_make_damage make_damage
def make_damage(value, item)
if $game_temp.btncrit
value = (value * $game_temp.btndata[0]).to_i
@critical = true if @battler.is_a?(Game_Enemy) && Galv_BtnAtk::SHOW_AS_CRIT
end
galv_btnhit_gar_make_damage(value, item)
end
end # Game_ActionResult
class Window_BattleLog < Window_Selectable
alias galv_btnhit_wblog_display_critical display_critical
def display_critical(target, item)
if $game_temp.btncrit && Galv_BtnAtk::BATTLE_LOG_TXT
if $game_temp.btndata[1]
text = $game_temp.btndata[1]
else
text = target.actor? ? Galv_BtnAtk::TXT[1] : Galv_BtnAtk::TXT[0]
end
add_text(text)
wait
else
galv_btnhit_wblog_display_critical(target, item)
end
end
end # Window_BattleLog < Window_Selectable
class Scene_Battle < Scene_Base
attr_accessor :btnactive
alias galv_btnhit_sb_update_basic update_basic
def update_basic
if @btnactive
btn_pressed
@hit_indicator.update if @hit_indicator
end
galv_btnhit_sb_update_basic
end
def hskill
if @def_opts
@def_opts
else
@subject.current_action.item
end
end
def btn_pressed
return if $game_switches[Galv_BtnAtk::DISABLE_SWITCH] || $game_troop.all_dead?
if Input.trigger?(hskill.btn) && @hit_indicator.hit?
$game_temp.btncrit = true
$game_temp.btndata[0] = hskill.btnmultiply
btn_addstate if hskill.btnstates
btn_remstate if hskill.btnrstates
@hit_indicator.success
if hskill.btnpress > 0
show_hit_success_anim(@current_inditargets, hskill.btnpress)
else
$game_troop.screen.start_flash(Color.new(255,255,255,255),25)
RPG::SE.new(Galv_BtnAtk::SE_HIT[0],Galv_BtnAtk::SE_HIT[1],Galv_BtnAtk::SE_HIT[2]).play
end
@btnactive = nil
elsif Input.trigger?(hskill.btn)
$game_temp.btncrit = false
@hit_indicator.fail
RPG::SE.new(Galv_BtnAtk::SE_MISS[0],Galv_BtnAtk::SE_MISS[1],Galv_BtnAtk::SE_MISS[2]).play
@btnactive = nil
end
end
def btn_addstate
@current_inditargets.each { |t|
already_dead = t.dead?
hskill.btnstates.each { |state| t.add_state(state) if !already_dead }
t.perform_collapse_effect if t.dead? && !already_dead
}
end
def btn_remstate
@current_inditargets.each { |t|
already_dead = t.dead?
hskill.btnrstates.each { |state| t.remove_state(state) }
t.perform_collapse_effect if t.dead? && !already_dead
}
end
def show_hit_success_anim(targets, animation_id, mirror = false)
if $data_animations[animation_id]
targets[0].animation_id = animation_id
targets[0].animation_mirror = mirror
end
end
alias galv_btnhit_sb_use_item use_item
def use_item
galv_btnhit_sb_use_item
$game_temp.btncrit = false
end
def do_indicator(targets)
return if targets.nil? || targets.empty? || @castanim
@def_opts = nil
item = @subject.current_action.item
target = targets[0]
if @subject.is_a?(Game_Actor) && !@subject.confusion? && item.btnpress >= 0
$game_temp.btndata[1] = item.btnmsg ? Galv_BtnAtk::TXT[item.btnmsg] : nil
@current_inditargets = targets
setup_hit_indicator(item.btnopt)
@btnactive = true
elsif target && defender?(target)
$game_temp.btndata[1] = @def_opts.btnmsg ?
Galv_BtnAtk::TXT[@def_opts.btnmsg] : Galv_BtnAtk::TXT[2]
@current_inditargets = targets
setup_hit_indicator(@def_opts.btnopt)
@btnactive = true
end
update_for_wait
end
alias galv_btnhit_sb_show_animation show_animation
def show_animation(targets, animation_id)
do_indicator(targets)
galv_btnhit_sb_show_animation(targets, animation_id)
end
def defender?(target)
return false if !target.is_a?(Game_Actor)
return false if @subject.is_a?(Game_Actor)
defend = -1
defend = $data_actors[target.id].btnpress
@def_opts = $data_actors[target.id]
target.equips.each { |i|
next if i.nil?
if i.btnpress > defend
defend = i.btnpress
@def_opts = i
end
}
return true if defend >= 0
end
def setup_hit_indicator(array)
return if @current_inditargets[0].nil? ||
$game_switches[Galv_BtnAtk::DISABLE_SWITCH] || $game_troop.all_dead?
if !@current_inditargets.empty? && @current_inditargets[0].screen_x
x = @current_inditargets[0].screen_x
y = @current_inditargets[0].screen_y + Galv_BtnAtk::Y_OFFSET
else
x = Graphics.width / 2 + Galv_BtnAtk::X_FRONT_OFFSET
y = Graphics.width / 1.7 + Galv_BtnAtk::Y_FRONT_OFFSET
end
@hit_indicator = Timed_Hits.new(x,y,array)
end
alias galv_btnhit_sb_invoke_item invoke_item
def invoke_item(target, item)
if !$imported["YEA-BattleEngine"]
@btnactive = nil
@hit_indicator.dispose if @hit_indicator
end
galv_btnhit_sb_invoke_item(target, item)
if $imported["YEA-BattleEngine"]
@btnactive = nil
@hit_indicator.dispose if @hit_indicator
end
end
alias galv_btnhit_sb_terminate terminate
def terminate
galv_btnhit_sb_terminate
@hit_indicator.dispose if @hit_indicator
end
if $imported["YEA-CastAnimations"]
alias galv_btnhit_sb_process_casting_animation process_casting_animation
def process_casting_animation
@castanim = true
item = @subject.current_action.item
cast_ani = item.cast_ani
return if cast_ani <= 0
show_animation([@subject], cast_ani)
@castanim = false
end
end
end # Scene_Battle < Scene_Base
#------------------------------------------------------------------------------#
# Class Timed_Hits
#------------------------------------------------------------------------------#
class Timed_Hits
def initialize(x,y,options)
@x = x
@y = y
@opt = options
@img = @opt[2].to_s
@start_frame = @opt[0]
@target_frame = @opt[1]
@current_frame = 0
@s = (3.to_f - 0.6) / (@target_frame - @start_frame)
@d = (Galv_BtnAtk::DIFFICULTY + @opt[3]).to_f * 0.5
create_hit_indicator
create_target_indicator
end
def create_target_indicator
@hittarget = Sprite.new(@viewport1)
@hittarget.bitmap = Cache.system(Galv_BtnAtk::TARGET_IMG + @img)
@hittarget.z = 100
@hittarget.x = @x - (@hittarget.bitmap.width / 2)
@hittarget.y = @y - (@hittarget.bitmap.height / 2)
@hittarget.opacity = 130
end
def create_hit_indicator
@hitindi = Sprite.new(@viewport1)
@hitindi.bitmap = Cache.system(Galv_BtnAtk::INDICATOR_IMG + @img)
@hitindi.x = @x
@hitindi.y = @y
@hitindi.z = 100
@hitindi.opacity = 0
@hitindi.zoom_x = 3
@hitindi.zoom_y = 3
@hitindi.color = Color.new(0, 0, 0, 0)
end
def hit?
@current_frame.between?(@target_frame - @d,@target_frame + @d)
end
def success
c = Galv_BtnAtk::COLOR_HIT
@hitindi.color = Color.new(c[0],c[1],c[2],255)
end
def fail
c = Galv_BtnAtk::COLOR_MISS
@hitindi.color = Color.new(c[0],c[1],c[2],255)
end
def update
update_indicators
end
def update_indicators
return @current_frame += 1 if @current_frame < @start_frame
@hitindi.opacity = 100
@hitindi.zoom_x -= @s
@hitindi.zoom_y -= @s
@hitindi.x = @x - (@hitindi.bitmap.width / 2) * @hitindi.zoom_x
@hitindi.y = @y - (@hitindi.bitmap.height / 2) * @hitindi.zoom_y
@current_frame += 1
if @hitindi.zoom_x <= 0
fail
SceneManager.scene.btnactive = nil
end
end
def dispose
@hitindi.dispose if @hitindi
@hittarget.dispose if @hittarget
end
end # Timed_Hits