Anim Editor: added FoeFlip property, Space to play, S to swap sides, P to show/hide property lines for selected particle

This commit is contained in:
Maruno17
2024-04-18 22:35:15 +01:00
parent 15033d6114
commit 4480def33c
23 changed files with 264 additions and 161 deletions

View File

@@ -24,6 +24,8 @@ class UIControls::BaseControl < BitmapSprite
invalidate
end
#-----------------------------------------------------------------------------
def width
return self.bitmap.width
end
@@ -32,6 +34,8 @@ class UIControls::BaseControl < BitmapSprite
return self.bitmap.height
end
#-----------------------------------------------------------------------------
def mouse_pos
mouse_coords = Mouse.getMousePos
return nil, nil if !mouse_coords
@@ -40,10 +44,6 @@ class UIControls::BaseControl < BitmapSprite
return ret_x, ret_y
end
def set_interactive_rects
@interactions = {}
end
def mouse_in_control?
return false if !@interactions || @interactions.empty?
mouse_x, mouse_y = mouse_pos
@@ -51,8 +51,6 @@ class UIControls::BaseControl < BitmapSprite
return @interactions.any? { |area, rect| rect.contains?(mouse_x, mouse_y) }
end
#-----------------------------------------------------------------------------
def disabled?
return @disabled
end
@@ -102,6 +100,12 @@ class UIControls::BaseControl < BitmapSprite
#-----------------------------------------------------------------------------
def set_interactive_rects
@interactions = {}
end
#-----------------------------------------------------------------------------
def draw_text(this_bitmap, text_x, text_y, this_text)
text_size = this_bitmap.text_size(this_text)
this_bitmap.draw_text(text_x, text_y, text_size.width, text_size.height, this_text, 0)

View File

@@ -10,6 +10,8 @@ class UIControls::Label < UIControls::BaseControl
@header = false
end
#-----------------------------------------------------------------------------
def text=(value)
@text = value
refresh
@@ -24,6 +26,8 @@ class UIControls::Label < UIControls::BaseControl
return self.bitmap.text_size(@text).width
end
#-----------------------------------------------------------------------------
def refresh
super
if @header

View File

@@ -6,7 +6,6 @@ class UIControls::Checkbox < UIControls::BaseControl
CHECKBOX_WIDTH = 40
CHECKBOX_HEIGHT = 24
CHECKBOX_FILL_SIZE = CHECKBOX_HEIGHT - 4
UNCHECKED_COLOR = Color.gray
CHECKED_COLOR = Color.new(48, 192, 48) # Darkish green
@@ -15,12 +14,16 @@ class UIControls::Checkbox < UIControls::BaseControl
@value = value
end
#-----------------------------------------------------------------------------
def value=(new_value)
return if @value == new_value
@value = new_value
invalidate
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@checkbox_rect = Rect.new(CHECKBOX_X, (height - CHECKBOX_HEIGHT) / 2,
CHECKBOX_WIDTH, CHECKBOX_HEIGHT)

View File

@@ -20,6 +20,8 @@ class UIControls::TextBox < UIControls::BaseControl
@blacklist = []
end
#-----------------------------------------------------------------------------
def value
return @value.dup
end
@@ -60,36 +62,6 @@ class UIControls::TextBox < UIControls::BaseControl
invalidate
end
def set_interactive_rects
@text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
[@box_width, width - (TEXT_BOX_X * 2)].min, TEXT_BOX_HEIGHT)
@interactions = {
:text_box => @text_box_rect
}
end
#-----------------------------------------------------------------------------
def disabled?
val = (@value.respond_to?("strip!")) ? @value.strip : @value
return true if @blacklist.include?(val)
return super
end
def busy?
return @cursor_pos >= 0 if @captured_area == :text_box
return super
end
def reset_interaction
@cursor_pos = -1
@display_pos = 0
@cursor_timer = nil
@initial_value = nil
Input.text_input = false
invalidate
end
#-----------------------------------------------------------------------------
def get_cursor_index_from_mouse_position
@@ -107,6 +79,36 @@ class UIControls::TextBox < UIControls::BaseControl
return @value.to_s.length
end
def disabled?
val = (@value.respond_to?("strip!")) ? @value.strip : @value
return true if @blacklist.include?(val)
return super
end
def busy?
return @cursor_pos >= 0 if @captured_area == :text_box
return super
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
[@box_width, width - (TEXT_BOX_X * 2)].min, TEXT_BOX_HEIGHT)
@interactions = {
:text_box => @text_box_rect
}
end
def reset_interaction
@cursor_pos = -1
@display_pos = 0
@cursor_timer = nil
@initial_value = nil
Input.text_input = false
invalidate
end
def reset_display_pos
box_width = @text_box_rect.width - (TEXT_BOX_PADDING * 2)
char_widths = []

