Added the animation properties pop-up window

This commit is contained in:
Maruno17
2023-12-02 01:39:43 +00:00
parent b69f1fc5a6
commit b4e7b765d1
247 changed files with 203 additions and 1245 deletions

View File

@@ -13,11 +13,12 @@
# don't think is ideal.
#===============================================================================
class UIControls::ControlsContainer
attr_reader :x, :y
attr_reader :controls
attr_reader :values
attr_reader :visible
attr_reader :viewport
attr_reader :x, :y
attr_accessor :label_offset_x, :label_offset_y
attr_reader :controls
attr_reader :values
attr_reader :visible
attr_reader :viewport
LINE_SPACING = 28
OFFSET_FROM_LABEL_X = 90
@@ -30,6 +31,8 @@ class UIControls::ControlsContainer
@y = y
@width = width
@height = height
@label_offset_x = OFFSET_FROM_LABEL_X
@label_offset_y = OFFSET_FROM_LABEL_Y
@controls = []
@row_count = 0
@pixel_offset = 0
@@ -196,7 +199,7 @@ class UIControls::ControlsContainer
def control_size(has_label = false)
if has_label
return @width - OFFSET_FROM_LABEL_X, LINE_SPACING - OFFSET_FROM_LABEL_Y
return @width - @label_offset_x, LINE_SPACING - @label_offset_y
end
return @width, LINE_SPACING
end
@@ -213,9 +216,9 @@ class UIControls::ControlsContainer
i = @controls.length
row_x = 0
row_y = (add_offset ? @row_count - 1 : @row_count) * LINE_SPACING
ctrl_x = row_x + (add_offset ? OFFSET_FROM_LABEL_X : 0)
ctrl_x = row_x + (add_offset ? @label_offset_x : 0)
ctrl_x += 4 if control.is_a?(UIControls::List)
ctrl_y = row_y + (add_offset ? OFFSET_FROM_LABEL_Y : 0) + @pixel_offset
ctrl_y = row_y + (add_offset ? @label_offset_y : 0) + @pixel_offset
add_control_at(id, control, ctrl_x, ctrl_y)
@row_count += rows if !add_offset
@pixel_offset -= (LINE_SPACING - UIControls::List::ROW_HEIGHT) * (rows - 1) if control.is_a?(UIControls::List)

View File

@@ -1,4 +1,3 @@
# TODO: Add "disabled" greying out/non-editable.
# TODO: Add indicator of whether the control's value is "lerping" between frames
# (use yellow somehow?).

View File

@@ -2,19 +2,19 @@
#
#===============================================================================
class UIControls::Label < UIControls::BaseControl
attr_reader :label
attr_reader :text
LABEL_END_X = 80
TEXT_OFFSET_Y = 5
def initialize(width, height, viewport, label)
def initialize(width, height, viewport, text)
super(width, height, viewport)
@label = label
@text = text
@header = false
end
def label=(value)
@label = value
def text=(value)
@text = value
refresh
end
@@ -26,11 +26,11 @@ class UIControls::Label < UIControls::BaseControl
def refresh
super
if @header
draw_text_centered(self.bitmap, 0, TEXT_OFFSET_Y, width, @label)
text_size = self.bitmap.text_size(@label)
draw_text_centered(self.bitmap, 0, TEXT_OFFSET_Y, width, @text)
text_size = self.bitmap.text_size(@text)
self.bitmap.fill_rect((width - text_size.width) / 2, TEXT_OFFSET_Y + text_size.height, text_size.width, 1, TEXT_COLOR)
else
draw_text(self.bitmap, 4, TEXT_OFFSET_Y, @label)
draw_text(self.bitmap, 4, TEXT_OFFSET_Y, @text)
end
end
end

View File

@@ -7,7 +7,7 @@
#===============================================================================
class UIControls::TextBox < UIControls::BaseControl
TEXT_BOX_X = 2
TEXT_BOX_WIDTH = 172
TEXT_BOX_WIDTH = 200
TEXT_BOX_HEIGHT = 24
TEXT_BOX_PADDING = 4 # Gap between sides of text box and text
TEXT_OFFSET_Y = 5

View File

@@ -3,18 +3,21 @@
#===============================================================================
class UIControls::DropdownList < UIControls::BaseControl
TEXT_BOX_X = 2
TEXT_BOX_WIDTH = 172
TEXT_BOX_WIDTH = 200
TEXT_BOX_HEIGHT = 24
TEXT_BOX_PADDING = 4 # Gap between sides of text box and text
TEXT_OFFSET_Y = 5
MAX_LIST_ROWS = 8
attr_accessor :max_rows
def initialize(width, height, viewport, options, value)
# NOTE: options is a hash: keys are symbols, values are display names.
super(width, height, viewport)
@options = options
@value = value
@toggling_dropdown_list = false
@max_rows = MAX_LIST_ROWS
end
def dispose
@@ -46,7 +49,7 @@ class UIControls::DropdownList < UIControls::BaseControl
#-----------------------------------------------------------------------------
def make_dropdown_menu
menu_height = UIControls::List::ROW_HEIGHT * [@options.length, MAX_LIST_ROWS].min
menu_height = UIControls::List::ROW_HEIGHT * [@options.length, @max_rows].min
# Draw menu's background
@dropdown_menu_bg = BitmapSprite.new(@button_rect.width, menu_height + 4, self.viewport)
@dropdown_menu_bg.x = self.x + @button_rect.x

