From 94f0a9c8d065811aa78695d0694cf57a9a2067a7 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Tue, 2 Jan 2024 23:34:59 +0000 Subject: [PATCH] Made New/Copy/Delete buttons in animation selector screen work, split animations compiler into its own compiler --- .../902_Anim GameData/001_Animation.rb | 21 ++++ .../903_Anim Compiler/001_Anim compiler.rb | 78 +++++++++--- .../904_Anim Editor/010_AnimationSelector.rb | 118 ++++++++++++++++-- 3 files changed, 193 insertions(+), 24 deletions(-) diff --git a/Data/Scripts/902_Anim GameData/001_Animation.rb b/Data/Scripts/902_Anim GameData/001_Animation.rb index 4bee10e5b..77bb17568 100644 --- a/Data/Scripts/902_Anim GameData/001_Animation.rb +++ b/Data/Scripts/902_Anim GameData/001_Animation.rb @@ -159,6 +159,25 @@ module GameData DATA[(id_num >= 0) ? id_num : DATA.keys.length] = self.new(hash) end + def self.new_hash(anim_type = 0, move = nil) + ret = {} + ret[:type] = (anim_type == 0) ? :move : :common + ret[:move] = (anim_type == 0) ? "STRUGGLE" : "Shiny" + ret[:move] = move if !move.nil? + ret[:version] = 0 + ret[:name] = _INTL("New animation") + ret[:no_target] = false + ret[:ignore] = false + ret[:particles] = [ + {:name => "User", :focus => :user, :graphic => "USER"}, + {:name => "Target", :focus => :target, :graphic => "TARGET"}, + {:name => "SE"} + ] + ret[:flags] = [] + ret[:pbs_path] = "New animation" + return ret + end + def initialize(hash) # NOTE: hash has an :id entry, but it's unused here. @type = hash[:type] @@ -180,6 +199,8 @@ module GameData ret[:move] = @move ret[:version] = @version ret[:name] = @name + ret[:no_target] = @no_target + ret[:ignore] = @ignore ret[:particles] = [] # Clone the @particles array, which is nested hashes and arrays @particles.each do |particle| new_p = {} diff --git a/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb b/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb index a80afbc7c..a5fe92709 100644 --- a/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb +++ b/Data/Scripts/903_Anim Compiler/001_Anim compiler.rb @@ -202,6 +202,7 @@ end #=============================================================================== # Hook into the regular Compiler to also compile animation PBS files. +# This is a separate Compiler that runs after the regular one. #=============================================================================== module Compiler module_function @@ -215,24 +216,69 @@ module Compiler end class << self - if !method_defined?(:__new_anims__get_all_pbs_files_to_compile) - alias_method :__new_anims__get_all_pbs_files_to_compile, :get_all_pbs_files_to_compile - end - if !method_defined?(:__new_anims__compile_pbs_files) - alias_method :__new_anims__compile_pbs_files, :compile_pbs_files + if !method_defined?(:__new_anims_main) + alias_method :__new_anims_main, :main end end - def get_all_pbs_files_to_compile - ret = __new_anims__get_all_pbs_files_to_compile - extra = get_animation_pbs_files_to_compile - ret[:Animation] = [nil, extra] - return ret - end - - def compile_pbs_files - __new_anims__compile_pbs_files - text_files = get_animation_pbs_files_to_compile - compile_battle_animations(*text_files) + def main + __new_anims_main + return if !$DEBUG + begin + Console.echo_h1(_INTL("Checking new animations data")) + must_compile = false + data_file = "animations.dat" + text_files = get_animation_pbs_files_to_compile + latest_data_time = 0 + latest_text_time = 0 + # Check data file for its latest modify time + if FileTest.exist?("Data/" + data_file) + begin + File.open("Data/#{data_file}") do |file| + latest_data_time = [latest_data_time, file.mtime.to_i].max + end + rescue SystemCallError + must_compile = true + end + else + must_compile = true if text_files.length > 0 + end + # Check PBS files for their latest modify time + text_files.each do |filepath| + begin + File.open(filepath) do |file| + latest_text_time = [latest_text_time, file.mtime.to_i].max + end + rescue SystemCallError + end + end + # Decide to compile if a PBS file was edited more recently than the .dat file + must_compile |= (latest_text_time >= latest_data_time) + # Should recompile if holding Ctrl + Input.update + must_compile = true if $full_compile || Input.press?(Input::CTRL) + # Delete old data file in preparation for recompiling + if must_compile + begin + File.delete("Data/#{data_file}") if FileTest.exist?("Data/#{data_file}") + rescue SystemCallError + end + # Recompile all data + compile_battle_animations(*text_files) + else + Console.echoln_li(_INTL("New animations data were not compiled")) + end + echoln "" + rescue Exception + e = $! + raise e if e.class.to_s == "Reset" || e.is_a?(Reset) || e.is_a?(SystemExit) + pbPrintException(e) + begin + File.delete("Data/#{data_file}") if FileTest.exist?("Data/#{data_file}") + rescue SystemCallError + end + raise Reset.new if e.is_a?(Hangup) + raise "Unknown exception when compiling animations." + end end end diff --git a/Data/Scripts/904_Anim Editor/010_AnimationSelector.rb b/Data/Scripts/904_Anim Editor/010_AnimationSelector.rb index 5431e666c..cae1b1a6d 100644 --- a/Data/Scripts/904_Anim Editor/010_AnimationSelector.rb +++ b/Data/Scripts/904_Anim Editor/010_AnimationSelector.rb @@ -2,6 +2,8 @@ # #=============================================================================== class AnimationEditor::AnimationSelector + BORDER_THICKNESS = 4 + QUIT_BUTTON_WIDTH = 80 QUIT_BUTTON_HEIGHT = 30 @@ -25,11 +27,23 @@ class AnimationEditor::AnimationSelector ACTION_BUTTON_X = ANIMATIONS_LIST_X + ANIMATIONS_LIST_WIDTH + 4 ACTION_BUTTON_Y = TYPE_BUTTONS_Y + ((ANIMATIONS_LIST_HEIGHT - (ACTION_BUTTON_HEIGHT * 3)) / 2) + 4 + # Pop-up window + MESSAGE_BOX_WIDTH = AnimationEditor::WINDOW_WIDTH * 3 / 4 + MESSAGE_BOX_HEIGHT = 160 + MESSAGE_BOX_BUTTON_WIDTH = 150 + MESSAGE_BOX_BUTTON_HEIGHT = 32 + MESSAGE_BOX_SPACING = 16 + def initialize generate_lists @viewport = Viewport.new(0, 0, AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT) @viewport.z = 99999 + @pop_up_viewport = Viewport.new(0, 0, AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT) + @pop_up_viewport.z = @viewport.z + 50 @screen_bitmap = BitmapSprite.new(AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT, @viewport) + @pop_up_bg_bitmap = BitmapSprite.new(AnimationEditor::WINDOW_WIDTH, AnimationEditor::WINDOW_HEIGHT, @pop_up_viewport) + @pop_up_bg_bitmap.z = -100 + @pop_up_bg_bitmap.visible = false draw_editor_background @animation_type = 0 # 0=move, 1=common @quit = false @@ -39,8 +53,10 @@ class AnimationEditor::AnimationSelector def dispose @screen_bitmap.dispose + @pop_up_bg_bitmap.dispose @components.dispose @viewport.dispose + @pop_up_viewport.dispose end LABEL_OFFSET_X = -4 @@ -108,6 +124,80 @@ class AnimationEditor::AnimationSelector #----------------------------------------------------------------------------- + def create_pop_up_window(width, height) + ret = BitmapSprite.new(width + (BORDER_THICKNESS * 2), + height + (BORDER_THICKNESS * 2), @pop_up_viewport) + ret.x = (AnimationEditor::WINDOW_WIDTH - ret.width) / 2 + ret.y = (AnimationEditor::WINDOW_HEIGHT - ret.height) / 2 + ret.z = -1 + ret.bitmap.font.color = Color.black + ret.bitmap.font.size = 18 + # Draw pop-up box border + ret.bitmap.border_rect(BORDER_THICKNESS, BORDER_THICKNESS, width, height, + BORDER_THICKNESS, Color.white, Color.black) + # Fill pop-up box with white + ret.bitmap.fill_rect(BORDER_THICKNESS, BORDER_THICKNESS, width, height, Color.white) + return ret + end + + #----------------------------------------------------------------------------- + + def message(text, *options) + @pop_up_bg_bitmap.visible = true + msg_bitmap = create_pop_up_window(MESSAGE_BOX_WIDTH, MESSAGE_BOX_HEIGHT) + # Draw text + text_size = msg_bitmap.bitmap.text_size(text) + msg_bitmap.bitmap.draw_text(0, (msg_bitmap.height / 2) - MESSAGE_BOX_BUTTON_HEIGHT, + msg_bitmap.width, text_size.height, text, 1) + # Create buttons + buttons = [] + options.each_with_index do |option, i| + btn = UIControls::Button.new(MESSAGE_BOX_BUTTON_WIDTH, MESSAGE_BOX_BUTTON_HEIGHT, @pop_up_viewport, option[1]) + btn.x = msg_bitmap.x + (msg_bitmap.width - (MESSAGE_BOX_BUTTON_WIDTH * options.length)) / 2 + btn.x += MESSAGE_BOX_BUTTON_WIDTH * i + btn.y = msg_bitmap.y + msg_bitmap.height - MESSAGE_BOX_BUTTON_HEIGHT - MESSAGE_BOX_SPACING + btn.set_fixed_size + btn.set_interactive_rects + buttons.push([option[0], btn]) + end + # Interaction loop + ret = nil + captured = nil + loop do + Graphics.update + Input.update + if captured + captured.update + captured = nil if !captured.busy? + else + buttons.each do |btn| + btn[1].update + captured = btn[1] if btn[1].busy? + end + end + buttons.each do |btn| + next if !btn[1].changed? + ret = btn[0] + break + end + ret = :cancel if Input.trigger?(Input::BACK) + break if ret + buttons.each { |btn| btn[1].repaint } + end + # Dispose and return + buttons.each { |btn| btn[1].dispose } + buttons.clear + msg_bitmap.dispose + @pop_up_bg_bitmap.visible = false + return ret + end + + def confirm_message(text) + return message(text, [:yes, _INTL("Yes")], [:no, _INTL("No")]) == :yes + end + + #----------------------------------------------------------------------------- + def generate_lists @move_list = [] @common_list = [] @@ -190,11 +280,11 @@ class AnimationEditor::AnimationSelector @quit = true return # Don't need to refresh the screen when :new - # TODO: New animation. Create a new animation hash with some default - # contents, go into the edit screen and immediately open the - # animation properties pop-up window. Use the first available ID - # number from GameData::Animation for it. Don't register the - # animation hash here, though. + new_anim = GameData::Animation.new_hash(@animation_type, @components.get_control(:moves_list).value) + new_id = GameData::Animation.keys.max + 1 + screen = AnimationEditor.new(new_id, new_anim) + screen.run + generate_lists when :moves @animation_type = 0 @components.get_control(:moves_list).selected = -1 @@ -213,12 +303,24 @@ class AnimationEditor::AnimationSelector when :copy anim_id = selected_animation_id if anim_id - # TODO: Copy animation. Append "(copy)" to its name. + new_anim = GameData::Animation.get(anim_id).clone_as_hash + new_anim[:name] += " " + _INTL("(copy)") if !nil_or_empty?(new_anim[:name]) + new_id = GameData::Animation.keys.max + 1 + screen = AnimationEditor.new(new_id, new_anim) + screen.run + generate_lists end when :delete anim_id = selected_animation_id - if anim_id - # TODO: Delete animation. Ask the user if they're sure. + if anim_id && confirm_message(_INTL("Are you sure you want to delete this animation?")) + pbs_path = GameData::Animation.get(anim_id).pbs_path + GameData::Animation::DATA.delete(anim_id) + if GameData::Animation::DATA.any? { |_key, anim| anim.pbs_path == pbs_path } + Compiler.write_battle_animation_file(pbs_path) + elsif FileTest.exist?("PBS/Animations/" + pbs_path + ".txt") + File.delete("PBS/Animations/" + pbs_path + ".txt") + end + generate_lists end end refresh