View File

@@ -7,7 +7,6 @@ class UIControls::NumberSlider < UIControls::BaseControl
PLUS_MINUS_SIZE = 16
SLIDER_PADDING = 6 # Gap between sides of interactive area for slider and drawn slider bar
MINUS_X = 0
SLIDER_X = MINUS_X + PLUS_MINUS_SIZE + SLIDER_PADDING
SLIDER_LENGTH = 128
@@ -21,6 +20,8 @@ class UIControls::NumberSlider < UIControls::BaseControl
self.value = value
end
#-----------------------------------------------------------------------------
def value=(new_value)
old_val = @value
@value = new_value.to_i.clamp(self.min_value, self.max_value)
@@ -41,6 +42,8 @@ class UIControls::NumberSlider < UIControls::BaseControl
self.invalidate
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@slider_rect = Rect.new(SLIDER_X - SLIDER_PADDING, (self.height - PLUS_MINUS_SIZE) / 2, SLIDER_LENGTH + (SLIDER_PADDING * 2), PLUS_MINUS_SIZE)
@minus_rect = Rect.new(MINUS_X, (self.height - PLUS_MINUS_SIZE) / 2, PLUS_MINUS_SIZE, PLUS_MINUS_SIZE)

View File

@@ -7,7 +7,6 @@ class UIControls::NumberTextBox < UIControls::TextBox
PLUS_MINUS_SIZE = 16
CONTROL_PADDING = 2 # Gap between buttons and text box
MINUS_X = 0
TEXT_BOX_X = MINUS_X + PLUS_MINUS_SIZE + CONTROL_PADDING
TEXT_BOX_WIDTH = 64
@@ -21,6 +20,8 @@ class UIControls::NumberTextBox < UIControls::TextBox
self.value = value
end
#-----------------------------------------------------------------------------
def value=(new_value)
old_val = @value.to_i
@value = new_value.to_i.clamp(self.min_value, self.max_value)
@@ -49,6 +50,8 @@ class UIControls::NumberTextBox < UIControls::TextBox
invalidate
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
TEXT_BOX_WIDTH, TEXT_BOX_HEIGHT)
@@ -61,8 +64,6 @@ class UIControls::NumberTextBox < UIControls::TextBox
}
end
#-----------------------------------------------------------------------------
def reset_interaction
super
self.value = @value # Turn value back into a number and clamp it

View File