View File

@@ -5,6 +5,7 @@ module GameData
attr_reader :version # Hit number
attr_reader :name # Shown in the sublist; cosmetic only
attr_reader :no_target # Whether there is no "Target" particle (false by default)
attr_reader :ignore # Whether the animation can't be played in battle
attr_reader :flags
attr_reader :pbs_path # Whole path minus "PBS/Animations/" at start and ".txt" at end
attr_reader :particles
@@ -29,6 +30,7 @@ module GameData
"Common" => :common, "OppCommon" => :opp_common}],
"Name" => [:name, "s"],
"NoTarget" => [:no_target, "b"],
"Ignore" => [:ignore, "b"],
# TODO: Boolean for whether the animation will be played if the target is
# on the same side as the user.
# TODO: DamageFrame (keyframe at which the battle continues, i.e. damage
@@ -98,6 +100,7 @@ module GameData
# TODO: Add "SetColor"/"SetTone" as shorthand for the above? They'd be
# converted in the Compiler.
# TODO: Bitmap masking.
# TODO: Sprite cropping.
# TODO: Hue? I don't think so; color/tone do the same job.
# These properties are specifically for the "SE" particle.
@@ -187,6 +190,7 @@ module GameData
@version = hash[:version] || 0
@name = hash[:name]
@no_target = hash[:no_target] || false
@ignore = hash[:ignore] || false
@particles = hash[:particles] || []
@flags = hash[:flags] || []
@pbs_path = hash[:pbs_path] || @move
@@ -197,9 +201,9 @@ module GameData
def clone_as_hash
ret = {}
ret[:type] = @type
ret[:move] = @move
ret[:version] = @version
ret[:name] = @name
ret[:move] = @move.dup
ret[:version] = @version.dup
ret[:name] = @name.dup
ret[:particles] = [] # Clone the @particles array, which is nested hashes and arrays
@particles.each do |particle|
new_p = {}

View File

@@ -98,8 +98,6 @@ module AnimationConverter
particle[:name] = "User"
elsif i == 1
particle[:name] = "Target"
else
particle[:visible] = [[0, 0, true]]
end
last_frame = last_frame_values[i] || default_frame.clone
@@ -165,11 +163,11 @@ end
#===============================================================================
# Add to Debug menu.
#===============================================================================
MenuHandlers.add(:debug_menu, :convert_anims, {
"name" => "Convert old animation to PBS files",
"parent" => :main,
"description" => "This is just for the sake of having lots of example animation PBS files.",
"effect" => proc {
AnimationConverter.convert_old_animations_to_new
}
})
# MenuHandlers.add(:debug_menu, :convert_anims, {
# "name" => "Convert old animation to PBS files",
# "parent" => :main,
# "description" => "This is just for the sake of having lots of example animation PBS files.",
# "effect" => proc {
# AnimationConverter.convert_old_animations_to_new
# }
# })

View File

