mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 21:24:59 +00:00
Added canvas to new animation editor (isn't interactive yet), improved example animations
This commit is contained in:
@@ -84,6 +84,7 @@ class AnimationEditor
|
||||
]
|
||||
|
||||
def initialize(anim_id, anim)
|
||||
load_settings
|
||||
@anim_id = anim_id
|
||||
@anim = anim
|
||||
@pbs_path = anim[:pbs_path]
|
||||
@@ -109,11 +110,7 @@ class AnimationEditor
|
||||
# Menu bar
|
||||
@components[:menu_bar] = AnimationEditor::MenuBar.new(0, 0, MENU_BAR_WIDTH, MENU_BAR_HEIGHT, @viewport)
|
||||
# Canvas
|
||||
@components[:canvas] = AnimationEditor::Canvas.new(@canvas_viewport)
|
||||
# Play controls
|
||||
@components[:play_controls] = AnimationEditor::PlayControls.new(
|
||||
PLAY_CONTROLS_X, PLAY_CONTROLS_Y, PLAY_CONTROLS_WIDTH, PLAY_CONTROLS_HEIGHT, @viewport
|
||||
)
|
||||
@components[:canvas] = AnimationEditor::Canvas.new(@canvas_viewport, @anim, @settings)
|
||||
# Side panes
|
||||
[:commands_pane, :se_pane, :particle_pane, :keyframe_pane].each do |pane|
|
||||
@components[pane] = UIControls::ControlsContainer.new(SIDE_PANE_X, SIDE_PANE_Y, SIDE_PANE_WIDTH, SIDE_PANE_HEIGHT)
|
||||
@@ -127,6 +124,10 @@ class AnimationEditor
|
||||
PARTICLE_LIST_X, PARTICLE_LIST_Y, PARTICLE_LIST_WIDTH, PARTICLE_LIST_HEIGHT, @viewport
|
||||
)
|
||||
@components[:particle_list].set_interactive_rects
|
||||
# Play controls
|
||||
@components[:play_controls] = AnimationEditor::PlayControls.new(
|
||||
PLAY_CONTROLS_X, PLAY_CONTROLS_Y, PLAY_CONTROLS_WIDTH, PLAY_CONTROLS_HEIGHT, @viewport
|
||||
)
|
||||
# Animation properties pop-up window
|
||||
@components[:animation_properties] = UIControls::ControlsContainer.new(
|
||||
ANIM_PROPERTIES_X + 4, ANIM_PROPERTIES_Y, ANIM_PROPERTIES_WIDTH - 8, ANIM_PROPERTIES_HEIGHT
|
||||
@@ -200,22 +201,14 @@ class AnimationEditor
|
||||
end
|
||||
|
||||
def set_canvas_contents
|
||||
@components[:canvas].bg_name = "indoor1"
|
||||
end
|
||||
|
||||
def set_play_controls_contents
|
||||
@components[:play_controls].duration = @components[:particle_list].duration
|
||||
end
|
||||
|
||||
def set_commands_pane_contents
|
||||
commands_pane = @components[:commands_pane]
|
||||
commands_pane.add_header_label(:header, _INTL("Edit particle at keyframe"))
|
||||
# :frame (related to graphic) - If the graphic is user's sprite/target's
|
||||
# sprite, make this instead a choice of front/back/same as the main sprite/
|
||||
# opposite of the main sprite. Probably need two controls in the same space
|
||||
# and refresh_component(:commands_pane) makes the appropriate one visible.
|
||||
commands_pane.add_labelled_number_text_box(:x, _INTL("X"), -(CANVAS_WIDTH + 128), CANVAS_WIDTH + 128, 0)
|
||||
commands_pane.add_labelled_number_text_box(:y, _INTL("Y"), -(CANVAS_WIDTH + 128), CANVAS_HEIGHT + 128, 0)
|
||||
commands_pane.add_labelled_number_text_box(:x, _INTL("X"), -200, 200, 0)
|
||||
commands_pane.add_labelled_number_text_box(:y, _INTL("Y"), -200, 200, 0)
|
||||
commands_pane.add_labelled_number_slider(:z, _INTL("Priority"), -50, 50, 0)
|
||||
# TODO: If the graphic is user's sprite/target's sprite, make :frame instead
|
||||
# a choice of front/back/same as the main sprite/opposite of the main
|
||||
# sprite. Will need two controls in the same space.
|
||||
@@ -232,13 +225,6 @@ class AnimationEditor
|
||||
2 => _INTL("Subtractive")
|
||||
}, 0)
|
||||
commands_pane.add_labelled_button(:color_tone, _INTL("Color/Tone"), _INTL("Edit"))
|
||||
# commands_pane.add_labelled_dropdown_list(:priority, _INTL("Priority"), { # TODO: Include sub-priority.
|
||||
# :behind_all => _INTL("Behind all"),
|
||||
# :behind_user => _INTL("Behind user"),
|
||||
# :above_user => _INTL("In front of user"),
|
||||
# :above_all => _INTL("In front of everything")
|
||||
# }, :above_user)
|
||||
# :sub_priority
|
||||
# commands_pane.add_labelled_button(:masking, _INTL("Masking"), _INTL("Edit"))
|
||||
# TODO: Add buttons that shift all commands from the current keyframe and
|
||||
# later forwards/backwards in time?
|
||||
@@ -271,12 +257,7 @@ class AnimationEditor
|
||||
particle_pane.get_control(:name).set_blacklist("User", "Target", "SE")
|
||||
particle_pane.add_labelled_label(:graphic_name, _INTL("Graphic"), "")
|
||||
particle_pane.add_labelled_button(:graphic, "", _INTL("Change"))
|
||||
particle_pane.add_labelled_dropdown_list(:focus, _INTL("Focus"), {
|
||||
:user => _INTL("User"),
|
||||
:target => _INTL("Target"),
|
||||
:user_and_target => _INTL("User and target"),
|
||||
:screen => _INTL("Screen")
|
||||
}, :user)
|
||||
particle_pane.add_labelled_dropdown_list(:focus, _INTL("Focus"), {}, :user)
|
||||
# FlipIfFoe
|
||||
# RotateIfFoe
|
||||
# Delete button (if not "User"/"Target"/"SE")
|
||||
@@ -302,6 +283,10 @@ class AnimationEditor
|
||||
@components[:particle_list].set_particles(@anim[:particles])
|
||||
end
|
||||
|
||||
def set_play_controls_contents
|
||||
@components[:play_controls].duration = @components[:particle_list].duration
|
||||
end
|
||||
|
||||
def set_animation_properties_contents
|
||||
anim_properties = @components[:animation_properties]
|
||||
anim_properties.add_header_label(:header, _INTL("Animation properties"))
|
||||
@@ -386,9 +371,9 @@ class AnimationEditor
|
||||
def set_components_contents
|
||||
set_menu_bar_contents
|
||||
set_canvas_contents
|
||||
set_play_controls_contents
|
||||
set_side_panes_contents
|
||||
set_particle_list_contents
|
||||
set_play_controls_contents # Intentionally after set_particle_list_contents
|
||||
set_animation_properties_contents
|
||||
set_graphic_chooser_contents
|
||||
set_audio_chooser_contents
|
||||
@@ -396,6 +381,21 @@ class AnimationEditor
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def load_settings
|
||||
# TODO: Load these from a saved file.
|
||||
@settings = {
|
||||
:side_sizes => [1, 1],
|
||||
:user_index => 0,
|
||||
:target_indices => [1],
|
||||
:user_opposes => false,
|
||||
# TODO: Ideally be able to independently choose base graphics, which will
|
||||
# be a separate setting here.
|
||||
:canvas_bg => "indoor1",
|
||||
:user_sprite_name => "ARCANINE",
|
||||
:target_sprite_name => "ABOMASNOW"
|
||||
}
|
||||
end
|
||||
|
||||
def save
|
||||
GameData::Animation.register(@anim, @anim_id)
|
||||
Compiler.write_battle_animation_file(@anim[:pbs_path])
|
||||
@@ -454,6 +454,7 @@ class AnimationEditor
|
||||
ctrl = @components[:animation_properties].get_control(:move)
|
||||
case @anim[:type]
|
||||
when :move, :opp_move
|
||||
# TODO: Cache this list?
|
||||
move_list = []
|
||||
GameData::Move.each { |m| move_list.push([m.id.to_s, m.name]) }
|
||||
move_list.push(["STRUGGLE", _INTL("Struggle")]) if move_list.none? { |val| val[0] == "STRUGGLE" }
|
||||
@@ -467,6 +468,8 @@ class AnimationEditor
|
||||
def refresh_component_values(component_sym)
|
||||
component = @components[component_sym]
|
||||
case component_sym
|
||||
when :canvas
|
||||
component.keyframe = keyframe
|
||||
when :commands_pane
|
||||
new_vals = AnimationEditor::ParticleDataHelper.get_all_keyframe_particle_values(@anim[:particles][particle_index], keyframe)
|
||||
component.controls.each do |ctrl|
|
||||
@@ -475,8 +478,45 @@ class AnimationEditor
|
||||
# TODO: new_vals[ctrl[0]][1] is whether the value is being interpolated,
|
||||
# which should be indicated somehow in ctrl[1].
|
||||
end
|
||||
# TODO: component.get_control(:frame).disable if the particle's graphic is
|
||||
# not a spritesheet or is "USER"/"TARGET"/etc. (enable otherwise).
|
||||
# Set an appropriate range for the X and Y properties depending on the
|
||||
# particle's focus
|
||||
case @anim[:particles][particle_index][:focus]
|
||||
when :foreground, :midground, :background # Cover the whole screen
|
||||
component.get_control(:x).min_value = -128
|
||||
component.get_control(:x).max_value = CANVAS_WIDTH + 128
|
||||
component.get_control(:y).min_value = -128
|
||||
component.get_control(:y).max_value = CANVAS_HEIGHT + 128
|
||||
when :user, :target, :user_side_foreground, :user_side_background,
|
||||
:target_side_foreground, :target_side_background # Around the focus
|
||||
component.get_control(:x).min_value = -CANVAS_WIDTH
|
||||
component.get_control(:x).max_value = CANVAS_WIDTH
|
||||
component.get_control(:y).min_value = -CANVAS_HEIGHT
|
||||
component.get_control(:y).max_value = CANVAS_HEIGHT
|
||||
when :user_and_target # Covers both foci
|
||||
component.get_control(:x).min_value = -CANVAS_WIDTH
|
||||
component.get_control(:x).max_value = GameData::Animation::USER_AND_TARGET_SEPARATION[0] + CANVAS_WIDTH
|
||||
component.get_control(:y).min_value = GameData::Animation::USER_AND_TARGET_SEPARATION[1] - CANVAS_HEIGHT
|
||||
component.get_control(:y).max_value = CANVAS_HEIGHT
|
||||
end
|
||||
# Set an appropriate range for the priority (z) property depending on the
|
||||
# particle's focus
|
||||
case @anim[:particles][particle_index][:focus]
|
||||
when :user_and_target
|
||||
component.get_control(:z).min_value = GameData::Animation::USER_AND_TARGET_SEPARATION[2] - 50
|
||||
component.get_control(:z).max_value = 50
|
||||
else
|
||||
component.get_control(:z).min_value = -50
|
||||
component.get_control(:z).max_value = 50
|
||||
end
|
||||
# Disable the "Frame" control if the particle's graphic is predefined to
|
||||
# be the user's or target's sprite
|
||||
# TODO: Also disable it if the particle's graphic isn't a spritesheet.
|
||||
if ["USER", "USER_OPP", "USER_FRONT", "USER_BACK",
|
||||
"TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"].include?(@anim[:particles][particle_index][:graphic])
|
||||
component.get_control(:frame).disable
|
||||
else
|
||||
component.get_control(:frame).enable
|
||||
end
|
||||
when :se_pane
|
||||
se_particle = @anim[:particles].select { |p| p[:name] == "SE" }[0]
|
||||
kyfrm = keyframe
|
||||
@@ -532,8 +572,32 @@ class AnimationEditor
|
||||
component.get_control(:graphic).enable
|
||||
component.get_control(:focus).enable
|
||||
end
|
||||
# TODO: Set the possible focus options depending on whether the animation
|
||||
# has a target/user.
|
||||
# Set the possible foci depending on whether the animation involves a
|
||||
# target
|
||||
# TODO: Also filter for user/no user if implemented.
|
||||
if @anim[:no_target]
|
||||
component.get_control(:focus).values = {
|
||||
:foreground => _INTL("Foreground"),
|
||||
:midground => _INTL("Midground"),
|
||||
:background => _INTL("Background"),
|
||||
:user => _INTL("User"),
|
||||
:user_side_foreground => _INTL("In front of user's side"),
|
||||
:user_side_background => _INTL("Behind user's side")
|
||||
}
|
||||
else
|
||||
component.get_control(:focus).values = {
|
||||
:foreground => _INTL("Foreground"),
|
||||
:midground => _INTL("Midground"),
|
||||
:background => _INTL("Background"),
|
||||
:user => _INTL("User"),
|
||||
:target => _INTL("Target"),
|
||||
:user_and_target => _INTL("User and target"),
|
||||
:user_side_foreground => _INTL("In front of user's side"),
|
||||
:user_side_background => _INTL("Behind user's side"),
|
||||
:target_side_foreground => _INTL("In front of target's side"),
|
||||
:target_side_background => _INTL("Behind target's side")
|
||||
}
|
||||
end
|
||||
when :animation_properties
|
||||
refresh_move_property_options
|
||||
case @anim[:type]
|
||||
@@ -596,6 +660,7 @@ class AnimationEditor
|
||||
@components[:particle_list].change_particle_commands(particle_index)
|
||||
@components[:play_controls].duration = @components[:particle_list].duration
|
||||
refresh_component(:commands_pane)
|
||||
refresh_component(:canvas)
|
||||
end
|
||||
when :se_pane
|
||||
case property
|
||||
@@ -651,6 +716,7 @@ class AnimationEditor
|
||||
new_cmds = AnimationEditor::ParticleDataHelper.set_property(particle, property, value)
|
||||
@components[:particle_list].change_particle(particle_index)
|
||||
refresh_component(:particle_pane)
|
||||
refresh_component(:canvas)
|
||||
end
|
||||
when :keyframe_pane
|
||||
# TODO: Stuff here once I decide what controls to add.
|
||||
@@ -658,6 +724,8 @@ class AnimationEditor
|
||||
# refresh if keyframe != old_keyframe || particle_index != old_particle_index
|
||||
# TODO: Lots of stuff here when buttons are added to it.
|
||||
when :animation_properties
|
||||
# TODO: Will changes here need to refresh any other components (e.g. side
|
||||
# panes)? Probably.
|
||||
case property
|
||||
when :type, :opp_variant
|
||||
type = @components[:animation_properties].get_control(:type).value
|
||||
@@ -669,12 +737,15 @@ class AnimationEditor
|
||||
@anim[:type] = (opp) ? :opp_common : :common
|
||||
end
|
||||
refresh_component(:animation_properties)
|
||||
refresh_component(:canvas)
|
||||
when :pbs_path
|
||||
txt = value.gsub!(/\.txt$/, "")
|
||||
@anim[property] = txt
|
||||
when :has_target
|
||||
@anim[:no_target] = !value
|
||||
# TODO: Add/delete the "Target" particle accordingly.
|
||||
# TODO: Add/delete the "Target" particle accordingly. Then refresh a lot
|
||||
# of components.
|
||||
refresh_component(:canvas)
|
||||
when :usable
|
||||
@anim[:ignore] = !value
|
||||
else
|
||||
@@ -704,6 +775,7 @@ class AnimationEditor
|
||||
end
|
||||
component.clear_changed
|
||||
end
|
||||
# TODO: Call repaint only if component responds to it? Canvas won't.
|
||||
component.repaint if sym == :particle_list || sym == :menu_bar
|
||||
if @captured
|
||||
@captured = nil if !component.busy?
|
||||
|
||||
@@ -34,7 +34,12 @@ module AnimationEditor::ParticleDataHelper
|
||||
next
|
||||
end
|
||||
# In a "MoveXYZ" command; need to interpolate
|
||||
ret[0] = lerp(ret[0], cmd[2], cmd[1], cmd[0], frame).to_i
|
||||
case (cmd[3] || :linear)
|
||||
when :linear
|
||||
ret[0] = lerp(ret[0], cmd[2], cmd[1], cmd[0], frame).to_i
|
||||
else
|
||||
# TODO: Use an appropriate interpolation.
|
||||
end
|
||||
ret[1] = true # Interpolating
|
||||
break
|
||||
end
|
||||
|
||||
@@ -1,44 +1,124 @@
|
||||
#===============================================================================
|
||||
# TODO
|
||||
# NOTE: z values:
|
||||
# -200 = backdrop.
|
||||
# -199 = side bases
|
||||
# -198 = battler shadows.
|
||||
# 0 +/-50 = background focus, foe side background.
|
||||
# 900, 800, 700... +/-50 = foe battlers.
|
||||
# 1000 +/-50 = foe side foreground, player side background.
|
||||
# 1100, 1200, 1300... +/-50 = player battlers.
|
||||
# 2000 +/-50 = player side foreground, foreground focus.
|
||||
# 9999+ = UI
|
||||
|
||||
# TODO: Should the canvas be able to show boxes/faded sprites of particles from
|
||||
# the previous keyframe? I suppose ideally, but don't worry about it.
|
||||
# TODO: Battler/particle sprites should be their own class, which combine a
|
||||
# sprite and a target-dependent coloured frame. Alternatively, have the
|
||||
# frame be a separate sprite but only draw it around the currently
|
||||
# selected particle(s).
|
||||
# TODO: Ideally refresh the canvas while editing a particle's property in the
|
||||
# :commands_pane component (e.g. moving a number slider but not finalising
|
||||
# it). Refresh a single particle. I don't think any other side pane needs
|
||||
# to refresh the canvas in the middle of changing a value. The new value
|
||||
# of a control in the middle of being changed isn't part of the particle's
|
||||
# data, so it'll need to be input manually somehow.
|
||||
#===============================================================================
|
||||
class AnimationEditor::Canvas < Sprite
|
||||
attr_reader :bg_name
|
||||
def initialize(viewport, anim, settings)
|
||||
super(viewport)
|
||||
@anim = anim
|
||||
@settings = settings
|
||||
@keyframe = 0
|
||||
@user_coords = []
|
||||
@target_coords = []
|
||||
@playing = false # TODO: What should this affect? Is it needed?
|
||||
initialize_background
|
||||
initialize_battlers
|
||||
initialize_particle_sprites
|
||||
refresh
|
||||
end
|
||||
|
||||
def initialize(viewport)
|
||||
super
|
||||
@bg_val = ""
|
||||
def initialize_background
|
||||
self.z = -200
|
||||
# NOTE: The background graphic is self.bitmap.
|
||||
# TODO: Add second (flipped) background graphic, for screen shake commands.
|
||||
player_base_pos = Battle::Scene.pbBattlerPosition(0)
|
||||
@player_base = IconSprite.new(*player_base_pos, viewport)
|
||||
@player_base.z = 1
|
||||
@player_base.z = -199
|
||||
foe_base_pos = Battle::Scene.pbBattlerPosition(1)
|
||||
@foe_base = IconSprite.new(*foe_base_pos, viewport)
|
||||
@foe_base.z = 1
|
||||
@foe_base.z = -199
|
||||
@message_bar_sprite = Sprite.new(viewport)
|
||||
@message_bar_sprite.z = 999
|
||||
@message_bar_sprite.z = 9999
|
||||
end
|
||||
|
||||
def initialize_battlers
|
||||
@battler_sprites = []
|
||||
end
|
||||
|
||||
def initialize_particle_sprites
|
||||
@particle_sprites = []
|
||||
end
|
||||
|
||||
def dispose
|
||||
@message_bar_sprite.dispose
|
||||
@user_bitmap_front&.dispose
|
||||
@user_bitmap_back&.dispose
|
||||
@target_bitmap_front&.dispose
|
||||
@target_bitmap_back&.dispose
|
||||
@player_base.dispose
|
||||
@foe_base.dispose
|
||||
@message_bar_sprite.dispose
|
||||
@battler_sprites.each { |s| s.dispose if s && !s.disposed? }
|
||||
@battler_sprites.clear
|
||||
@particle_sprites.each do |s|
|
||||
if s.is_a?(Array)
|
||||
s.each { |s2| s2.dispose if s2 && !s2.disposed? }
|
||||
else
|
||||
s.dispose if s && !s.disposed?
|
||||
end
|
||||
end
|
||||
@particle_sprites.clear
|
||||
super
|
||||
end
|
||||
|
||||
def bg_name=(val)
|
||||
return if @bg_name == val
|
||||
@bg_name = val
|
||||
# TODO: Make the choice of background graphics match the in-battle one in
|
||||
# def pbCreateBackdropSprites. Ideally make that method a class method
|
||||
# so the canvas can use it rather than duplicate it.
|
||||
self.bitmap = RPG::Cache.load_bitmap("Graphics/Battlebacks/", @bg_name + "_bg")
|
||||
@player_base.setBitmap("Graphics/Battlebacks/" + @bg_name + "_base0")
|
||||
@player_base.ox = @player_base.bitmap.width / 2
|
||||
@player_base.oy = @player_base.bitmap.height
|
||||
@foe_base.setBitmap("Graphics/Battlebacks/" + @bg_name + "_base1")
|
||||
@foe_base.ox = @foe_base.bitmap.width / 2
|
||||
@foe_base.oy = @foe_base.bitmap.height / 2
|
||||
@message_bar_sprite.bitmap = RPG::Cache.load_bitmap("Graphics/Battlebacks/", @bg_name + "_message")
|
||||
@message_bar_sprite.y = Settings::SCREEN_HEIGHT - @message_bar_sprite.height
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# Returns whether the user is on the foe's (non-player's) side.
|
||||
def sides_swapped?
|
||||
return @settings[:user_opposes] || [:opp_move, :opp_common].include?(@anim[:type])
|
||||
end
|
||||
|
||||
# index is a battler index (even for player's side, odd for foe's side)
|
||||
def side_size(index)
|
||||
side = index % 2
|
||||
side = (side + 1) % 2 if sides_swapped?
|
||||
return @settings[:side_sizes][side]
|
||||
end
|
||||
|
||||
def user_index
|
||||
ret = @settings[:user_index]
|
||||
ret += 1 if sides_swapped?
|
||||
return ret
|
||||
end
|
||||
|
||||
def target_indices
|
||||
ret = @settings[:target_indices].clone
|
||||
if sides_swapped?
|
||||
ret.length.times do |i|
|
||||
ret[i] += (ret[i].even?) ? 1 : -1
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def position_empty?(index)
|
||||
return user_index != index && !target_indices.include?(index)
|
||||
end
|
||||
|
||||
def keyframe=(val)
|
||||
return if @keyframe == val || val < 0
|
||||
@keyframe = val
|
||||
refresh
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@@ -53,9 +133,304 @@ class AnimationEditor::Canvas < Sprite
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def repaint
|
||||
def prepare_to_play_animation
|
||||
# TODO: Hide particle sprites, set battler sprites to starting positions so
|
||||
# that the animation can play properly. Also need a way to end this
|
||||
# override after the animation finishes playing. This method does not
|
||||
# literally play the animation; the main editor screen or playback
|
||||
# control does that.
|
||||
@playing = true
|
||||
end
|
||||
|
||||
def end_playing_animation
|
||||
@playing = false
|
||||
refresh
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def refresh_bg_graphics
|
||||
return if @bg_name && @bg_name == @settings[:canvas_bg]
|
||||
@bg_name = @settings[:canvas_bg]
|
||||
# TODO: Make the choice of background graphics match the in-battle one in
|
||||
# def pbCreateBackdropSprites. Ideally make that method a class method
|
||||
# so the canvas can use it rather than duplicate it.
|
||||
self.bitmap = RPG::Cache.load_bitmap("Graphics/Battlebacks/", @bg_name + "_bg")
|
||||
@player_base.setBitmap("Graphics/Battlebacks/" + @bg_name + "_base0")
|
||||
@player_base.ox = @player_base.bitmap.width / 2
|
||||
@player_base.oy = @player_base.bitmap.height
|
||||
@foe_base.setBitmap("Graphics/Battlebacks/" + @bg_name + "_base1")
|
||||
@foe_base.ox = @foe_base.bitmap.width / 2
|
||||
@foe_base.oy = @foe_base.bitmap.height / 2
|
||||
@message_bar_sprite.bitmap = RPG::Cache.load_bitmap("Graphics/Battlebacks/", @bg_name + "_message")
|
||||
@message_bar_sprite.y = Settings::SCREEN_HEIGHT - @message_bar_sprite.height
|
||||
end
|
||||
|
||||
# TODO: def refresh_box_display which checks @settings for whether boxes
|
||||
# should be drawn around sprites.
|
||||
|
||||
# TODO: Create shadow sprites?
|
||||
def ensure_battler_sprites
|
||||
if !@side_size0 || @side_size0 != side_size(0)
|
||||
@battler_sprites.each_with_index { |s, i| s.dispose if i.even? && s && !s.disposed? }
|
||||
@side_size0 = side_size(0)
|
||||
@side_size0.times do |i|
|
||||
next if position_empty?(i * 2)
|
||||
@battler_sprites[i * 2] = Sprite.new(self.viewport)
|
||||
end
|
||||
end
|
||||
if !@side_size1 || @side_size1 != side_size(1)
|
||||
@battler_sprites.each_with_index { |s, i| s.dispose if i.odd? && s && !s.disposed? }
|
||||
@side_size1 = side_size(1)
|
||||
@side_size1.times do |i|
|
||||
next if position_empty?((i * 2) + 1)
|
||||
@battler_sprites[(i * 2) + 1] = Sprite.new(self.viewport)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_battler_graphics
|
||||
if !@user_sprite_name || !@user_sprite_name || @user_sprite_name != @settings[:user_sprite_name]
|
||||
@user_sprite_name = @settings[:user_sprite_name]
|
||||
@user_bitmap_front&.dispose
|
||||
@user_bitmap_back&.dispose
|
||||
@user_bitmap_front = RPG::Cache.load_bitmap("Graphics/Pokemon/Front/", @user_sprite_name)
|
||||
@user_bitmap_back = RPG::Cache.load_bitmap("Graphics/Pokemon/Back/", @user_sprite_name)
|
||||
end
|
||||
if !@target_bitmap_front || !@target_sprite_name || @target_sprite_name != @settings[:target_sprite_name]
|
||||
@target_sprite_name = @settings[:target_sprite_name]
|
||||
@target_bitmap_front&.dispose
|
||||
@target_bitmap_back&.dispose
|
||||
@target_bitmap_front = RPG::Cache.load_bitmap("Graphics/Pokemon/Front/", @target_sprite_name)
|
||||
@target_bitmap_back = RPG::Cache.load_bitmap("Graphics/Pokemon/Back/", @target_sprite_name)
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_battler_positions
|
||||
user_idx = user_index
|
||||
@user_coords = recalculate_battler_position(
|
||||
user_idx, side_size(user_idx), @user_sprite_name,
|
||||
(user_idx.even?) ? @user_bitmap_back : @user_bitmap_front
|
||||
)
|
||||
target_indices.each do |target_idx|
|
||||
@target_coords[target_idx] = recalculate_battler_position(
|
||||
target_idx, side_size(target_idx), @target_sprite_name,
|
||||
(target_idx.even?) ? @target_bitmap_back : @target_bitmap_front
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def recalculate_battler_position(index, size, sprite_name, btmp)
|
||||
spr = Sprite.new(self.viewport)
|
||||
spr.x, spr.y = Battle::Scene.pbBattlerPosition(index, size)
|
||||
data = GameData::Species.get_species_form(sprite_name, 0) # Form 0
|
||||
data.apply_metrics_to_sprite(spr, index) if data
|
||||
return [spr.x, spr.y - (btmp.height / 2)]
|
||||
end
|
||||
|
||||
def create_particle_sprite(index, target_idx = -1)
|
||||
if target_idx >= 0
|
||||
if @particle_sprites[index].is_a?(Array)
|
||||
return if @particle_sprites[index][target_idx] && !@particle_sprites[index][target_idx].disposed?
|
||||
else
|
||||
@particle_sprites[index].dispose if @particle_sprites[index] && !@particle_sprites[index].disposed?
|
||||
@particle_sprites[index] = []
|
||||
end
|
||||
@particle_sprites[index][target_idx] = Sprite.new(self.viewport)
|
||||
else
|
||||
if @particle_sprites[index].is_a?(Array)
|
||||
@particle_sprites[index].each { |s| s.dispose if s && !s.disposed? }
|
||||
@particle_sprites[index] = nil
|
||||
else
|
||||
return if @particle_sprites[index] && !@particle_sprites[index].disposed?
|
||||
end
|
||||
@particle_sprites[index] = Sprite.new(self.viewport)
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_sprite(index, target_idx = -1)
|
||||
particle = @anim[:particles][index]
|
||||
return if !particle
|
||||
# Get sprite
|
||||
case particle[:name]
|
||||
when "SE"
|
||||
return
|
||||
when "User"
|
||||
spr = @battler_sprites[user_index]
|
||||
raise _INTL("Sprite for particle {1} not found somehow (battler index {2}).",
|
||||
particle[:name], user_index) if !spr
|
||||
when "Target"
|
||||
spr = @battler_sprites[target_idx]
|
||||
raise _INTL("Sprite for particle {1} not found somehow (battler index {2}).",
|
||||
particle[:name], target_idx) if !spr
|
||||
else
|
||||
create_particle_sprite(index, target_idx)
|
||||
if target_idx >= 0
|
||||
spr = @particle_sprites[index][target_idx]
|
||||
else
|
||||
spr = @particle_sprites[index]
|
||||
end
|
||||
end
|
||||
# Calculate all values of particle at the current keyframe
|
||||
values = AnimationEditor::ParticleDataHelper.get_all_keyframe_particle_values(particle, @keyframe)
|
||||
values.each_pair do |property, val|
|
||||
values[property] = val[0]
|
||||
end
|
||||
# Set visibility
|
||||
spr.visible = values[:visible]
|
||||
return if !spr.visible
|
||||
# Set opacity
|
||||
spr.opacity = values[:opacity]
|
||||
# Set coordinates
|
||||
spr.x = values[:x]
|
||||
spr.y = values[:y]
|
||||
case particle[:focus]
|
||||
when :foreground, :midground, :background
|
||||
when :user
|
||||
spr.x += @user_coords[0]
|
||||
spr.y += @user_coords[1]
|
||||
when :target
|
||||
spr.x += @target_coords[target_idx][0]
|
||||
spr.y += @target_coords[target_idx][1]
|
||||
when :user_and_target
|
||||
user_pos = @user_coords
|
||||
target_pos = @target_coords[target_idx]
|
||||
distance = GameData::Animation::USER_AND_TARGET_SEPARATION
|
||||
spr.x = user_pos[0] + ((values[:x].to_f / distance[0]) * (target_pos[0] - user_pos[0])).to_i
|
||||
spr.y = user_pos[1] + ((values[:y].to_f / distance[1]) * (target_pos[1] - user_pos[1])).to_i
|
||||
when :user_side_foreground, :user_side_background
|
||||
base_coords = Battle::Scene.pbBattlerPosition(target_idx)
|
||||
spr.x += base_coords[0]
|
||||
spr.y += base_coords[1]
|
||||
when :target_side_foreground, :target_side_background
|
||||
base_coords = Battle::Scene.pbBattlerPosition(target_idx)
|
||||
spr.x += base_coords[0]
|
||||
spr.y += base_coords[1]
|
||||
end
|
||||
# Set graphic and ox/oy (may also alter y coordinate)
|
||||
case particle[:graphic]
|
||||
when "USER", "USER_OPP", "USER_FRONT", "USER_BACK",
|
||||
"TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"
|
||||
case particle[:graphic]
|
||||
when "USER"
|
||||
spr.bitmap = (user_index.even?) ? @user_bitmap_back : @user_bitmap_front
|
||||
when "USER_OPP"
|
||||
spr.bitmap = (user_index.even?) ? @user_bitmap_front : @user_bitmap_back
|
||||
when "USER_FRONT"
|
||||
spr.bitmap = @user_bitmap_front
|
||||
when "USER_BACK"
|
||||
spr.bitmap = @user_bitmap_back
|
||||
when "TARGET"
|
||||
if target_idx < 0
|
||||
raise _INTL("Particle {1} was given a graphic of \"TARGET\" but its focus doesn't include a target.",
|
||||
particle[:name])
|
||||
end
|
||||
spr.bitmap = (target_idx.even?) ? @target_bitmap_back : @target_bitmap_front
|
||||
when "TARGET_OPP"
|
||||
if target_idx < 0
|
||||
raise _INTL("Particle {1} was given a graphic of \"TARGET_OPP\" but its focus doesn't include a target.",
|
||||
particle[:name])
|
||||
end
|
||||
spr.bitmap = (target_idx.even?) ? @target_bitmap_front : @target_bitmap_back
|
||||
when "TARGET_FRONT"
|
||||
if target_idx < 0
|
||||
raise _INTL("Particle {1} was given a graphic of \"TARGET_FRONT\" but its focus doesn't include a target.",
|
||||
particle[:name])
|
||||
end
|
||||
spr.bitmap = @target_bitmap_front
|
||||
when "TARGET_BACK"
|
||||
if target_idx < 0
|
||||
raise _INTL("Particle {1} was given a graphic of \"TARGET_BACK\" but its focus doesn't include a target.",
|
||||
particle[:name])
|
||||
end
|
||||
spr.bitmap = @target_bitmap_back
|
||||
end
|
||||
spr.ox = spr.bitmap.width / 2
|
||||
spr.oy = spr.bitmap.height
|
||||
spr.y += spr.bitmap.height / 2
|
||||
else
|
||||
spr.bitmap = RPG::Cache.load_bitmap("Graphics/Battle animations/", particle[:graphic])
|
||||
# TODO: Set the oy to spr.bitmap.height if particle[:graphic] has
|
||||
# something special in it (don't know what yet).
|
||||
if spr.bitmap.width > spr.bitmap.height * 2
|
||||
spr.src_rect.set(values[:frame] * spr.bitmap.height, 0, spr.bitmap.height, spr.bitmap.height)
|
||||
spr.ox = spr.bitmap.height / 2
|
||||
spr.oy = spr.bitmap.height / 2
|
||||
else
|
||||
spr.src_rect.set(0, 0, spr.bitmap.width, spr.bitmap.height)
|
||||
spr.ox = spr.bitmap.width / 2
|
||||
spr.oy = spr.bitmap.height / 2
|
||||
end
|
||||
end
|
||||
# Set z (priority)
|
||||
spr.z = values[:z]
|
||||
case particle[:focus]
|
||||
when :foreground
|
||||
spr.z += 2000
|
||||
when :midground
|
||||
spr.z += 1000
|
||||
when :background
|
||||
# NOTE: No change.
|
||||
when :user
|
||||
spr.z += 1000 + ((100 * ((user_index / 2) + 1)) * (user_index.even? ? 1 : -1))
|
||||
when :target
|
||||
spr.z += 1000 + ((100 * ((target_idx / 2) + 1)) * (target_idx.even? ? 1 : -1))
|
||||
when :user_and_target
|
||||
user_pos = 1000 + ((100 * ((user_index / 2) + 1)) * (user_index.even? ? 1 : -1))
|
||||
target_pos = 1000 + ((100 * ((target_idx / 2) + 1)) * (target_idx.even? ? 1 : -1))
|
||||
distance = GameData::Animation::USER_AND_TARGET_SEPARATION[2]
|
||||
spr.z = user_pos + ((values[:z].to_f / distance) * (target_pos - user_pos)).to_i
|
||||
when :user_side_foreground, :target_side_foreground
|
||||
this_idx = (particle[:focus] == :user_side_foreground) ? user_index : target_idx
|
||||
spr.z += 1000
|
||||
spr.z += 1000 if this_idx.even? # On player's side
|
||||
when :user_side_background, :target_side_background
|
||||
this_idx = (particle[:focus] == :user_side_background) ? user_index : target_idx
|
||||
spr.z += 1000 if this_idx.even? # On player's side
|
||||
end
|
||||
# Set various other properties
|
||||
spr.zoom_x = values[:zoom_x] / 100.0
|
||||
spr.zoom_y = values[:zoom_y] / 100.0
|
||||
spr.angle = values[:angle]
|
||||
spr.mirror = values[:flip]
|
||||
spr.blend_type = values[:blending]
|
||||
# Set color and tone
|
||||
spr.color.set(values[:color_red], values[:color_green], values[:color_blue], values[:color_alpha])
|
||||
spr.tone.set(values[:tone_red], values[:tone_green], values[:tone_blue], values[:tone_gray])
|
||||
end
|
||||
|
||||
def refresh_particle(index)
|
||||
target_indices.each { |target_idx| refresh_sprite(index, target_idx) }
|
||||
end
|
||||
|
||||
def refresh
|
||||
refresh_bg_graphics
|
||||
ensure_battler_sprites
|
||||
refresh_battler_graphics
|
||||
refresh_battler_positions
|
||||
@battler_sprites.each { |s| s.visible = false if s && !s.disposed? }
|
||||
@particle_sprites.each do |s|
|
||||
if s.is_a?(Array)
|
||||
s.each { |s2| s2.visible = false if s2 && !s2.disposed? }
|
||||
else
|
||||
s.visible = false if s && !s.disposed?
|
||||
end
|
||||
end
|
||||
@anim[:particles].each_with_index do |particle, i|
|
||||
if GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(particle[:focus])
|
||||
refresh_particle(i)
|
||||
else
|
||||
refresh_sprite(i) if particle[:name] != "SE"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# TODO: def update_input. Includes def pbSpriteHitTest equivalent.
|
||||
|
||||
def update
|
||||
# TODO: Update input (mouse clicks, dragging particles).
|
||||
# TODO: Keyboard shortcuts?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,10 +19,16 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
|
||||
INTERP_LINE_Y = (ROW_HEIGHT / 2) - (INTERP_LINE_HEIGHT / 2)
|
||||
DURATION_BUFFER = 20 # Extra keyframes shown after the animation's end
|
||||
CONTROL_BG_COLORS = {
|
||||
:user => Color.new(96, 248, 96), # Green
|
||||
:target => Color.new(248, 96, 96), # Red
|
||||
:user_and_target => Color.new(248, 248, 96), # Yellow
|
||||
:screen => Color.new(128, 160, 248) # Blue
|
||||
:foreground => Color.new(128, 160, 248), # Blue
|
||||
:midground => Color.new(128, 160, 248), # Blue
|
||||
:background => Color.new(128, 160, 248), # Blue
|
||||
:user => Color.new(96, 248, 96), # Green
|
||||
:target => Color.new(248, 96, 96), # Red
|
||||
:user_and_target => Color.new(248, 248, 96), # Yellow
|
||||
:user_side_foreground => Color.new(128, 248, 248), # Cyan
|
||||
:user_side_background => Color.new(128, 248, 248), # Cyan
|
||||
:target_side_foreground => Color.new(128, 248, 248), # Cyan
|
||||
:target_side_background => Color.new(128, 248, 248) # Cyan
|
||||
}
|
||||
SE_CONTROL_BG = Color.gray
|
||||
|
||||
|
||||
Reference in New Issue
Block a user