Anim Editor: graphics and audio now support subfolders, other tweaks

This commit is contained in:
Maruno17
2024-04-07 19:51:17 +01:00
parent 29140a517e
commit f34f9040c6
244 changed files with 1900 additions and 1891 deletions

View File

@@ -458,7 +458,7 @@ class AnimationEditor
else
component.get_control(:move_particle_up).enable
end
if cur_index < 0 || cur_index >= @anim[:particles].length - 2
if cur_index < 0 || cur_index >= @anim[:particles].length - 1 || @anim[:particles][cur_index][:name] == "SE"
component.get_control(:move_particle_down).disable
else
component.get_control(:move_particle_down).enable
@@ -563,15 +563,11 @@ class AnimationEditor
when :particle_list
case property
when :add_particle
new_idx = particle_index
if new_idx >= 0
new_idx += 1
new_idx = @anim[:particles].length - 1 if new_idx == 0 || new_idx >= @anim[:particles].length
end
new_idx = particle_index + 1
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].particle_index = new_idx
@components[:particle_list].keyframe = -1
refresh
when :move_particle_up
@@ -630,6 +626,7 @@ class AnimationEditor
when :has_user
@anim[:no_user] = !value
if @anim[:no_user]
user_idx = @anim[:particles].index { |particle| particle[:name] == "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])
@@ -640,7 +637,7 @@ class AnimationEditor
end
particle[:user_cry] = nil if particle[:name] == "SE"
end
@components[:particle_list].delete_particle(0)
@components[:particle_list].delete_particle(user_idx)
elsif @anim[:particles].none? { |particle| particle[:name] == "User" }
@anim[:particles].insert(0, {
:name => "User", :focus => :user, :graphic => "USER"
@@ -652,6 +649,7 @@ class AnimationEditor
when :has_target
@anim[:no_target] = !value
if @anim[:no_target]
target_idx = @anim[:particles].index { |particle| particle[:name] == "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])
@@ -662,12 +660,12 @@ class AnimationEditor
end
particle[:target_cry] = nil if particle[:name] == "SE"
end
@components[:particle_list].delete_particle(@anim[:no_user] ? 0 : 1)
@components[:particle_list].delete_particle(target_idx)
elsif @anim[:particles].none? { |particle| particle[:name] == "Target" }
@anim[:particles].insert((@anim[:no_user] ? 0 : 1), {
@anim[:particles].insert(0, {
:name => "Target", :focus => :target, :graphic => "TARGET"
})
@components[:particle_list].add_particle((@anim[:no_user] ? 0 : 1))
@components[:particle_list].add_particle(0)
end
@components[:particle_list].set_particles(@anim[:particles])
refresh

View File

@@ -126,26 +126,24 @@ class AnimationEditor
#-----------------------------------------------------------------------------
# Generates a list of all files in the given folder which have a file
# extension that matches one in exts. Removes any files from the list whose
# filename is the same as one in prepends (case insensitive), and then adds
# all the files in prepends to the start of the list.
def get_all_files_in_folder_and_prepend(folder, exts, prepends)
# Generates a list of all files in the given folder and its subfolders which
# have a file extension that matches one in exts. Removes any files from the
# list whose filename is the same as one in blacklist (case insensitive).
def get_all_files_in_folder(folder, exts, blacklist)
ret = []
Dir.chdir(folder) do
exts.each do |type|
Dir.glob(type) { |f| ret.push([File.basename(f, ".*"), f]) }
end
Dir.all(folder).each do |f|
next if !exts.include?(File.extname(f))
file = f.sub(folder + "/", "")
ret.push([file.sub(File.extname(file), ""), file])
end
ret.delete_if { |f| prepends.any? { |add| add[0] == f[0].upcase } }
ret.delete_if { |f| blacklist.any? { |add| add.upcase == f[0].upcase } }
ret.sort! { |a, b| a[0].downcase <=> b[0].downcase }
ret.prepend(*prepends)
return ret
end
def choose_graphic_file(selected)
selected ||= ""
sprite_folder = "Graphics/Battle animations/"
sprite_folder = "Graphics/Battle animations"
# Show pop-up window
@pop_up_bg_bitmap.visible = true
bg_bitmap = create_pop_up_window(GRAPHIC_CHOOSER_WINDOW_WIDTH, GRAPHIC_CHOOSER_WINDOW_HEIGHT)
@@ -154,17 +152,26 @@ class AnimationEditor
# Draw box around list control
list = graphic_chooser.get_control(:list)
# Get a list of files
files = get_all_files_in_folder_and_prepend(
sprite_folder, ["*.png", "*.jpg", "*.jpeg"],
[["USER", _INTL("[[User's sprite]]")],
["USER_OPP", _INTL("[[User's other side sprite]]")],
["USER_FRONT", _INTL("[[User's front sprite]]")],
["USER_BACK", _INTL("[[User's back sprite]]")],
["TARGET", _INTL("[[Target's sprite]]")],
["TARGET_OPP", _INTL("[[Target's other side sprite]]")],
["TARGET_FRONT", _INTL("[[Target's front sprite]]")],
["TARGET_BACK", _INTL("[[Target's back sprite]]")]]
files = get_all_files_in_folder(
sprite_folder, [".png", ".jpg", ".jpeg"],
["USER", "USER_OPP", "USER_FRONT", "USER_BACK", "TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"]
)
if !@anim[:no_target]
files.prepend(
["TARGET", _INTL("[[Target's sprite]]")],
["TARGET_OPP", _INTL("[[Target's other side sprite]]")],
["TARGET_FRONT", _INTL("[[Target's front sprite]]")],
["TARGET_BACK", _INTL("[[Target's back sprite]]")]
)
end
if !@anim[:no_user]
files.prepend(
["USER", _INTL("[[User's sprite]]")],
["USER_OPP", _INTL("[[User's other side sprite]]")],
["USER_FRONT", _INTL("[[User's front sprite]]")],
["USER_BACK", _INTL("[[User's back sprite]]")]
)
end
idx = 0
files.each_with_index do |f, i|
next if f[0] != selected
@@ -185,7 +192,7 @@ class AnimationEditor
preview_bitmap = nil
set_preview_graphic = lambda do |sprite, filename|
preview_bitmap&.dispose
folder = sprite_folder
folder = sprite_folder + "/"
fname = filename
if ["USER", "USER_BACK", "USER_FRONT", "USER_OPP",
"TARGET", "TARGET_FRONT", "TARGET_BACK", "TARGET_OPP"].include?(filename)
@@ -260,7 +267,7 @@ class AnimationEditor
def choose_audio_file(selected, volume = 100, pitch = 100)
selected ||= ""
audio_folder = "Audio/SE/Anim/"
audio_folder = "Audio/SE/Anim"
# Show pop-up window
@pop_up_bg_bitmap.visible = true
bg_bitmap = create_pop_up_window(AUDIO_CHOOSER_WINDOW_WIDTH, AUDIO_CHOOSER_WINDOW_HEIGHT)
@@ -269,11 +276,9 @@ class AnimationEditor
# Draw box around list control
list = audio_chooser.get_control(:list)
# Get a list of files
files = get_all_files_in_folder_and_prepend(
audio_folder, ["*.wav", "*.ogg", "*.mp3", "*.wma"],
[["USER", _INTL("[[User's cry]]")],
["TARGET", _INTL("[[Target's cry]]")]]
)
files = get_all_files_in_folder(audio_folder, [".wav", ".ogg", ".mp3", ".wma"], ["USER", "TARGET"])
files.prepend(["TARGET", _INTL("[[Target's cry]]")]) if !@anim[:no_target]
files.prepend(["USER", _INTL("[[User's cry]]")]) if !@anim[:no_user]
idx = 0
files.each_with_index do |f, i|
next if f[0] != selected

View File

@@ -337,7 +337,7 @@ AnimationEditor::SidePanes.add_property(:se_pane, :list, {
pane.add_control_at(:list, list, 3, 28)
},
:refresh_value => proc { |control, editor|
se_particle = editor.anim[:particles].select { |ptcl| ptcl[:name] == "SE" }[0]
se_particle = editor.anim[:particles].select { |particle| particle[:name] == "SE" }[0]
keyframe = editor.keyframe
# Populate list of files
list = []
@@ -542,7 +542,7 @@ AnimationEditor::SidePanes.add_property(:particle_pane, :duplicate, {
pane.add_button(:duplicate, _INTL("Duplicate this particle"))
},
:refresh_value => proc { |control, editor|
if ["SE"].include?(editor.anim[:particles][editor.particle_index][:name])
if editor.anim[:particles][editor.particle_index][:name] == "SE"
control.disable
else
control.enable

View File

@@ -6,10 +6,10 @@ module AnimationEditor::ParticleDataHelper
def get_duration(particles)
ret = 0
particles.each do |p|
p.each_pair do |cmd, val|
next if !val.is_a?(Array) || val.length == 0
max = val.last[0] + val.last[1] # Keyframe + duration
particles.each do |particle|
particle.each_pair do |property, value|
next if !value.is_a?(Array) || value.length == 0
max = value.last[0] + value.last[1] # Keyframe + duration
ret = max if ret < max
end
end
@@ -133,9 +133,9 @@ module AnimationEditor::ParticleDataHelper
def get_particle_commands_timeline(particle)
ret = []
durations = []
particle.each_pair do |prop, val|
next if !val.is_a?(Array)
val.each do |cmd|
particle.each_pair do |property, value|
next if !value.is_a?(Array)
value.each do |cmd|
ret[cmd[0]] = true
if cmd[1] > 0
ret[cmd[0] + cmd[1]] = true
@@ -189,9 +189,9 @@ module AnimationEditor::ParticleDataHelper
def has_se_command_at?(particles, frame)
ret = false
se_particle = particles.select { |ptcl| ptcl[:name] == "SE" }[0]
se_particle = particles.select { |particle| particle[:name] == "SE" }[0]
if se_particle
se_particle.each_pair do |prop, values|
se_particle.each_pair do |property, values|
next if !values.is_a?(Array) || values.length == 0
ret = values.any? { |value| value[0] == frame }
break if ret
@@ -496,16 +496,15 @@ module AnimationEditor::ParticleDataHelper
:graphic => GameData::Animation::PARTICLE_DEFAULT_VALUES[:graphic],
:focus => GameData::Animation::PARTICLE_DEFAULT_VALUES[:focus]
}
if index > 0 && index <= particles.length - 1
old_particle = particles[index - 1]
new_particle[:focus] = old_particle[:focus]
if index > 0 && index <= particles.length && particles[index - 1][:name] != "SE"
new_particle[:focus] = particles[index - 1][:focus]
end
index = particles.length - 1 if index < 0
index = particles.length if index < 0
particles.insert(index, new_particle)
end
# Copies the particle at index and inserts the copy immediately after that
# index.
# index. This assumes the original particle can be copied, i.e. isn't "SE".
def duplicate_particle(particles, index)
new_particle = {}
particles[index].each_pair do |key, value|
@@ -524,7 +523,8 @@ module AnimationEditor::ParticleDataHelper
particles[index1], particles[index2] = particles[index2], particles[index1]
end
# Deletes the particle at the given index
# Deletes the particle at the given index. This assumes the particle can be
# deleted, i.e. isn't "User"/"Target"/"SE".
def delete_particle(particles, index)
particles[index] = nil
particles.compact!

View File

@@ -13,11 +13,14 @@
class AnimationEditor::Canvas < Sprite
attr_reader :values
FRAME_SIZE = 48
def initialize(viewport, anim, settings)
super(viewport)
@anim = anim
@settings = settings
@keyframe = 0
@display_keyframe = 0
@selected_particle = -2
@captured = nil
@user_coords = []
@@ -54,7 +57,7 @@ class AnimationEditor::Canvas < Sprite
def initialize_particle_frames
# Frame for selected particle
@sel_frame_bitmap = Bitmap.new(64, 64)
@sel_frame_bitmap = Bitmap.new(FRAME_SIZE, FRAME_SIZE)
@sel_frame_bitmap.outline_rect(0, 0, @sel_frame_bitmap.width, @sel_frame_bitmap.height, Color.new(0, 0, 0, 64))
@sel_frame_bitmap.outline_rect(2, 2, @sel_frame_bitmap.width - 4, @sel_frame_bitmap.height - 4, Color.new(0, 0, 0, 64))
@sel_frame_sprite = Sprite.new(viewport)
@@ -63,7 +66,7 @@ class AnimationEditor::Canvas < Sprite
@sel_frame_sprite.ox = @sel_frame_bitmap.width / 2
@sel_frame_sprite.oy = @sel_frame_bitmap.height / 2
# Frame for other particles
@frame_bitmap = Bitmap.new(64, 64)
@frame_bitmap = Bitmap.new(FRAME_SIZE, FRAME_SIZE)
@frame_bitmap.outline_rect(1, 1, @frame_bitmap.width - 2, @frame_bitmap.height - 2, Color.new(0, 0, 0, 64))
@battler_frame_sprites = []
@frame_sprites = []
@@ -150,8 +153,10 @@ class AnimationEditor::Canvas < Sprite
end
def keyframe=(val)
return if @keyframe == val || val < 0
return if @keyframe == val
@keyframe = val
return if val < 0
@display_keyframe = val
refresh
end
@@ -379,7 +384,7 @@ class AnimationEditor::Canvas < Sprite
# Get sprite
spr, frame = get_sprite_and_frame(index, target_idx)
# Calculate all values of particle at the current keyframe
values = AnimationEditor::ParticleDataHelper.get_all_keyframe_particle_values(particle, @keyframe)
values = AnimationEditor::ParticleDataHelper.get_all_keyframe_particle_values(particle, @display_keyframe)
values.each_pair do |property, val|
values[property] = val[0]
end
@@ -429,28 +434,12 @@ class AnimationEditor::Canvas < Sprite
when "USER_BACK"
spr.bitmap = @user_bitmap_back
when "TARGET"
if target_idx < 0
raise _INTL("Particle \"{1}\" was given a graphic of \"TARGET\" but its focus doesn't include a target.",
particle[:name])
end
spr.bitmap = (target_idx.even?) ? @target_bitmap_back : @target_bitmap_front
when "TARGET_OPP"
if target_idx < 0
raise _INTL("Particle \"{1}\" was given a graphic of \"TARGET_OPP\" but its focus doesn't include a target.",
particle[:name])
end
spr.bitmap = (target_idx.even?) ? @target_bitmap_front : @target_bitmap_back
when "TARGET_FRONT"
if target_idx < 0
raise _INTL("Particle \"{1}\" was given a graphic of \"TARGET_FRONT\" but its focus doesn't include a target.",
particle[:name])
end
spr.bitmap = @target_bitmap_front
when "TARGET_BACK"
if target_idx < 0
raise _INTL("Particle \"{1}\" was given a graphic of \"TARGET_BACK\" but its focus doesn't include a target.",
particle[:name])
end
spr.bitmap = @target_bitmap_back
end
spr.ox = spr.bitmap.width / 2
@@ -520,7 +509,10 @@ class AnimationEditor::Canvas < Sprite
# Position frame over spr
frame.x = spr.x
frame.y = spr.y
case @anim[:particles][index][:graphic]
# TODO: Offset frame.x and frame.y for screen-sized sprites with a
# screen-based focus, and for sprites whose graphic has "[bottom]" in
# its name.
case particle[:graphic]
when "USER", "USER_OPP", "USER_FRONT", "USER_BACK",
"TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"
frame.y -= spr.bitmap.height / 2
@@ -538,7 +530,8 @@ class AnimationEditor::Canvas < Sprite
end
def refresh_particle_frame
return if @selected_particle < 0 || @selected_particle >= @anim[:particles].length - 1
return if @selected_particle < 0 || @selected_particle >= @anim[:particles].length ||
@anim[:particles][@selected_particle][:name] == "SE"
focus = @anim[:particles][@selected_particle][:focus]
frame_color = AnimationEditor::ParticleList::CONTROL_BG_COLORS[focus] || Color.magenta
@sel_frame_bitmap.outline_rect(1, 1, @sel_frame_bitmap.width - 2, @sel_frame_bitmap.height - 2, frame_color)
@@ -587,8 +580,10 @@ class AnimationEditor::Canvas < Sprite
mouse_x < @sel_frame_sprite.x - @sel_frame_sprite.ox + @sel_frame_sprite.width &&
mouse_y >= @sel_frame_sprite.y - @sel_frame_sprite.oy &&
mouse_y < @sel_frame_sprite.y - @sel_frame_sprite.oy + @sel_frame_sprite.height
@captured = [@sel_frame_sprite.x, @sel_frame_sprite.y,
@sel_frame_sprite.x - mouse_x, @sel_frame_sprite.y - mouse_y]
if @keyframe >= 0
@captured = [@sel_frame_sprite.x, @sel_frame_sprite.y,
@sel_frame_sprite.x - mouse_x, @sel_frame_sprite.y - mouse_y]
end
return
end
# Find closest particle to mouse
@@ -706,7 +701,8 @@ class AnimationEditor::Canvas < Sprite
end
def update_selected_particle_frame
if @selected_particle < 0 || @selected_particle >= @anim[:particles].length - 1
if @selected_particle < 0 || @selected_particle >= @anim[:particles].length ||
@anim[:particles][@selected_particle][:name] == "SE"
@sel_frame_sprite.visible = false
return
end
@@ -721,7 +717,7 @@ class AnimationEditor::Canvas < Sprite
@anim[:particles][@selected_particle][:name]) if !target
else
target = @particle_sprites[@selected_particle]
target = target[target_indices[0]] if target&.is_a?(Array)
target = target[first_target_index] if target&.is_a?(Array)
end
if !target || !target.visible
@sel_frame_sprite.visible = false
@@ -730,6 +726,9 @@ class AnimationEditor::Canvas < Sprite
@sel_frame_sprite.visible = true
@sel_frame_sprite.x = target.x
@sel_frame_sprite.y = target.y
# TODO: Offset sel_frame_sprite.x and sel_frame_sprite.y for screen-sized
# sprites with a screen-based focus, and for sprites whose graphic has
# "[bottom]" in its name.
case @anim[:particles][@selected_particle][:graphic]
when "USER", "USER_OPP", "USER_FRONT", "USER_BACK",
"TARGET", "TARGET_OPP", "TARGET_FRONT", "TARGET_BACK"

View File

@@ -202,8 +202,7 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
def particle_index=(val)
old_index = @row_index
@row_index = @particle_list.index { |row| (row.is_a?(Array) && row[0] == val) ||
(!row.is_a?(Array) && row == val) }
@row_index = @particle_list.index { |row| !row.is_a?(Array) && row == val }
return if @row_index == old_index
invalidate
scroll_to_row(@row_index)
@@ -690,6 +689,7 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
spr.bitmap.fill_rect(10 + (i * 2), 12, 1, 1, Color.black)
end
elsif @expanded_particles.include?(p_index)
# Draw down-pointing arrow
11.times do |i|
j = (i == 0 || i == 10) ? 1 : 0
h = [2, 4, 5, 6, 7, 8, 7, 6, 5, 4, 2][i]
@@ -697,6 +697,7 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
spr.bitmap.fill_rect(5 + i, 9 + j, 1, h, Color.black)
end
elsif particle_data[:name] != "SE"
# Draw right-pointing arrow
11.times do |j|
i = (j == 0 || j == 10) ? 1 : 0
w = [2, 4, 5, 6, 7, 8, 7, 6, 5, 4, 2][j]
@@ -1049,13 +1050,19 @@ class AnimationEditor::ParticleList < UIControls::BaseControl
end
elsif Input.repeat?(Input::DOWN)
if @row_index < @particle_list.length - 1
old_row_index = @row_index
loop do
@row_index += 1
break if !@particle_list[@row_index].is_a?(Array)
break if !@particle_list[@row_index].is_a?(Array) || @row_index >= @particle_list.length
end
if @row_index < @particle_list.length
@keyframe = 0 if @keyframe < 0 && !@particle_list[@row_index].is_a?(Array) &&
@particles[@particle_list[@row_index]][:name] == "SE"
scroll_to_row(@row_index)
set_changed
else
@row_index = old_row_index
end
@keyframe = 0 if @row_index >= @particle_list.length - 1 && @keyframe < 0
scroll_to_row(@row_index)
set_changed
end
end
# Mouse scroll wheel