@@ -45,10 +45,15 @@ class AnimationEditor
PARTICLE_LIST_WIDTH = WINDOW_WIDTH - (BORDER_THICKNESS * 2)
PARTICLE_LIST_HEIGHT = WINDOW_HEIGHT - PARTICLE_LIST_Y - BORDER_THICKNESS
ANIM_PROPERTIES_WIDTH = SIDE_PANE_WIDTH + 80
ANIM_PROPERTIES_HEIGHT = WINDOW_HEIGHT * 3 / 4
ANIM_PROPERTIES_X = (WINDOW_WIDTH - ANIM_PROPERTIES_WIDTH) / 2
ANIM_PROPERTIES_Y = (WINDOW_HEIGHT - ANIM_PROPERTIES_HEIGHT) / 2
def initialize(anim_id, anim)
@anim_id = anim_id
@anim = anim
@pbs_path = anim[:pbs_path]
@pbs_path = anim[:pbs_path].dup
@quit = false
# Viewports
@viewport = Viewport.new(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT)
@@ -92,12 +97,19 @@ class AnimationEditor
PARTICLE_LIST_X, PARTICLE_LIST_Y, PARTICLE_LIST_WIDTH, PARTICLE_LIST_HEIGHT, @viewport
)
@components[:particle_list].set_interactive_rects
# Animation properties pop-up window
@components[:animation_properties] = UIControls::ControlsContainer.new(
ANIM_PROPERTIES_X + 4, ANIM_PROPERTIES_Y + 4, ANIM_PROPERTIES_WIDTH - 8, ANIM_PROPERTIES_HEIGHT - 8
)
@components[:animation_properties].viewport.z = @pop_up_viewport.z + 1
@components[:animation_properties].label_offset_x = 170
@captured = nil
set_menu_bar_contents
set_canvas_contents
set_play_controls_contents
set_side_panes_contents
set_particle_list_contents
set_play_controls_contents
set_animation_properties_contents
refresh
end
@@ -155,6 +167,10 @@ class AnimationEditor
@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"))
@@ -246,8 +262,44 @@ class AnimationEditor
@components[:particle_list].set_particles(@anim[:particles])
end
def set_play_controls_contents
@components[:play_controls].duration = @components[:particle_list].duration
def set_animation_properties_contents
anim_properties = @components[:animation_properties]
anim_properties.add_header_label(:header, _INTL("Animation properties"))
# Create animation type control
anim_properties.add_labelled_dropdown_list(:type, _INTL("Animation type"), {
:move => _INTL("Move"),
:common => _INTL("Common")
}, :move)
# Create "opp" variant
anim_properties.add_labelled_checkbox(:opp_variant, _INTL("User is opposing?"), false)
# Create move control
# TODO: Also make a TextBox here for common animations, and toggle these two
# controls' visibility depending on :type?
move_list = []
GameData::Move.each { |m| move_list.push([m.id.to_s, m.name]) }
move_list.sort! { |a, b| a[1] <=> b[1] }
# TODO: Make this a TextBoxDropdownList instead.
anim_properties.add_labelled_dropdown_list(:move, _INTL("Move"), move_list.to_h, move_list[0][0])
move_ctrl = anim_properties.get_control(:move)
move_ctrl.max_rows = 16
common_text = UIControls::TextBox.new(move_ctrl.width, move_ctrl.height, move_ctrl.viewport, "")
anim_properties.add_control_at(:common_anim, common_text, move_ctrl.x, move_ctrl.y)
# Create version control
anim_properties.add_labelled_number_text_box(:version, _INTL("Version"), 0, 99, 0)
# Create animation name control
anim_properties.add_labelled_text_box(:name, _INTL("Name"), "")
# Create filepath controls
# TODO: Have two TextBoxes, one for folder and one for filename?
anim_properties.add_labelled_text_box(:pbs_path, _INTL("PBS filepath"), "")
# Create "involves a target" control
anim_properties.add_labelled_checkbox(:has_target, _INTL("Involves a target?"), true)
# Create flags control
# TODO: List, TextBox and some Buttons to add/delete.
# Create "usable in battle" control
anim_properties.add_labelled_checkbox(:usable, _INTL("Can be used in battle?"), true)
# TODO: "Play if target is on the same side as the user".
anim_properties.add_button(:close, _INTL("Close"))
anim_properties.visible = false
end
#-----------------------------------------------------------------------------
@@ -367,7 +419,7 @@ class AnimationEditor
"TARGET_BACK" => _INTL("[[Target's back sprite]]"),
}
graphic_name = graphic_override_names[graphic_name] if graphic_override_names[graphic_name]
component.get_control(:graphic_name).label = graphic_name
component.get_control(:graphic_name).text = graphic_name
# Enable/disable the Graphic and Focus controls for "User"/"Target"
if ["User", "Target"].include?(@anim[:particles][particle_index][:name])
component.get_control(:graphic).disable
@@ -376,6 +428,20 @@ class AnimationEditor
component.get_control(:graphic).enable
component.get_control(:focus).enable
end
when :animation_properties
case @anim[:type]
when :move, :opp_move
component.get_control(:move_label).text = _INTL("Move")
component.get_control(:move).visible = true
component.get_control(:move).value = @anim[:move].dup
component.get_control(:common_anim).visible = false
when :common, :opp_common
component.get_control(:move_label).text = _INTL("Common animation")
component.get_control(:move).visible = false
component.get_control(:common_anim).visible = true
component.get_control(:common_anim).value = @anim[:move].dup
end
# TODO: Maybe other things as well?
end
end
@@ -403,12 +469,17 @@ class AnimationEditor
when :save
save
when :name
# TODO: Open the animation properties pop-up window.
echoln "Animation's name button clicked"
edit_animation_properties
@components[:menu_bar].anim_name = get_animation_display_name
# TODO: May need to refresh other things.
refresh_component(:particle_list)
end
when :canvas
# TODO: Detect and apply changes made in canvas, e.g. moving particle,
# double-clicking to add particle, deleting particle.
when :play_controls
# TODO: Will the play controls ever signal themselves as changed? I don't
# think so.
when :commands_pane
case property
when :color_tone # Button
@@ -486,9 +557,31 @@ class AnimationEditor
when :particle_list
# refresh if keyframe != old_keyframe || particle_index != old_particle_index
# TODO: Lots of stuff here when buttons are added to it.
when :play_controls
# TODO: Will the play controls ever signal themselves as changed? I don't
# think so.
when :animation_properties
case property
when :type, :opp_variant
type = @components[:animation_properties].get_control(:type).value
opp = @components[:animation_properties].get_control(:opp_variant).value
case type
when :move
@anim[:type] = (opp) ? :opp_move : :move
when :common
@anim[:type] = (opp) ? :opp_common : :common
end
refresh_component(:animation_properties)
when :common_anim
@anim[:move] = value
when :pbs_path
txt = value.dup.gsub!(/\.txt$/, "")
@anim[property] = txt
when :has_target
@anim[:no_target] = !value
# TODO: Add/delete the "Target" particle accordingly.
when :usable
@anim[:ignore] = !value
else
@anim[property] = value
end
end
end