@@ -16,21 +16,12 @@ class UIControls::Button < UIControls::BaseControl
@highlight = false
end
#-----------------------------------------------------------------------------
def set_fixed_size
@fixed_size = true
end
def set_interactive_rects
@interactions&.clear
button_width = (@fixed_size) ? width - (BUTTON_X * 2) : self.bitmap.text_size(@text).width + (BUTTON_PADDING * 2)
button_height = (@fixed_size) ? height - (2 * BUTTON_Y) : BUTTON_HEIGHT
button_height = [button_height, height - (2 * BUTTON_Y)].min
@button_rect = Rect.new(BUTTON_X, (height - button_height) / 2, button_width, button_height)
@interactions = {
:button => @button_rect
}
end
def set_text(val)
return if @text == val
@text = val
@@ -38,6 +29,8 @@ class UIControls::Button < UIControls::BaseControl
invalidate
end
#-----------------------------------------------------------------------------
def disabled?
return highlighted? || super
end
@@ -70,6 +63,19 @@ class UIControls::Button < UIControls::BaseControl
#-----------------------------------------------------------------------------
def set_interactive_rects
@interactions&.clear
button_width = (@fixed_size) ? width - (BUTTON_X * 2) : self.bitmap.text_size(@text).width + (BUTTON_PADDING * 2)
button_height = (@fixed_size) ? height - (2 * BUTTON_Y) : BUTTON_HEIGHT
button_height = [button_height, height - (2 * BUTTON_Y)].min
@button_rect = Rect.new(BUTTON_X, (height - button_height) / 2, button_width, button_height)
@interactions = {
:button => @button_rect
}
end
#-----------------------------------------------------------------------------
def refresh
super
if highlighted?

View File

@@ -12,6 +12,8 @@ class UIControls::BitmapButton < UIControls::Button
@disabled_bitmap = disabled_bitmap
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@interactions&.clear
@button_rect = Rect.new(0, 0, width, height)

View File

@@ -6,7 +6,6 @@ class UIControls::List < UIControls::BaseControl
ROW_HEIGHT = 24
TEXT_PADDING_X = 4
TEXT_OFFSET_Y = 3
SELECTED_ROW_COLOR = Color.new(216, 192, 32) # Dark yellow
def initialize(width, height, viewport, values = [])
@@ -30,6 +29,8 @@ class UIControls::List < UIControls::BaseControl
super
end
#-----------------------------------------------------------------------------
def x=(new_val)
super(new_val)
@scrollbar.x = new_val + width - UIControls::Scrollbar::SLIDER_WIDTH - BORDER_THICKNESS
@@ -64,6 +65,17 @@ class UIControls::List < UIControls::BaseControl
invalidate
end
# Returns the ID of the selected row.
def value
return nil if @selected < 0
if @values.is_a?(Array)
return (@values[@selected].is_a?(Array)) ? @values[@selected][0] : @selected
elsif @values.is_a?(Hash)
return @values.keys[@selected]
end
return nil
end
def top_row=(val)
old_val = @top_row
@top_row = val
@@ -81,16 +93,7 @@ class UIControls::List < UIControls::BaseControl
invalidate
end
# Returns the ID of the selected row.
def value
return nil if @selected < 0
if @values.is_a?(Array)
return (@values[@selected].is_a?(Array)) ? @values[@selected][0] : @selected
elsif @values.is_a?(Hash)
return @values.keys[@selected]
end
return nil
end
#-----------------------------------------------------------------------------
def mouse_in_control?
mouse_x, mouse_y = mouse_pos
@@ -100,6 +103,12 @@ class UIControls::List < UIControls::BaseControl
return false
end
def busy?
return !@captured_area.nil?
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@interactions = {}
@values.length.times do |i|
@@ -112,12 +121,6 @@ class UIControls::List < UIControls::BaseControl
#-----------------------------------------------------------------------------
def busy?
return !@captured_area.nil?
end
#-----------------------------------------------------------------------------
def draw_area_highlight
# If a row is captured, it will automatically be selected and the selection
# colour will be drawn over the highlight. There's no point drawing a

View File

@@ -26,23 +26,17 @@ class UIControls::DropdownList < UIControls::BaseControl
super
end
def value=(new_value)
return if @value == new_value
@value = new_value
invalidate
end
#-----------------------------------------------------------------------------
def values=(new_vals)
@options = new_vals
@dropdown_menu.values = @options if @dropdown_menu
end
def set_interactive_rects
@button_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
[@box_width, width - (TEXT_BOX_X * 2)].min, TEXT_BOX_HEIGHT)
@interactions = {
:button => @button_rect
}
def value=(new_value)
return if @value == new_value
@value = new_value
invalidate
end
#-----------------------------------------------------------------------------
@@ -54,6 +48,14 @@ class UIControls::DropdownList < UIControls::BaseControl
#-----------------------------------------------------------------------------
def set_interactive_rects
@button_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
[@box_width, width - (TEXT_BOX_X * 2)].min, TEXT_BOX_HEIGHT)
@interactions = {
:button => @button_rect
}
end
def make_dropdown_menu
menu_height = (UIControls::List::ROW_HEIGHT * [@options.length, @max_rows].min) + (UIControls::List::BORDER_THICKNESS * 2)
# Draw menu's background

