mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 21:24:59 +00:00
Added the animation properties pop-up window
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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?).
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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
|
||||
# }
|
||||
# })
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user