View File

@@ -344,4 +344,53 @@ class AnimationEditor
@pop_up_bg_bitmap.visible = false
return [ret, vol, ptch]
end
#-----------------------------------------------------------------------------
def edit_animation_properties
# Show pop-up window
@pop_up_bg_bitmap.visible = true
bg_bitmap = create_pop_up_window(ANIM_PROPERTIES_WIDTH, ANIM_PROPERTIES_HEIGHT)
# TODO: Draw box around list control(s), i.e. flags.
@components[:animation_properties].visible = true
# Set control values
anim_properties = @components[:animation_properties]
case @anim[:type]
when :move, :opp_move
anim_properties.get_control(:type).value = :move
when :common, :opp_common
anim_properties.get_control(:type).value = :common
end
anim_properties.get_control(:opp_variant).value = ([:opp_move, :opp_common].include?(@anim[:type]))
anim_properties.get_control(:version).value = @anim[:version] || 0
anim_properties.get_control(:name).value = @anim[:name] || ""
anim_properties.get_control(:pbs_path).value = (@anim[:pbs_path] || "unsorted") + ".txt"
anim_properties.get_control(:has_target).value = !@anim[:no_target]
anim_properties.get_control(:usable).value = !(@anim[:ignore] || false)
# TODO: Populate flags.
refresh_component(:animation_properties) # This sets the :move control's value
# Interaction loop
ret = nil
captured = nil
loop do
Graphics.update
Input.update
anim_properties.update
if anim_properties.changed?
break if anim_properties.values.keys.include?(:close)
anim_properties.values.each_pair do |property, value|
apply_changed_value(:animation_properties, property, value)
end
anim_properties.clear_changed
end
break if !anim_properties.busy? && Input.trigger?(Input::BACK)
anim_properties.repaint
end
# Dispose and return
bg_bitmap.dispose
@pop_up_bg_bitmap.visible = false
anim_properties.clear_changed
anim_properties.visible = false
end
end

View File

@@ -118,7 +118,7 @@ class AnimationEditor::AnimationSelector
name = ""
name += _INTL("[Foe]") + " " if anim.opposing_animation?
name += "[#{anim.version}]" + " " if anim.version > 0
name += anim.name
name += (anim.name || anim.move)
if anim.move_animation?
move_name = GameData::Move.try_get(anim.move)&.name || anim.move
@move_list.push([anim.move, move_name]) if !@move_animations[anim.move]
@@ -161,12 +161,12 @@ class AnimationEditor::AnimationSelector
@components.get_control(:moves).disable
@components.get_control(:commons).enable
@components.get_control(:moves_list).values = @move_list
@components.get_control(:moves_label).label = _INTL("Moves")
@components.get_control(:moves_label).text = _INTL("Moves")
when 1
@components.get_control(:moves).enable
@components.get_control(:commons).disable
@components.get_control(:moves_list).values = @common_list
@components.get_control(:moves_label).label = _INTL("Common animations")
@components.get_control(:moves_label).text = _INTL("Common animations")
end
# Put the correct list into the animations list
@components.get_control(:animations_list).values = selected_move_animations

View File

@@ -87,6 +87,14 @@ module AnimationEditor::ParticleDataHelper
value = GameData::Animation::PARTICLE_KEYFRAME_DEFAULT_VALUES[:visible]
value = true if ["User", "Target", "SE"].include?(particle[:name])
ret = []
if !["User", "Target", "SE"].include?(particle[:name])
earliest = duration
particle.each_pair do |prop, value|
next if !value.is_a?(Array) || value.length == 0
earliest = value[0][0] if earliest > value[0][0]
end
ret[earliest] = true
end
if particle[:visible]
particle[:visible].each { |cmd| ret[cmd[0]] = cmd[2] }
end