View File

@@ -26,21 +26,13 @@ class UIControls::TextBoxDropdownList < UIControls::TextBox
super
end
#-----------------------------------------------------------------------------
def values=(new_vals)
@options = new_vals
@dropdown_menu.values = @options if @dropdown_menu
end
def set_interactive_rects
@text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
[@box_width, width - (TEXT_BOX_X * 2) - BUTTON_WIDTH].min, TEXT_BOX_HEIGHT)
@button_rect = Rect.new(BUTTON_X, @text_box_rect.y, BUTTON_WIDTH, BUTTON_HEIGHT)
@interactions = {
:text_box => @text_box_rect,
:button => @button_rect
}
end
#-----------------------------------------------------------------------------
def busy?
@@ -50,6 +42,16 @@ class UIControls::TextBoxDropdownList < UIControls::TextBox
#-----------------------------------------------------------------------------
def set_interactive_rects
@text_box_rect = Rect.new(TEXT_BOX_X, (height - TEXT_BOX_HEIGHT) / 2,
[@box_width, width - (TEXT_BOX_X * 2) - BUTTON_WIDTH].min, TEXT_BOX_HEIGHT)
@button_rect = Rect.new(BUTTON_X, @text_box_rect.y, BUTTON_WIDTH, BUTTON_HEIGHT)
@interactions = {
:text_box => @text_box_rect,
:button => @button_rect
}
end
def make_dropdown_menu
menu_height = (UIControls::List::ROW_HEIGHT * [@options.length, @max_rows].min) + (UIControls::List::BORDER_THICKNESS * 2)
# Draw menu's background

View File

@@ -2,6 +2,8 @@
#
#===============================================================================
class UIControls::Scrollbar < UIControls::BaseControl
attr_reader :slider_top
SLIDER_WIDTH = 16
WIDTH_PADDING = 0
SCROLL_DISTANCE = 16
@@ -9,8 +11,6 @@ class UIControls::Scrollbar < UIControls::BaseControl
SLIDER_COLOR = Color.black
GRAB_COLOR = HOVER_COLOR # Cyan
attr_reader :slider_top
def initialize(x, y, size, viewport, horizontal = false, always_visible = false)
if horizontal
super(size, SLIDER_WIDTH, viewport)
@@ -28,18 +28,7 @@ class UIControls::Scrollbar < UIControls::BaseControl
self.visible = @always_visible
end
def position
return 0 if @range <= @tray_size
return (@range - @tray_size) * @slider_top / (@tray_size - @slider_size)
end
def minimum?
return @slider_top <= 0
end
def maximum?
return @slider_top >= @tray_size - @slider_size
end
#-----------------------------------------------------------------------------
# Range is the total size of the large area that the scrollbar is able to
# show part of.
@@ -68,6 +57,21 @@ class UIControls::Scrollbar < UIControls::BaseControl
invalidate if @slider_top != old_val
end
def position
return 0 if @range <= @tray_size
return (@range - @tray_size) * @slider_top / (@tray_size - @slider_size)
end
def minimum?
return @slider_top <= 0
end
def maximum?
return @slider_top >= @tray_size - @slider_size
end
#-----------------------------------------------------------------------------
def set_interactive_rects
@interactions = {}
if @horizontal

View File

