diff --git a/Data/Scripts/801_UI controls/Control elements/101_Scrollbar.rb b/Data/Scripts/801_UI controls/Control elements/101_Scrollbar.rb index d0a9f5c38..b91de8dac 100644 --- a/Data/Scripts/801_UI controls/Control elements/101_Scrollbar.rb +++ b/Data/Scripts/801_UI controls/Control elements/101_Scrollbar.rb @@ -33,6 +33,14 @@ class UIControls::Scrollbar < UIControls::BaseControl 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. def range=(new_val) diff --git a/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb b/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb index fb61aadab..a31e7991f 100644 --- a/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb +++ b/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb @@ -47,7 +47,7 @@ module Compiler elsif line[/^\s*(\w+)\s*=\s*(.*)$/] # XXX=YYY lines if !data_hash - raise _INTL("Expected a section at the beginning of the file.\n{1}", FileLineData.linereport) + raise _INTL("Expected a section at the beginning of the file.") + "\n" + FileLineData.linereport end key = $~[1] if schema[key] # Property of the animation @@ -62,7 +62,7 @@ module Compiler end elsif sub_schema[key] # Property of a particle if !current_particle - raise _INTL("Particle hasn't been defined yet!\n{1}", FileLineData.linereport) + raise _INTL("Particle hasn't been defined yet!") + "\n" + FileLineData.linereport end value = get_csv_record($~[2], sub_schema[key]) if sub_schema[key][1][0] == "^" @@ -154,20 +154,51 @@ module Compiler when "Target" then particle[:graphic] = "TARGET" end end - # Ensure that particles don't have a focus involving a user if the - # animation itself doesn't involve a user - if hash[:no_user] && GameData::Animation::FOCUS_TYPES_WITH_USER.include?(particle[:focus]) - raise _INTL("Particle \"{1}\" can't have a \"Focus\" that involves a user if property \"NoUser\" is set to true.", - particle[:name]) + "\n" + FileLineData.linereport + # Ensure that particles don't have a focus involving a user, and the + # animation doesn't play a user's cry, if the animation itself doesn't + # involve a user + if hash[:no_user] + if GameData::Animation::FOCUS_TYPES_WITH_USER.include?(particle[:focus]) + raise _INTL("Particle \"{1}\" can't have a \"Focus\" that involves a user if property \"NoUser\" is set to true.", + particle[:name]) + "\n" + FileLineData.linereport + end + if particle[:name] == "SE" && particle[:user_cry] && !particle[:user_cry].empty? + raise _INTL("Animation can't play the user's cry if property \"NoUser\" is set to true.") + "\n" + FileLineData.linereport + end end - # Ensure that particles don't have a focus involving a target if the - # animation itself doesn't involve a target - if hash[:no_target] && GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(particle[:focus]) - raise _INTL("Particle \"{1}\" can't have a \"Focus\" that involves a target if property \"NoTarget\" is set to true.", - particle[:name]) + "\n" + FileLineData.linereport + # Ensure that particles don't have a focus involving a target, and the + # animation doesn't play a target's cry, if the animation itself doesn't + # involve a target + if hash[:no_target] + if GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(particle[:focus]) + raise _INTL("Particle \"{1}\" can't have a \"Focus\" that involves a target if property \"NoTarget\" is set to true.", + particle[:name]) + "\n" + FileLineData.linereport + end + if particle[:name] == "SE" && particle[:target_cry] && !particle[:target_cry].empty? + raise _INTL("Animation can't play the target's cry if property \"NoTarget\" is set to true.") + "\n" + FileLineData.linereport + end + end + # Ensure that the same SE isn't played twice in the same frame + if particle[:name] == "SE" + [:se, :user_cry, :target_cry].each do |property| + next if !particle[property] + files_played = [] + particle[property].each do |play| + files_played[play[0]] ||= [] + if files_played[play[0]].include?(play[1]) + case property + when :se + raise _INTL("SE \"{1}\" should not play twice in the same frame ({2}).", play[1], play[0]) + "\n" + FileLineData.linereport + when :user_cry + raise _INTL("User's cry should not play twice in the same frame ({1}).", play[0]) + "\n" + FileLineData.linereport + when :target_cry + raise _INTL("Target's cry should not play twice in the same frame ({1}).", play[0]) + "\n" + FileLineData.linereport + end + end + files_played[play[0]].push(play[1]) + end + end end - # TODO: For SE particle, ensure that it doesn't play two instances of the - # same file in the same frame. # Convert all "SetXYZ" particle commands to "MoveXYZ" by giving them a # duration of 0 (even ones that can't have a "MoveXYZ" command) GameData::Animation::PARTICLE_KEYFRAME_DEFAULT_VALUES.keys.each do |prop| @@ -293,6 +324,7 @@ module Compiler rescue SystemCallError end raise Reset.new if e.is_a?(Hangup) + raise SystemExit.new if e.is_a?(RuntimeError) raise "Unknown exception when compiling animations." end end diff --git a/Data/Scripts/904_Anim Editor/001_AnimationEditor.rb b/Data/Scripts/904_Anim Editor/001_AnimationEditor.rb index 59db48eec..17a0a1eb8 100644 --- a/Data/Scripts/904_Anim Editor/001_AnimationEditor.rb +++ b/Data/Scripts/904_Anim Editor/001_AnimationEditor.rb @@ -711,7 +711,7 @@ class AnimationEditor # Disable the "move particle up/down" buttons if the selected particle # can't move that way (or there is no selected particle) cur_index = particle_index - if cur_index < 1 + if cur_index < 1 || @anim[:particles][cur_index][:name] == "SE" component.get_control(:move_particle_up).disable else component.get_control(:move_particle_up).enable @@ -847,12 +847,14 @@ class AnimationEditor end when :duplicate AnimationEditor::ParticleDataHelper.duplicate_particle(@anim[:particles], particle_index) + @components[:particle_list].add_particle(particle_index + 1) @components[:particle_list].set_particles(@anim[:particles]) @components[:particle_list].particle_index = particle_index + 1 refresh when :delete if confirm_message(_INTL("Are you sure you want to delete this particle?")) AnimationEditor::ParticleDataHelper.delete_particle(@anim[:particles], particle_index) + @components[:particle_list].delete_particle(particle_index) @components[:particle_list].set_particles(@anim[:particles]) @components[:particle_list].keyframe = 0 if @anim[:particles][particle_index][:name] == "SE" refresh @@ -875,6 +877,7 @@ class AnimationEditor new_idx = @anim[:particles].length - 1 if new_idx == 0 || new_idx >= @anim[:particles].length end AnimationEditor::ParticleDataHelper.add_particle(@anim[:particles], new_idx) + @components[:particle_list].add_particle(new_idx) @components[:particle_list].set_particles(@anim[:particles]) @components[:particle_list].particle_index = (new_idx >= 0) ? new_idx : @anim[:particles].length - 2 @components[:particle_list].keyframe = -1 @@ -883,6 +886,7 @@ class AnimationEditor idx1 = particle_index idx2 = idx1 - 1 AnimationEditor::ParticleDataHelper.swap_particles(@anim[:particles], idx1, idx2) + @components[:particle_list].swap_particles(idx1, idx2) @components[:particle_list].set_particles(@anim[:particles]) @components[:particle_list].particle_index = idx2 refresh @@ -890,6 +894,7 @@ class AnimationEditor idx1 = particle_index idx2 = idx1 + 1 AnimationEditor::ParticleDataHelper.swap_particles(@anim[:particles], idx1, idx2) + @components[:particle_list].swap_particles(idx1, idx2) @components[:particle_list].set_particles(@anim[:particles]) @components[:particle_list].particle_index = idx2 refresh @@ -932,16 +937,48 @@ class AnimationEditor @anim[property] = txt when :has_user @anim[:no_user] = !value - # TODO: Add/delete the "User" particle accordingly, and change the foci - # of any other particle involving a user. Then refresh a lot of - # components. - refresh_component(:canvas) + if @anim[:no_user] + @anim[:particles].delete_if { |particle| particle[:name] == "User" } + @anim[:particles].each do |particle| + if ["USER", "USER_OPP", "USER_FRONT", "USER_BACK"].include?(particle[:graphic]) + particle[:graphic] = GameData::Animation::PARTICLE_DEFAULT_VALUES[:graphic] + end + if GameData::Animation::FOCUS_TYPES_WITH_USER.include?(particle[:focus]) + particle[:focus] = GameData::Animation::PARTICLE_DEFAULT_VALUES[:focus] + end + particle[:user_cry] = nil if particle[:name] == "SE" + end + @components[:particle_list].delete_particle(0) + elsif @anim[:particles].none? { |particle| particle[:name] == "User" } + @anim[:particles].insert(0, { + :name => "User", :focus => :user, :graphic => "USER" + }) + @components[:particle_list].add_particle(0) + end + @components[:particle_list].set_particles(@anim[:particles]) + refresh when :has_target @anim[:no_target] = !value - # TODO: Add/delete the "Target" particle accordingly, and change the - # foci of any other particle involving a target. Then refresh a - # lot of components. - refresh_component(:canvas) + if @anim[:no_target] + @anim[:particles].delete_if { |particle| particle[:name] == "Target" } + @anim[:particles].each do |particle| + if ["TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"].include?(particle[:graphic]) + particle[:graphic] = GameData::Animation::PARTICLE_DEFAULT_VALUES[:graphic] + end + if GameData::Animation::FOCUS_TYPES_WITH_TARGET.include?(particle[:focus]) + particle[:focus] = GameData::Animation::PARTICLE_DEFAULT_VALUES[:focus] + end + particle[:target_cry] = nil if particle[:name] == "SE" + end + @components[:particle_list].delete_particle(@anim[:no_user] ? 0 : 1) + elsif @anim[:particles].none? { |particle| particle[:name] == "Target" } + @anim[:particles].insert((@anim[:no_user] ? 0 : 1), { + :name => "Target", :focus => :target, :graphic => "TARGET" + }) + @components[:particle_list].add_particle((@anim[:no_user] ? 0 : 1)) + end + @components[:particle_list].set_particles(@anim[:particles]) + refresh when :usable @anim[:ignore] = !value else diff --git a/Data/Scripts/904_Anim Editor/Anim Editor elements/001_Canvas.rb b/Data/Scripts/904_Anim Editor/Anim Editor elements/001_Canvas.rb index 1da9b6048..87e8e233a 100644 --- a/Data/Scripts/904_Anim Editor/Anim Editor elements/001_Canvas.rb +++ b/Data/Scripts/904_Anim Editor/Anim Editor elements/001_Canvas.rb @@ -321,25 +321,25 @@ class AnimationEditor::Canvas < Sprite 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.", + 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.", + 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.", + 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.", + 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 diff --git a/Data/Scripts/904_Anim Editor/Anim Editor elements/003_ParticleList.rb b/Data/Scripts/904_Anim Editor/Anim Editor elements/003_ParticleList.rb index 280dcb4a1..197ac7cb5 100644 --- a/Data/Scripts/904_Anim Editor/Anim Editor elements/003_ParticleList.rb +++ b/Data/Scripts/904_Anim Editor/Anim Editor elements/003_ParticleList.rb @@ -1,6 +1,5 @@ #=============================================================================== -# TODO: The Add/Up/Down buttons don't get captured, and don't prevent anything -# else in this control highlighting when hovered over, and vice versa. +# #=============================================================================== class AnimationEditor::ParticleList < UIControls::BaseControl VIEWPORT_SPACING = 1 @@ -48,66 +47,11 @@ class AnimationEditor::ParticleList < UIControls::BaseControl self.x = x self.y = y draw_control_background - # Create viewports - @list_viewport = Viewport.new( - x + LIST_X, y + LIST_Y, LIST_WIDTH, height - LIST_Y - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING - ) - @list_viewport.z = self.viewport.z + 1 - @commands_bg_viewport = Viewport.new( - x + COMMANDS_X, y + COMMANDS_Y, - width - COMMANDS_X - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, @list_viewport.rect.height - ) - @commands_bg_viewport.z = self.viewport.z + 1 - @position_viewport = Viewport.new(@commands_bg_viewport.rect.x, y, @commands_bg_viewport.rect.width, height) - @position_viewport.z = self.viewport.z + 2 - @commands_viewport = Viewport.new(@commands_bg_viewport.rect.x, @commands_bg_viewport.rect.y, - @commands_bg_viewport.rect.width, @commands_bg_viewport.rect.height) - @commands_viewport.z = self.viewport.z + 3 - # Create scrollbars - @list_scrollbar = UIControls::Scrollbar.new( - x + width - UIControls::Scrollbar::SLIDER_WIDTH, @commands_bg_viewport.rect.y, - @commands_bg_viewport.rect.height, self.viewport, false, true - ) - @list_scrollbar.set_interactive_rects - @time_scrollbar = UIControls::Scrollbar.new( - @commands_bg_viewport.rect.x, y + height - UIControls::Scrollbar::SLIDER_WIDTH, - @commands_bg_viewport.rect.width, self.viewport, true, true - ) - @time_scrollbar.set_interactive_rects - # Time background bitmap sprite - @time_bg_sprite = BitmapSprite.new( - @commands_viewport.rect.width, - TIMELINE_HEIGHT + VIEWPORT_SPACING + @list_viewport.rect.height, self.viewport - ) - @time_bg_sprite.x = @commands_viewport.rect.x - @time_bg_sprite.y = self.y - # Timeline bitmap sprite - @timeline_sprite = BitmapSprite.new(@commands_viewport.rect.width, TIMELINE_HEIGHT, self.viewport) - @timeline_sprite.x = @commands_viewport.rect.x - @timeline_sprite.y = self.y - @timeline_sprite.bitmap.font.color = TEXT_COLOR - @timeline_sprite.bitmap.font.size = TIMELINE_TEXT_SIZE - # Position line sprite - @position_sprite = BitmapSprite.new(3, height - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, @position_viewport) - @position_sprite.ox = @position_sprite.width / 2 - @position_sprite.bitmap.fill_rect(0, 0, @position_sprite.bitmap.width, @position_sprite.bitmap.height, Color.red) - # Selected particle line sprite - @particle_line_sprite = BitmapSprite.new(@position_viewport.rect.width, 3, @commands_viewport) - @particle_line_sprite.z = -10 - @particle_line_sprite.oy = @particle_line_sprite.height / 2 - @particle_line_sprite.bitmap.fill_rect(0, 0, @particle_line_sprite.bitmap.width, @particle_line_sprite.bitmap.height, Color.red) - # Buttons and button bitmaps - initialize_button_bitmaps - @controls = [] - add_particle_button = UIControls::BitmapButton.new(x + 1, y + 1, viewport, @add_button_bitmap) - add_particle_button.set_interactive_rects - @controls.push([:add_particle, add_particle_button]) - up_particle_button = UIControls::BitmapButton.new(x + 22, y + 1, viewport, @up_button_bitmap) - up_particle_button.set_interactive_rects - @controls.push([:move_particle_up, up_particle_button]) - down_particle_button = UIControls::BitmapButton.new(x + 43, y + 1, viewport, @down_button_bitmap) - down_particle_button.set_interactive_rects - @controls.push([:move_particle_down, down_particle_button]) + initialize_viewports + initialize_scrollbars + initialize_timeline_bitmaps + initialize_selection_bitmaps + initialize_controls # List sprites and commands sprites @list_sprites = [] @commands_bg_sprites = [] @@ -127,7 +71,81 @@ class AnimationEditor::ParticleList < UIControls::BaseControl @commands = {} end - def initialize_button_bitmaps + def initialize_viewports + @list_viewport = Viewport.new( + x + LIST_X, y + LIST_Y, LIST_WIDTH, height - LIST_Y - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING + ) + @list_viewport.z = self.viewport.z + 1 + @commands_bg_viewport = Viewport.new( + x + COMMANDS_X, y + COMMANDS_Y, + width - COMMANDS_X - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, @list_viewport.rect.height + ) + @commands_bg_viewport.z = self.viewport.z + 1 + @position_viewport = Viewport.new(@commands_bg_viewport.rect.x, y, @commands_bg_viewport.rect.width, height) + @position_viewport.z = self.viewport.z + 2 + @commands_viewport = Viewport.new(@commands_bg_viewport.rect.x, @commands_bg_viewport.rect.y, + @commands_bg_viewport.rect.width, @commands_bg_viewport.rect.height) + @commands_viewport.z = self.viewport.z + 3 + end + + def initialize_scrollbars + # Vertical scrollbar + @list_scrollbar = UIControls::Scrollbar.new( + x + width - UIControls::Scrollbar::SLIDER_WIDTH, @commands_bg_viewport.rect.y, + @commands_bg_viewport.rect.height, self.viewport, false, true + ) + @list_scrollbar.set_interactive_rects + # Horizontal scrollbar + @time_scrollbar = UIControls::Scrollbar.new( + @commands_bg_viewport.rect.x, y + height - UIControls::Scrollbar::SLIDER_WIDTH, + @commands_bg_viewport.rect.width, self.viewport, true, true + ) + @time_scrollbar.set_interactive_rects + end + + def initialize_timeline_bitmaps + # Time background bitmap sprite + @time_bg_sprite = BitmapSprite.new( + @commands_viewport.rect.width, + TIMELINE_HEIGHT + VIEWPORT_SPACING + @list_viewport.rect.height, self.viewport + ) + @time_bg_sprite.x = @commands_viewport.rect.x + @time_bg_sprite.y = self.y + # Timeline bitmap sprite + @timeline_sprite = BitmapSprite.new(@commands_viewport.rect.width, TIMELINE_HEIGHT, self.viewport) + @timeline_sprite.x = @commands_viewport.rect.x + @timeline_sprite.y = self.y + @timeline_sprite.bitmap.font.color = TEXT_COLOR + @timeline_sprite.bitmap.font.size = TIMELINE_TEXT_SIZE + end + + def initialize_selection_bitmaps + # Position line sprite + @position_sprite = BitmapSprite.new(3, height - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, @position_viewport) + @position_sprite.ox = @position_sprite.width / 2 + @position_sprite.bitmap.fill_rect(0, 0, @position_sprite.bitmap.width, @position_sprite.bitmap.height, Color.red) + # Selected particle line sprite + @particle_line_sprite = BitmapSprite.new(@position_viewport.rect.width, 3, @commands_viewport) + @particle_line_sprite.z = -10 + @particle_line_sprite.oy = @particle_line_sprite.height / 2 + @particle_line_sprite.bitmap.fill_rect(0, 0, @particle_line_sprite.bitmap.width, @particle_line_sprite.bitmap.height, Color.red) + end + + def initialize_controls + generate_button_bitmaps + @controls = [] + add_particle_button = UIControls::BitmapButton.new(x + 1, y + 1, viewport, @add_button_bitmap) + add_particle_button.set_interactive_rects + @controls.push([:add_particle, add_particle_button]) + up_particle_button = UIControls::BitmapButton.new(x + 22, y + 1, viewport, @up_button_bitmap) + up_particle_button.set_interactive_rects + @controls.push([:move_particle_up, up_particle_button]) + down_particle_button = UIControls::BitmapButton.new(x + 43, y + 1, viewport, @down_button_bitmap) + down_particle_button.set_interactive_rects + @controls.push([:move_particle_down, down_particle_button]) + end + + def generate_button_bitmaps @add_button_bitmap = Bitmap.new(12, 12) @add_button_bitmap.fill_rect(1, 5, 10, 2, TEXT_COLOR) @add_button_bitmap.fill_rect(5, 1, 2, 10, TEXT_COLOR) @@ -143,24 +161,6 @@ class AnimationEditor::ParticleList < UIControls::BaseControl end end - def draw_control_background - self.bitmap.clear - # Separator lines - self.bitmap.fill_rect(0, TIMELINE_HEIGHT, width, VIEWPORT_SPACING, Color.black) - self.bitmap.fill_rect(LIST_WIDTH, 0, VIEWPORT_SPACING, height, Color.black) - self.bitmap.fill_rect(0, height - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, width, VIEWPORT_SPACING, Color.black) - self.bitmap.fill_rect(width - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, 0, VIEWPORT_SPACING, height, Color.black) - end - - def dispose_listed_sprites - @list_sprites.each { |p| p&.dispose } - @list_sprites.clear - @commands_bg_sprites.each { |p| p&.dispose } - @commands_bg_sprites.clear - @commands_sprites.each { |p| p&.dispose } - @commands_sprites.clear - end - def dispose @list_scrollbar.dispose @time_scrollbar.dispose @@ -179,6 +179,17 @@ class AnimationEditor::ParticleList < UIControls::BaseControl @commands_viewport.dispose end + def dispose_listed_sprites + @list_sprites.each { |p| p&.dispose } + @list_sprites.clear + @commands_bg_sprites.each { |p| p&.dispose } + @commands_bg_sprites.clear + @commands_sprites.each { |p| p&.dispose } + @commands_sprites.clear + end + + #----------------------------------------------------------------------------- + def duration return [@duration - DURATION_BUFFER, 0].max end @@ -236,6 +247,71 @@ class AnimationEditor::ParticleList < UIControls::BaseControl end end + def scroll_to_row(new_row) + if new_row * ROW_HEIGHT < @top_pos + # Scroll up + new_pos = new_row * ROW_HEIGHT + loop do + @list_scrollbar.slider_top -= 1 + break if @list_scrollbar.position <= new_pos || @list_scrollbar.minimum? + end + elsif new_row * ROW_HEIGHT > @top_pos + @list_viewport.rect.height - ROW_HEIGHT + # Scroll down + new_pos = (new_row * ROW_HEIGHT) - @list_viewport.rect.height + ROW_HEIGHT + loop do + @list_scrollbar.slider_top += 1 + break if @list_scrollbar.position >= new_pos || @list_scrollbar.maximum? + end + end + end + + def scroll_to_keyframe(new_keyframe) + if TIMELINE_LEFT_BUFFER + (new_keyframe * KEYFRAME_SPACING) - (KEYFRAME_SPACING / 2) < @left_pos + # Scroll left + new_pos = TIMELINE_LEFT_BUFFER + (new_keyframe * KEYFRAME_SPACING) - (KEYFRAME_SPACING / 2) + loop do + @time_scrollbar.slider_top -= 1 + break if @time_scrollbar.position <= new_pos || @time_scrollbar.minimum? + end + elsif TIMELINE_LEFT_BUFFER + (new_keyframe * KEYFRAME_SPACING) + (KEYFRAME_SPACING / 2) > @left_pos + @commands_bg_viewport.rect.width + # Scroll right + new_pos = TIMELINE_LEFT_BUFFER + (new_keyframe * KEYFRAME_SPACING) + (KEYFRAME_SPACING / 2) - @commands_bg_viewport.rect.width + loop do + @time_scrollbar.slider_top += 1 + break if @time_scrollbar.position >= new_pos || @time_scrollbar.maximum? + end + end + end + + # Ensures that the array of which particle rows have been expanded ends up + # with the same particles having expanded rows after adding a particle. + def add_particle(index) + @expanded_particles.each_with_index do |idx, i| + @expanded_particles[i] += 1 if idx >= index + end + end + + # Ensures that the array of which particle rows have been expanded ends up + # with the same particles having expanded rows after deleting a particle. + def delete_particle(index) + @expanded_particles.delete(index) + @expanded_particles.each_with_index do |idx, i| + @expanded_particles[i] -= 1 if idx > index + end + end + + # Ensures that the array of which particle rows have been expanded ends up + # with the same particles having expanded rows after the swap. + def swap_particles(idx1, idx2) + if @expanded_particles.include?(idx1) && !@expanded_particles.include?(idx2) + @expanded_particles.delete(idx1) + @expanded_particles.push(idx2) + elsif @expanded_particles.include?(idx2) && !@expanded_particles.include?(idx1) + @expanded_particles.delete(idx2) + @expanded_particles.push(idx1) + end + end + def set_particles(particles) @particles = particles calculate_all_commands_and_durations @@ -449,9 +525,6 @@ class AnimationEditor::ParticleList < UIControls::BaseControl invalidate_rows end - # TODO: Methods that will show/hide individual property rows for a given - # @particles index. - #----------------------------------------------------------------------------- def each_visible_keyframe(early_start = false) @@ -478,6 +551,17 @@ class AnimationEditor::ParticleList < UIControls::BaseControl #----------------------------------------------------------------------------- + def draw_control_background + self.bitmap.clear + # Separator lines + self.bitmap.fill_rect(0, TIMELINE_HEIGHT, width, VIEWPORT_SPACING, Color.black) + self.bitmap.fill_rect(LIST_WIDTH, 0, VIEWPORT_SPACING, height, Color.black) + self.bitmap.fill_rect(0, height - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, width, VIEWPORT_SPACING, Color.black) + self.bitmap.fill_rect(width - UIControls::Scrollbar::SLIDER_WIDTH - VIEWPORT_SPACING, 0, VIEWPORT_SPACING, height, Color.black) + end + + #----------------------------------------------------------------------------- + def repaint @list_scrollbar.repaint if @list_scrollbar.invalid? @time_scrollbar.repaint if @time_scrollbar.invalid? @@ -850,14 +934,16 @@ class AnimationEditor::ParticleList < UIControls::BaseControl else case @captured_row_button when :expand - particle_index = @particle_list[@captured_row] - particle_index = particle_index[0] if particle_index.is_a?(Array) - if @expanded_particles.include?(particle_index) # Contract - @expanded_particles.delete(particle_index) - else # Expand - @expanded_particles.push(particle_index) + old_row_idx_particle = @particle_list[@row_index] + idx_particle = @particle_list[@captured_row] + idx_particle = idx_particle[0] if idx_particle.is_a?(Array) + if @expanded_particles.include?(idx_particle) # Contract + @expanded_particles.delete(idx_particle) + else # Expand + @expanded_particles.push(idx_particle) end set_particles(@particles) + @row_index = @particle_list.index(old_row_idx_particle) else # :row button or somewhere in the commands area or timeline, just change selection if @captured_row && @particle_list[@captured_row].is_a?(Array) @captured_row = @particle_list.index(@particle_list[@captured_row][0]) @@ -877,11 +963,6 @@ class AnimationEditor::ParticleList < UIControls::BaseControl super # Make this control not busy again end - def on_right_mouse_release - # TODO: Toggle interpolation line at mouse's position. Should this also have - # a def on_right_mouse_press and @right_captured_whatever? - end - def update_hover_highlight # Remove the hover highlight if there are no interactions for this control # or if the mouse is off-screen @@ -938,6 +1019,59 @@ class AnimationEditor::ParticleList < UIControls::BaseControl end end + def update_input + # Left/right to change current keyframe + if Input.repeat?(Input::LEFT) + if @keyframe > 0 + @keyframe -= 1 + scroll_to_keyframe(@keyframe) + set_changed + end + elsif Input.repeat?(Input::RIGHT) + if @keyframe < @duration - 1 + @keyframe += 1 + scroll_to_keyframe(@keyframe) + set_changed + end + end + # Up/down to change selected particle + if Input.repeat?(Input::UP) + if @row_index > 0 + loop do + @row_index -= 1 + break if !@particle_list[@row_index].is_a?(Array) + end + scroll_to_row(@row_index) + set_changed + end + elsif Input.repeat?(Input::DOWN) + if @row_index < @particle_list.length - 1 + loop do + @row_index += 1 + break if !@particle_list[@row_index].is_a?(Array) + end + @keyframe = 0 if @row_index >= @particle_list.length - 1 && @keyframe < 0 + scroll_to_row(@row_index) + set_changed + end + end + # Mouse scroll wheel + mouse_x, mouse_y = mouse_pos + if mouse_x && mouse_y + if @interactions[:list].contains?(mouse_x, mouse_y) || + @interactions[:commands].contains?(mouse_x, mouse_y) + wheel_v = Input.scroll_v + if wheel_v > 0 # Scroll up + @list_scrollbar.slider_top -= UIControls::Scrollbar::SCROLL_DISTANCE + self.top_pos = @list_scrollbar.position + elsif wheel_v < 0 # Scroll down + @list_scrollbar.slider_top += UIControls::Scrollbar::SCROLL_DISTANCE + self.top_pos = @list_scrollbar.position + end + end + end + end + def update return if !self.visible @list_scrollbar.update @@ -949,9 +1083,8 @@ class AnimationEditor::ParticleList < UIControls::BaseControl # Refresh sprites if a scrollbar has been moved self.top_pos = @list_scrollbar.position self.left_pos = @time_scrollbar.position - # Update the current keyframe line's position + # Update the positions of the selected particle/keyframe lines refresh_position_line - # Update the selected particle line's position refresh_particle_line # Add/move particle buttons @controls.each do |c| @@ -960,55 +1093,7 @@ class AnimationEditor::ParticleList < UIControls::BaseControl @values[c[0]] = true c[1].clear_changed end - - if Input.release?(Input::MOUSERIGHT) - on_right_mouse_release - end - - # TODO: This is testing code, and should be replaced by clicking on the - # timeline or a command sprite. Maybe keep it after all? If so, - # probably change left/right to <>, and also move the scrollbar(s) to - # keep the "cursor" on-screen. - if Input.repeat?(Input::LEFT) - if @keyframe > 0 - @keyframe -= 1 - set_changed - end - elsif Input.repeat?(Input::RIGHT) - if @keyframe < @duration - 1 - @keyframe += 1 - set_changed - end - # TODO: If this is to be kept, @row_index should be changed by potentially - # more than 1, so that @particle_list[@row_index] is an integer and - # not an array. - # elsif Input.repeat?(Input::UP) - # if @row_index > 0 - # @row_index -= 1 - # set_changed - # end - # elsif Input.repeat?(Input::DOWN) - # if @row_index < @particles.length - 1 - # @row_index += 1 - # set_changed - # end - end - - # Mouse scroll wheel - mouse_x, mouse_y = mouse_pos - if mouse_x && mouse_y - if @interactions[:list].contains?(mouse_x, mouse_y) || - @interactions[:commands].contains?(mouse_x, mouse_y) - wheel_v = Input.scroll_v - if wheel_v > 0 # Scroll up - @list_scrollbar.slider_top -= UIControls::Scrollbar::SCROLL_DISTANCE - self.top_pos = @list_scrollbar.position - elsif wheel_v < 0 # Scroll down - @list_scrollbar.slider_top += UIControls::Scrollbar::SCROLL_DISTANCE - self.top_pos = @list_scrollbar.position - end - end - end - + # Up/down/left/right navigation, and mouse scroll wheel + update_input end end diff --git a/PBS/Animations/Converted/Move/ABSORB.txt b/PBS/Animations/Converted/Move/ABSORB.txt index 36e86f959..6545786ea 100644 --- a/PBS/Animations/Converted/Move/ABSORB.txt +++ b/PBS/Animations/Converted/Move/ABSORB.txt @@ -236,7 +236,6 @@ Name = ABSORB SetY = 12,-50 SetVisible = 13,false - Play = 0,Absorb2,80 Play = 0,Absorb2,80 Play = 2,Absorb2,80 Play = 5,Absorb2,80 diff --git a/PBS/Animations/Converted/Move/MAGICCOAT.txt b/PBS/Animations/Converted/Move/MAGICCOAT.txt index db99696f9..47acb1267 100644 --- a/PBS/Animations/Converted/Move/MAGICCOAT.txt +++ b/PBS/Animations/Converted/Move/MAGICCOAT.txt @@ -166,5 +166,4 @@ Name = MAGICCOAT SetVisible = 9,false Play = 0,Sword2,80 - Play = 0,Sword2,80,101 Play = 4,Sword2,80