@@ -66,6 +66,7 @@ module GameData
"Focus" => [:focus, "e", FOCUS_TYPES],
"FoeInvertX" => [:foe_invert_x, "b"],
"FoeInvertY" => [:foe_invert_y, "b"],
"FoeFlip" => [:foe_flip, "b"],
# All properties below are "SetXYZ" or "MoveXYZ". "SetXYZ" has the
# keyframe and the value, and "MoveXYZ" has the keyframe, duration and the
# value. All have "^" in their schema. "SetXYZ" is turned into "MoveXYZ"
@@ -115,7 +116,8 @@ module GameData
:graphic => "",
:focus => :foreground,
:foe_invert_x => false,
:foe_invert_y => false
:foe_invert_y => false,
:foe_flip => false
}
# NOTE: Particles are invisible until their first command, and automatically
# become visible then. "User" and "Target" are visible from the start,

View File

@@ -198,6 +198,19 @@ module Compiler
raise _INTL("Particle \"{1}\" can't set \"FoeInvertY\" if its focus isn't exactly 1 thing.",
particle[:name]) + "\n" + FileLineData.linereport
end
if particle[:foe_flip]
raise _INTL("Particle \"{1}\" can't set \"FoeFlip\" if its focus isn't exactly 1 thing.",
particle[:name]) + "\n" + FileLineData.linereport
end
end
# Ensure that a particle with a user's/target's graphic doesn't have any
# :frame commands
if !["User", "Target", "SE"].include?(particle[:name]) &&
["USER", "USER_OPP", "USER_FRONT", "USER_BACK",
"TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"].include?(particle[:graphic]) &&
particle[:frame] && !particle[:frame].empty?
raise _INTL("Particle \"{1}\" can't have any \"Frame\" commands if its graphic is a Pokémon's sprite.",
particle[:name]) + "\n" + FileLineData.linereport
end
# Ensure that the same SE isn't played twice in the same frame
if particle[:name] == "SE"

View File

@@ -457,6 +457,7 @@ class AnimationEditor
break
end
end
break if Input.triggerex?(:SPACE)
break if anim_player.finished?
end
anim_player.dispose
@@ -741,6 +742,15 @@ class AnimationEditor
end
end
def update_input
if Input.triggerex?(:S)
@settings[:user_opposes] = !@settings[:user_opposes]
refresh
elsif Input.triggerex?(:SPACE)
@ready_to_play = true
end
end
def update
old_keyframe = keyframe
old_particle_index = particle_index
@@ -769,6 +779,7 @@ class AnimationEditor
break
end
end
update_input
end
#-----------------------------------------------------------------------------

View File

@@ -58,7 +58,7 @@ class AnimationEditor
ret = btn[0]
break
end
ret = :cancel if Input.trigger?(Input::BACK)
ret = :cancel if Input.triggerex?(:ESCAPE)
break if ret
buttons.each { |btn| btn[1].repaint }
end
@@ -110,7 +110,7 @@ class AnimationEditor
end
anim_properties.clear_changed
end
break if !anim_properties.busy? && Input.trigger?(Input::BACK)
break if !anim_properties.busy? && Input.triggerex?(:ESCAPE)
anim_properties.repaint
end
# Dispose and return
@@ -242,10 +242,13 @@ class AnimationEditor
end
graphic_chooser.clear_changed
end
ret = selected if !graphic_chooser.busy? && Input.trigger?(Input::BACK)
break if ret
graphic_chooser.repaint
end
if !graphic_chooser.busy? && Input.triggerex?(:ESCAPE)
ret = selected
break
end
end
# Dispose and return
bg_bitmap.dispose
@@ -315,13 +318,14 @@ class AnimationEditor
end
audio_chooser.clear_changed
end
if !audio_chooser.busy? && Input.trigger?(Input::BACK)
ret = selected
cance = true
end
break if ret
audio_chooser.repaint
end
if !audio_chooser.busy? && Input.triggerex?(:ESCAPE)
ret = selected
cancel = true
break
end
end
vol = (cancel) ? volume : audio_chooser.get_control(:volume).value
ptch = (cancel) ? pitch : audio_chooser.get_control(:pitch).value
@@ -332,5 +336,4 @@ class AnimationEditor
audio_chooser.visible = false
return [ret, vol, ptch]
end
end

View File

@@ -480,6 +480,12 @@ AnimationEditor::SidePanes.add_property(:particle_pane, :graphic, {
new_file = editor.choose_graphic_file(editor.anim[:particles][p_index][:graphic])
if editor.anim[:particles][p_index][:graphic] != new_file
editor.anim[:particles][p_index][:graphic] = new_file
if ["USER", "USER_BACK", "USER_FRONT", "USER_OPP",
"TARGET", "TARGET_FRONT", "TARGET_BACK", "TARGET_OPP"].include?(new_file)
editor.anim[:particles][p_index].delete(:frame)
editor.components[:particle_list].set_particles(editor.anim[:particles])
editor.refresh_component(:particle_list)
end
editor.refresh_component(:particle_pane)
editor.refresh_component(:canvas)
end
@@ -552,6 +558,20 @@ AnimationEditor::SidePanes.add_property(:particle_pane, :foe_invert_y, {
}
})
AnimationEditor::SidePanes.add_property(:particle_pane, :foe_flip, {
:new => proc { |pane, editor|
pane.add_labelled_checkbox(:foe_flip, _INTL("Flip Sprite"), false)
},
:refresh_value => proc { |control, editor|
focus = editor.anim[:particles][editor.particle_index][:focus]
if GameData::Animation::FOCUS_TYPES_WITH_USER.include?(focus) == GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(focus)
control.disable
else
control.enable
end
}
})
AnimationEditor::SidePanes.add_property(:particle_pane, :duplicate, {
:new => proc { |pane, editor|
pane.add_button(:duplicate, _INTL("Duplicate this particle"))

View File

@@ -194,7 +194,7 @@ class AnimationEditor::AnimationSelector
ret = btn[0]
break
end
ret = :cancel if Input.trigger?(Input::BACK)
ret = :cancel if Input.triggerex?(:ESCAPE)
break if ret
buttons.each { |btn| btn[1].repaint }
end

View File

@@ -475,6 +475,7 @@ class AnimationEditor::Canvas < Sprite
spr.zoom_y = values[:zoom_y] / 100.0
spr.angle = values[:angle]
spr.mirror = values[:flip]
spr.mirror = !spr.mirror if relative_to_index >= 0 && relative_to_index.odd? && particle[:foe_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])

View File

@@ -5,9 +5,9 @@ class AnimationEditor::PlayControls < UIControls::ControlsContainer
attr_reader :slowdown, :looping
ROW_HEIGHT = 28
PLAY_BUTTON_X = 241
PLAY_BUTTON_Y = 13
PLAY_BUTTON_SIZE = 22
PLAY_BUTTON_X = 231
PLAY_BUTTON_Y = 3
PLAY_BUTTON_SIZE = 42
LOOP_BUTTON_X = PLAY_BUTTON_X + PLAY_BUTTON_SIZE + 12
LOOP_BUTTON_Y = 16
LOOP_BUTTON_SIZE = 16
@@ -34,7 +34,8 @@ class AnimationEditor::PlayControls < UIControls::ControlsContainer
end
def dispose
@bitmaps.each { |b| b&.dispose }
@bitmaps.each_value { |b| b&.dispose }
@bitmaps.clear
super
end
@@ -43,12 +44,12 @@ class AnimationEditor::PlayControls < UIControls::ControlsContainer
def generate_button_bitmaps
@bitmaps = {}
play_button = Bitmap.new(PLAY_BUTTON_SIZE, PLAY_BUTTON_SIZE)
(PLAY_BUTTON_SIZE - 2).times do |j|
play_button.fill_rect(PLAY_BUTTON_SIZE / 4, j + 1, (j >= (PLAY_BUTTON_SIZE - 2) / 2) ? PLAY_BUTTON_SIZE - j : j + 3, 1, ICON_COLOR)
(PLAY_BUTTON_SIZE - 10).times do |j|
play_button.fill_rect(11, j + 5, (j >= (PLAY_BUTTON_SIZE - 10) / 2) ? PLAY_BUTTON_SIZE - j - 4 : j + 7, 1, ICON_COLOR)
end
@bitmaps[:play_button] = play_button
stop_button = Bitmap.new(PLAY_BUTTON_SIZE, PLAY_BUTTON_SIZE)
stop_button.fill_rect(4, 4, PLAY_BUTTON_SIZE - 8, PLAY_BUTTON_SIZE - 8, ICON_COLOR)
stop_button.fill_rect(8, 8, PLAY_BUTTON_SIZE - 16, PLAY_BUTTON_SIZE - 16, ICON_COLOR)
@bitmaps[:stop_button] = stop_button
# Loop button
play_once_button = Bitmap.new(LOOP_BUTTON_SIZE, LOOP_BUTTON_SIZE)
@@ -79,6 +80,22 @@ class AnimationEditor::PlayControls < UIControls::ControlsContainer
end
def add_play_controls
# Slowdown label
duration_label = UIControls::Label.new(200, ROW_HEIGHT, self.viewport, _INTL("Slowdown factor"))
duration_label.x = SLOWDOWN_BUTTON_X + (SLOWDOWN_FACTORS.length * (SLOWDOWN_BUTTON_WIDTH + SLOWDOWN_BUTTON_SPACING) / 2)
duration_label.x -= (duration_label.text_width / 2) + 5
duration_label.y = SLOWDOWN_LABEL_Y
@controls.push([:slowdown_label, duration_label])
# Slowdown factor buttons
SLOWDOWN_FACTORS.each_with_index do |value, i|
button = UIControls::Button.new(SLOWDOWN_BUTTON_WIDTH, ROW_HEIGHT, self.viewport, value.to_s)
button.set_fixed_size
button.x = SLOWDOWN_BUTTON_X + ((SLOWDOWN_BUTTON_WIDTH + SLOWDOWN_BUTTON_SPACING) * i)
button.y = SLOWDOWN_BUTTON_Y
button.set_interactive_rects
button.set_highlighted if value == @slowdown
@controls.push([("slowdown" + value.to_s).to_sym, button])
end
# Play button
play_button = UIControls::BitmapButton.new(PLAY_BUTTON_X, PLAY_BUTTON_Y, self.viewport, @bitmaps[:play_button])
play_button.set_interactive_rects
@@ -98,22 +115,6 @@ class AnimationEditor::PlayControls < UIControls::ControlsContainer
unloop_button.set_interactive_rects
unloop_button.visible = false if !@looping
@controls.push([:unloop, unloop_button])
# Slowdown label
duration_label = UIControls::Label.new(200, ROW_HEIGHT, self.viewport, _INTL("Slowdown factor"))
duration_label.x = SLOWDOWN_BUTTON_X + (SLOWDOWN_FACTORS.length * (SLOWDOWN_BUTTON_WIDTH + SLOWDOWN_BUTTON_SPACING) / 2)
duration_label.x -= (duration_label.text_width / 2) + 5
duration_label.y = SLOWDOWN_LABEL_Y
@controls.push([:slowdown_label, duration_label])
# Slowdown factor buttons
SLOWDOWN_FACTORS.each_with_index do |value, i|
button = UIControls::Button.new(SLOWDOWN_BUTTON_WIDTH, ROW_HEIGHT, self.viewport, value.to_s)
button.set_fixed_size
button.x = SLOWDOWN_BUTTON_X + ((SLOWDOWN_BUTTON_WIDTH + SLOWDOWN_BUTTON_SPACING) * i)
button.y = SLOWDOWN_BUTTON_Y
button.set_interactive_rects
button.set_highlighted if value == @slowdown
@controls.push([("slowdown" + value.to_s).to_sym, button])
end
# Duration label
duration_label = UIControls::Label.new(200, ROW_HEIGHT, self.viewport, _INTL("Duration"))
duration_label.x = DURATION_TEXT_X - (duration_label.text_width / 2)

View File

@@ -175,7 +175,8 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
@particle_line_sprite.dispose
@controls.each { |c| c[1].dispose }
@controls.clear
@bitmaps.each { |b| b&.dispose }
@bitmaps.each_value { |b| b&.dispose }
@bitmaps.clear
dispose_listed_sprites
@list_viewport.dispose
@commands_bg_viewport.dispose
@@ -433,16 +434,16 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
#-----------------------------------------------------------------------------
def calculate_duration
@duration = AnimationPlayer::Helper.get_duration(@particles)
@duration += DURATION_BUFFER
end
def calculate_all_commands_and_durations
calculate_duration
calculate_all_commands
end
def calculate_duration
@duration = AnimationPlayer::Helper.get_duration(@particles)
@duration += DURATION_BUFFER
end
def calculate_all_commands
@commands = {}
@particles.each_with_index do |particle, index|
@@ -1024,13 +1025,13 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
def update_input
# Left/right to change current keyframe
if Input.repeat?(Input::LEFT)
if Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
if @keyframe > 0
@keyframe -= 1
scroll_to_keyframe(@keyframe)
set_changed
end
elsif Input.repeat?(Input::RIGHT)
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
if @keyframe < @duration - 1
@keyframe += 1
scroll_to_keyframe(@keyframe)
@@ -1038,7 +1039,7 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
end
end
# Up/down to change selected particle
if Input.repeat?(Input::UP)
if Input.triggerex?(:UP) || Input.repeatex?(:UP)
if @row_index > 0
loop do
@row_index -= 1
@@ -1047,7 +1048,7 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
scroll_to_row(@row_index)
set_changed
end
elsif Input.repeat?(Input::DOWN)
elsif Input.triggerex?(:DOWN) || Input.repeatex?(:DOWN)
if @row_index < @particle_list.length - 1
old_row_index = @row_index
loop do
@@ -1064,6 +1065,18 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
end
end
end
if Input.triggerex?(:P)
idx_particle = @particle_list[@row_index]
idx_particle = idx_particle[0] if idx_particle.is_a?(Array)
if @row_index >= 0 && @particles[idx_particle][:name] != "SE"
if @expanded_particles.include?(idx_particle) # Contract
@expanded_particles.delete(idx_particle)
else # Expand
@expanded_particles.push(idx_particle)
end
set_particles(@particles)
end
end
# Mouse scroll wheel
mouse_x, mouse_y = mouse_pos
if mouse_x && mouse_y

View File

@@ -124,6 +124,7 @@ class AnimationPlayer
if relative_to_index >= 0 && relative_to_index.odd? && particle[:focus] != :user_and_target
particle_sprite.foe_invert_x = particle[:foe_invert_x]
particle_sprite.foe_invert_y = particle[:foe_invert_y]
particle_sprite.foe_flip = particle[:foe_flip]
end
# Find earliest command and add a "make visible" command then
if sprite && !particle_sprite.battler_sprite?
@@ -231,7 +232,7 @@ class AnimationPlayer
# Update all particles/sprites
@anim_sprites.each { |particle| particle.update(elapsed) }
# Finish or loop the animation
if elapsed >= @duration
if elapsed >= @duration * @slowdown
if looping
@need_reset = true
else

View File

@@ -5,7 +5,7 @@
class AnimationPlayer::ParticleSprite
attr_accessor :sprite
attr_accessor :focus_xy, :offset_xy, :focus_z
attr_accessor :foe_invert_x, :foe_invert_y
attr_accessor :foe_invert_x, :foe_invert_y, :foe_flip
FRAMES_PER_SECOND = 20.0
@@ -97,7 +97,9 @@ class AnimationPlayer::ParticleSprite
case property
when :frame then @sprite.src_rect.x = value.floor * @sprite.src_rect.width
when :blending then @sprite.blend_type = value
when :flip then @sprite.mirror = value
when :flip
@sprite.mirror = value
@sprite.mirror = !@sprite.mirror if @foe_flip
when :x
value = value.round
value *= -1 if @foe_invert_x