mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 13:15:01 +00:00
Created animation PBS file compiler and writer
This commit is contained in:
@@ -6,3 +6,91 @@ class Bitmap
|
||||
fill_rect(x + width - thickness, y, thickness, height, color)
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Fixed Compiler.pbWriteCsvRecord to make it detect enums first, allowing enum
|
||||
# values to be turned into symbols/booleans/whatever instead of just numbers.
|
||||
#===============================================================================
|
||||
module Compiler
|
||||
module_function
|
||||
|
||||
def pbWriteCsvRecord(record, file, schema)
|
||||
rec = (record.is_a?(Array)) ? record.flatten : [record]
|
||||
start = (["*", "^"].include?(schema[1][0, 1])) ? 1 : 0
|
||||
index = -1
|
||||
loop do
|
||||
(start...schema[1].length).each do |i|
|
||||
index += 1
|
||||
value = rec[index]
|
||||
if schema[1][i, 1][/[A-Z]/] # Optional
|
||||
# Check the rest of the values for non-nil things
|
||||
later_value_found = false
|
||||
(index...rec.length).each do |j|
|
||||
later_value_found = true if !rec[j].nil?
|
||||
break if later_value_found
|
||||
end
|
||||
if !later_value_found
|
||||
start = -1
|
||||
break
|
||||
end
|
||||
end
|
||||
file.write(",") if index > 0
|
||||
next if value.nil?
|
||||
case schema[1][i, 1]
|
||||
when "e", "E" # Enumerable
|
||||
enumer = schema[2 + i]
|
||||
case enumer
|
||||
when Array
|
||||
file.write(enumer[value])
|
||||
when Symbol, String
|
||||
mod = Object.const_get(enumer.to_sym)
|
||||
file.write(getConstantName(mod, value))
|
||||
when Module
|
||||
file.write(getConstantName(enumer, value))
|
||||
when Hash
|
||||
enumer.each_key do |key|
|
||||
next if enumer[key] != value
|
||||
file.write(key)
|
||||
break
|
||||
end
|
||||
end
|
||||
when "y", "Y" # Enumerable or integer
|
||||
enumer = schema[2 + i]
|
||||
case enumer
|
||||
when Array
|
||||
file.write((enumer[value].nil?) ? value : enumer[value])
|
||||
when Symbol, String
|
||||
mod = Object.const_get(enumer.to_sym)
|
||||
file.write(getConstantNameOrValue(mod, value))
|
||||
when Module
|
||||
file.write(getConstantNameOrValue(enumer, value))
|
||||
when Hash
|
||||
hasenum = false
|
||||
enumer.each_key do |key|
|
||||
next if enumer[key] != value
|
||||
file.write(key)
|
||||
hasenum = true
|
||||
break
|
||||
end
|
||||
file.write(value) unless hasenum
|
||||
end
|
||||
else
|
||||
if value.is_a?(String)
|
||||
file.write((schema[1][i, 1].downcase == "q") ? value : csvQuote(value))
|
||||
elsif value.is_a?(Symbol)
|
||||
file.write(csvQuote(value.to_s))
|
||||
elsif value == true
|
||||
file.write("true")
|
||||
elsif value == false
|
||||
file.write("false")
|
||||
else
|
||||
file.write(value.inspect)
|
||||
end
|
||||
end
|
||||
end
|
||||
break if start > 0 && index >= rec.length - 1
|
||||
break if start <= 0
|
||||
end
|
||||
return record
|
||||
end
|
||||
end
|
||||
|
||||
8
Data/Scripts/900_New utilities/anim debug.rb
Normal file
8
Data/Scripts/900_New utilities/anim debug.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
MenuHandlers.add(:debug_menu, :create_animation_pbs_files, {
|
||||
"name" => _INTL("Write all animation PBS files"),
|
||||
"parent" => :files_menu,
|
||||
"description" => _INTL("Write all animation PBS files."),
|
||||
"effect" => proc {
|
||||
Compiler.write_all_battle_animations
|
||||
}
|
||||
})
|
||||
@@ -1,26 +1,91 @@
|
||||
module GameData
|
||||
class Animation
|
||||
attr_reader :name
|
||||
attr_reader :move, :type # Type is move's type; useful for filtering; move==nil means common animation
|
||||
attr_reader :type # :move, :opp_move, :common, :opp_common
|
||||
attr_reader :move # Either the move's ID or the common animation's name
|
||||
attr_reader :version # Hit number
|
||||
# TODO: Boolean for whether user is on player's side or foe's side.
|
||||
attr_reader :name # Shown in the sublist; cosmetic only
|
||||
# TODO: Boolean for not played if target is on user's side.
|
||||
attr_reader :particles
|
||||
attr_reader :flags
|
||||
# TODO: PBS filename.
|
||||
# attr_reader :pbs_filename
|
||||
attr_reader :pbs_path # Whole path minus "PBS/Animations/" at start and ".txt" at end
|
||||
|
||||
DATA = {}
|
||||
# TODO: Make sure the existence of animations.dat is optional. Currently
|
||||
# it's required.
|
||||
# DATA_FILENAME = "animations.dat"
|
||||
# PBS_BASE_FILENAME = "animations"
|
||||
DATA_FILENAME = "animations.dat"
|
||||
OPTIONAL = true
|
||||
|
||||
SCHEMA = {
|
||||
# TODO: Add support for overworld animations.
|
||||
"SectionName" => [:id, "esU", {"Move" => :move, "OppMove" => :opp_move,
|
||||
"Common" => :common, "OppCommon" => :opp_common}],
|
||||
"Name" => [:name, "s"],
|
||||
# TODO: Target (Screen, User, UserAndTarget, etc. Determines which focuses
|
||||
# a particle can be given).
|
||||
# TODO: DamageFrame (keyframe at which the battle continues, i.e. damage
|
||||
# animations start playing).
|
||||
"Flags" => [:flags, "*s"],
|
||||
"Particle" => [:particles, "s"]
|
||||
}
|
||||
# For individual particles. All actions should have "^" in them.
|
||||
# TODO: If more "SetXYZ"/"MoveXYZ" properties are added, ensure the "SetXYZ"
|
||||
# ones are given a duration of 0 in def validate_compiled_animation.
|
||||
SUB_SCHEMA = {
|
||||
# These properties cannot be changed partway through the animation.
|
||||
# TODO: "Name" isn't actually used; the name comes from the subsection
|
||||
# written between <these> and uses "Particle" above.
|
||||
# "Name" => [:name, "s"],
|
||||
"Focus" => [:focus, "e", {"User" => :user, "Target" => :target,
|
||||
"UserAndTarget" => :user_and_target, "Screen" => :screen}],
|
||||
# TODO FlipIfFoe, RotateIfFoe kinds of thing.
|
||||
|
||||
# All properties below are "Set" or "Move". "Set" has the keyframe and the
|
||||
# value, and "Move" has the keyframe, duration and the value. All are "^".
|
||||
# "Set" is turned into "Move" with a duration (second value) of 0.
|
||||
# TODO: The "MoveXYZ" commands will have optional easing (an enum).
|
||||
"SetGraphic" => [:graphic, "^us"],
|
||||
"SetFrame" => [:frame, "^uu"], # Frame within the graphic if it's a spritesheet
|
||||
"MoveFrame" => [:frame, "^uuu"],
|
||||
"SetBlending" => [:blending, "^uu"], # 0, 1 or 2
|
||||
"SetFlip" => [:flip, "^ub"],
|
||||
"SetX" => [:x, "^ui"],
|
||||
"MoveX" => [:x, "^uui"],
|
||||
"SetY" => [:y, "^ui"],
|
||||
"MoveY" => [:y, "^uui"],
|
||||
"SetZoomX" => [:zoom_x, "^uu"],
|
||||
"MoveZoomX" => [:zoom_x, "^uuu"],
|
||||
"SetZoomY" => [:zoom_y, "^uu"],
|
||||
"MoveZoomY" => [:zoom_y, "^uuu"],
|
||||
"SetAngle" => [:angle, "^ui"],
|
||||
"MoveAngle" => [:angle, "^uui"],
|
||||
"SetOpacity" => [:opacity, "^uu"],
|
||||
"MoveOpacity" => [:opacity, "^uuu"]
|
||||
# TODO: SetPriority should be an enum. There should also be a property
|
||||
# (set and move) for the sub-priority within that priority bracket.
|
||||
# "SetPriority"
|
||||
# TODO: Color.
|
||||
# TODO: Tone.
|
||||
|
||||
# TODO: Play, PlayUserCry, PlayTargetCry.
|
||||
# TODO: ScreenShake? Not sure how to work this yet. Edit def
|
||||
# validate_compiled_animation like the "SE" particle does with the
|
||||
# "Play"-type commands.
|
||||
}
|
||||
|
||||
@@cmd_to_pbs_name = nil # USed for writing animation PBS files
|
||||
|
||||
extend ClassMethodsIDNumbers
|
||||
include InstanceMethods
|
||||
|
||||
def register(hash, id = -1)
|
||||
DATA[(id >= 0) ? id : DATA.keys.length] = self.new(hash)
|
||||
singleton_class.alias_method(:__new_anim__load, :load) unless singleton_class.method_defined?(:__new_anim__load)
|
||||
def self.load
|
||||
__new_anim__load if FileTest.exist?("Data/#{self::DATA_FILENAME}")
|
||||
end
|
||||
|
||||
def self.sub_schema
|
||||
return SUB_SCHEMA
|
||||
end
|
||||
|
||||
def self.register(hash, id_num = -1)
|
||||
DATA[(id_num >= 0) ? id_num : DATA.keys.length] = self.new(hash)
|
||||
end
|
||||
|
||||
# TODO: Rewrite this to query animations from other criteria. Remember that
|
||||
@@ -45,23 +110,116 @@ module GameData
|
||||
# end
|
||||
|
||||
def initialize(hash)
|
||||
@name = hash[:name]
|
||||
@move = hash[:move]
|
||||
# NOTE: hash has an :id entry, but it's unused here.
|
||||
@type = hash[:type]
|
||||
@move = hash[:move]
|
||||
@version = hash[:version] || 0
|
||||
@particles = []
|
||||
# TODO: Copy particles info from hash somehow.
|
||||
@name = hash[:name]
|
||||
@particles = hash[:particles] || []
|
||||
@flags = hash[:flags] || []
|
||||
# TODO: Come up with a decent default PBS filename; likely the move's name
|
||||
# (for move anims) or @name (for common anims).
|
||||
@pbs_path = hash[:pbs_path] || "#{@type} - #{@move}"
|
||||
end
|
||||
|
||||
# Returns a clone of the animation in a hash format, the same as created by
|
||||
# the Compiler. This hash can be passed into self.register.
|
||||
def clone_as_hash
|
||||
ret = {}
|
||||
ret[:type] = @type
|
||||
ret[:move] = @move
|
||||
ret[:version] = @version
|
||||
ret[:name] = @name
|
||||
ret[:particles] = [] # Clone the @particles array, which is nested hashes and arrays
|
||||
@particles.each do |particle|
|
||||
new_p = {}
|
||||
particle.each_pair do |key, val|
|
||||
if val.is_a?(Array)
|
||||
new_p[val] = []
|
||||
val.each { |cmd| new_p[val].push(cmd.clone) }
|
||||
else
|
||||
new_p[key] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
ret[:flags] = @flags.clone
|
||||
ret[:pbs_path] = @pbs_path
|
||||
end
|
||||
|
||||
def move_animation?
|
||||
return !@move.nil?
|
||||
return [:move, :opp_move].include?(@type)
|
||||
end
|
||||
|
||||
# TODO: Create a def to_hash or something, which returns a hash copy version
|
||||
# of this Animation object which can be edited. This hash should be
|
||||
# able to be passed into def register (with an ID number).
|
||||
def common_animation?
|
||||
return [:common, :opp_common].include?(@type)
|
||||
end
|
||||
|
||||
alias __new_anim__get_property_for_PBS get_property_for_PBS unless method_defined?(:__new_anim__get_property_for_PBS)
|
||||
def get_property_for_PBS(key)
|
||||
ret = __new_anim__get_property_for_PBS(key)
|
||||
case key
|
||||
when "SectionName"
|
||||
ret = [@type, @move]
|
||||
ret.push(@version) if @version > 0
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def get_particle_property_for_PBS(key, index = 0)
|
||||
ret = nil
|
||||
ret = @particles[index][SUB_SCHEMA[key][0]] if SUB_SCHEMA[key]
|
||||
ret = nil if ret == false || (ret.is_a?(Array) && ret.length == 0) || ret == ""
|
||||
case key
|
||||
when "Focus"
|
||||
# The User and Target particles are hardcoded to only have their
|
||||
# corresponding foci, so they don't need writing to PBS
|
||||
if ["User", "Target"].include?(@particles[index][:name])
|
||||
ret = nil
|
||||
elsif ret
|
||||
ret = SUB_SCHEMA[key][2].key(ret)
|
||||
end
|
||||
when "AllCommands"
|
||||
# Get translations of all properties to their names as seen in PBS
|
||||
# animation files
|
||||
if !@@cmd_to_pbs_name
|
||||
@@cmd_to_pbs_name = {}
|
||||
SUB_SCHEMA.each_pair do |key, val|
|
||||
@@cmd_to_pbs_name[val[0]] ||= []
|
||||
@@cmd_to_pbs_name[val[0]].push([key, val[1].length])
|
||||
end
|
||||
# For each property translation, put "SetXYZ" before "MoveXYZ"
|
||||
@@cmd_to_pbs_name.each_value do |val|
|
||||
val.sort! { |a, b| a[1] <=> b[1] }
|
||||
val.map! { |a| a[0] }
|
||||
end
|
||||
end
|
||||
# Gather all commands into a single array
|
||||
ret = []
|
||||
@particles[index].each_pair do |key, val|
|
||||
next if !val.is_a?(Array)
|
||||
val.each do |cmd|
|
||||
new_cmd = cmd.clone
|
||||
new_cmd.insert(1, 0) if @@cmd_to_pbs_name[key].length == 1 # "SetXYZ" only
|
||||
if new_cmd[1] > 0
|
||||
ret.push([@@cmd_to_pbs_name[key][1]] + new_cmd) # ["MoveXYZ", keyframe, duration, value]
|
||||
else
|
||||
ret.push([@@cmd_to_pbs_name[key][0]] + new_cmd) # ["SetXYZ", keyframe, duration, value]
|
||||
end
|
||||
end
|
||||
end
|
||||
# Sort the array of commands by keyframe order, then by duration, then
|
||||
# by the order they're defined in SUB_SCHEMA
|
||||
ret.sort! do |a, b|
|
||||
if a[1] == b[1]
|
||||
if a[2] == b[2]
|
||||
next SUB_SCHEMA.keys.index(a[0]) <=> SUB_SCHEMA.keys.index(b[0])
|
||||
else
|
||||
next a[2] <=> b[2] # Sort by duration
|
||||
end
|
||||
else
|
||||
next a[1] <=> b[1] # Sort by keyframe
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
182
Data/Scripts/902_Anim compiler/anim pbs compiler.rb
Normal file
182
Data/Scripts/902_Anim compiler/anim pbs compiler.rb
Normal file
@@ -0,0 +1,182 @@
|
||||
module Compiler
|
||||
module_function
|
||||
|
||||
def compile_battle_animations(*paths)
|
||||
GameData::Animation::DATA.clear
|
||||
schema = GameData::Animation.schema
|
||||
sub_schema = GameData::Animation.sub_schema
|
||||
idx = 0
|
||||
# Read from PBS file(s)
|
||||
paths.each do |path|
|
||||
compile_pbs_file_message_start(path)
|
||||
file_name = path.gsub(/^PBS\/Animations\//, "").gsub(/.txt$/, "")
|
||||
data_hash = nil
|
||||
current_particle = nil
|
||||
section_name = nil
|
||||
section_line = nil
|
||||
# Read each line of the animation PBS file at a time and compile it as an
|
||||
# animation property
|
||||
pbCompilerEachPreppedLine(path) do |line, line_no|
|
||||
echo "." if idx % 100 == 0
|
||||
idx += 1
|
||||
Graphics.update if idx % 500 == 0
|
||||
FileLineData.setSection(section_name, nil, section_line)
|
||||
if line[/^\s*\[\s*(.+)\s*\]\s*$/]
|
||||
# New section [anim_type, name]
|
||||
section_name = $~[1]
|
||||
section_line = line
|
||||
if data_hash
|
||||
validate_compiled_animation(data_hash)
|
||||
GameData::Animation.register(data_hash)
|
||||
end
|
||||
FileLineData.setSection(section_name, nil, section_line)
|
||||
# Construct data hash
|
||||
data_hash = {
|
||||
:pbs_path => file_name
|
||||
}
|
||||
data_hash[schema["SectionName"][0]] = get_csv_record(section_name.clone, schema["SectionName"])
|
||||
data_hash[schema["Particle"][0]] = []
|
||||
current_particle = nil
|
||||
elsif line[/^\s*<\s*(.+)\s*>\s*$/]
|
||||
# New subsection [particle_name]
|
||||
value = get_csv_record($~[1], schema["Particle"])
|
||||
current_particle = {
|
||||
# TODO: If "Particle" is changed to be more than just a single
|
||||
# string, add more properties accordingly.
|
||||
:name => value
|
||||
}
|
||||
data_hash[schema["Particle"][0]].push(current_particle)
|
||||
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)
|
||||
end
|
||||
key = $~[1]
|
||||
if schema[key] # Property of the animation
|
||||
value = get_csv_record($~[2], schema[key])
|
||||
if schema[key][1][0] == "^"
|
||||
value = nil if value.is_a?(Array) && value.empty?
|
||||
data_hash[schema[key][0]] ||= []
|
||||
data_hash[schema[key][0]].push(value) if value
|
||||
else
|
||||
value = nil if value.is_a?(Array) && value.empty?
|
||||
data_hash[schema[key][0]] = value
|
||||
end
|
||||
elsif sub_schema[key] # Property of a particle
|
||||
if !current_particle
|
||||
raise _INTL("Particle hasn't been defined yet!\n{1}", FileLineData.linereport)
|
||||
end
|
||||
value = get_csv_record($~[2], sub_schema[key])
|
||||
if sub_schema[key][1][0] == "^"
|
||||
value = nil if value.is_a?(Array) && value.empty?
|
||||
current_particle[sub_schema[key][0]] ||= []
|
||||
current_particle[sub_schema[key][0]].push(value) if value
|
||||
else
|
||||
value = nil if value.is_a?(Array) && value.empty?
|
||||
current_particle[sub_schema[key][0]] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# Add last animation's data to records
|
||||
if data_hash
|
||||
FileLineData.setSection(section_name, nil, section_line)
|
||||
validate_compiled_animation(data_hash)
|
||||
GameData::Animation.register(data_hash)
|
||||
end
|
||||
process_pbs_file_message_end
|
||||
end
|
||||
validate_all_compiled_animations
|
||||
# Save all data
|
||||
GameData::Animation.save
|
||||
end
|
||||
|
||||
def validate_compiled_animation(hash)
|
||||
# Split anim_type, move/common_name, version into their own values
|
||||
hash[:type] = hash[:id][0]
|
||||
hash[:move] = hash[:id][1]
|
||||
hash[:version] = hash[:id][2] || 0
|
||||
# Go through each particle in turn
|
||||
hash[:particles].each do |particle|
|
||||
# Convert all "SetXYZ" particle commands to "MoveXYZ" by giving them a
|
||||
# duration of 0
|
||||
[:frame, :x, :y, :zoom_x, :zoom_y, :angle, :opacity].each do |prop|
|
||||
next if !particle[prop]
|
||||
particle[prop].each do |cmd|
|
||||
cmd.insert(1, 0) if cmd.length == 2
|
||||
end
|
||||
end
|
||||
# Sort each particle's commands by their keyframe and duration
|
||||
particle.keys.each do |key|
|
||||
next if !particle[key].is_a?(Array)
|
||||
particle[key].sort! { |a, b| a[0] == b[0] ? a[1] == b[1] ? 0 : a[1] <=> b[1] : a[0] <=> b[0] }
|
||||
# TODO: Find any overlapping particle commands and raise an error.
|
||||
end
|
||||
# Ensure valid values for "SetBlending" commands
|
||||
if particle[:blending]
|
||||
particle[:blending].each do |blend|
|
||||
next if blend[1] <= 2
|
||||
raise _INTL("Invalid blend value: {1} (must be 0, 1 or 2).\n{2}",
|
||||
blend[1], FileLineData.linereport)
|
||||
end
|
||||
end
|
||||
# TODO: Ensure "Play", "PlayUserCry", "PlayTargetCry" are exclusively used
|
||||
# by the particle "SE", and that the "SE" particle can only use
|
||||
# those commands. Raise if problems found.
|
||||
|
||||
# Ensure all particles have a default focus if not given
|
||||
if !particle[:focus]
|
||||
if particle[:name] == "User"
|
||||
particle[:focus] = :user
|
||||
elsif particle[:name] == "Target"
|
||||
particle[:focus] = :target
|
||||
elsif particle[:name] != "SE"
|
||||
particle[:focus] = :screen
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Depending on hash[:target], ensure all particles have an
|
||||
# appropriate focus (i.e. can't be :user_and_target if hash[:target]
|
||||
# doesn't include a target). Raise if problems found.
|
||||
end
|
||||
end
|
||||
|
||||
def validate_all_compiled_animations; end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Hook into the regular Compiler to also compile animation PBS files.
|
||||
#===============================================================================
|
||||
module Compiler
|
||||
module_function
|
||||
|
||||
def get_animation_pbs_files_to_compile
|
||||
ret = []
|
||||
if FileTest.directory?("PBS/Animations")
|
||||
Dir.all("PBS/Animations", "**/**.txt").each { |file| ret.push(file) }
|
||||
end
|
||||
return ret
|
||||
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
|
||||
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)
|
||||
end
|
||||
end
|
||||
122
Data/Scripts/902_Anim compiler/anim pbs writer.rb
Normal file
122
Data/Scripts/902_Anim compiler/anim pbs writer.rb
Normal file
@@ -0,0 +1,122 @@
|
||||
module Compiler
|
||||
module_function
|
||||
|
||||
def write_all_battle_animations
|
||||
# Delete all existing .txt files in the PBS/Animations/ folder
|
||||
files_to_delete = get_animation_pbs_files_to_compile
|
||||
files_to_delete.each { |path| File.delete(path) }
|
||||
# Get all files that need writing
|
||||
paths = []
|
||||
GameData::Animation.each { |anim| paths.push(anim.pbs_path) if !paths.include?(anim.pbs_path) }
|
||||
idx = 0
|
||||
# Write each file in turn
|
||||
paths.each do |path|
|
||||
Graphics.update if idx % 500 == 0
|
||||
idx += 1
|
||||
write_battle_animation_file(path)
|
||||
end
|
||||
end
|
||||
|
||||
def write_battle_animation_file(path)
|
||||
schema = GameData::Animation.schema
|
||||
sub_schema = GameData::Animation.sub_schema
|
||||
write_pbs_file_message_start(path)
|
||||
# Create all subfolders needed
|
||||
dirs = ("PBS/Animations/" + path).split("/")
|
||||
dirs.pop # Remove the filename
|
||||
dirs.length.times do |i|
|
||||
dir_string = dirs[0..i].join("/")
|
||||
if !FileTest.directory?(dir_string)
|
||||
Dir.mkdir(dir_string) rescue nil
|
||||
end
|
||||
end
|
||||
# Write file
|
||||
File.open("PBS/Animations/" + path + ".txt", "wb") do |f|
|
||||
add_PBS_header_to_file(f)
|
||||
# Write each element in turn
|
||||
GameData::Animation.each do |element|
|
||||
next if element.pbs_path != path
|
||||
f.write("\#-------------------------------\r\n")
|
||||
if schema["SectionName"]
|
||||
f.write("[")
|
||||
pbWriteCsvRecord(element.get_property_for_PBS("SectionName"), f, schema["SectionName"])
|
||||
f.write("]\r\n")
|
||||
else
|
||||
f.write("[#{element.id}]\r\n")
|
||||
end
|
||||
# Write each animation property
|
||||
schema.each_key do |key|
|
||||
next if ["SectionName", "Particle"].include?(key)
|
||||
val = element.get_property_for_PBS(key)
|
||||
next if val.nil?
|
||||
f.write(sprintf("%s = ", key))
|
||||
pbWriteCsvRecord(val, f, schema[key])
|
||||
f.write("\r\n")
|
||||
end
|
||||
# Write each particle in turn
|
||||
element.particles.sort! do |a, b|
|
||||
a_val = 0
|
||||
a_val = -2 if a[:name] == "User"
|
||||
a_val = -1 if a[:name] == "Target"
|
||||
a_val = 1 if a[:name] == "SE"
|
||||
b_val = 0
|
||||
b_val = -2 if b[:name] == "User"
|
||||
b_val = -1 if b[:name] == "Target"
|
||||
b_val = 1 if b[:name] == "SE"
|
||||
next a_val <=> b_val
|
||||
end
|
||||
element.particles.each_with_index do |particle, i|
|
||||
# Write header
|
||||
f.write("<" + particle[:name] + ">")
|
||||
f.write("\r\n")
|
||||
# Write one-off particle properties
|
||||
sub_schema.each_pair do |key, val|
|
||||
next if val[1][0] == "^"
|
||||
val = element.get_particle_property_for_PBS(key, i)
|
||||
next if val.nil?
|
||||
f.write(sprintf(" %s = ", key))
|
||||
pbWriteCsvRecord(val, f, sub_schema[key])
|
||||
f.write("\r\n")
|
||||
end
|
||||
# Write particle commands (in keyframe order)
|
||||
cmds = element.get_particle_property_for_PBS("AllCommands", i)
|
||||
cmds.each do |cmd|
|
||||
if cmd[2] == 0 # Duration of 0
|
||||
f.write(sprintf(" %s = ", cmd[0]))
|
||||
new_cmd = cmd[1..-1]
|
||||
new_cmd.delete_at(1)
|
||||
pbWriteCsvRecord(new_cmd, f, sub_schema[cmd[0]])
|
||||
f.write("\r\n")
|
||||
else # Has a duration
|
||||
f.write(sprintf(" %s = ", cmd[0]))
|
||||
pbWriteCsvRecord(cmd[1..-1], f, sub_schema[cmd[0]])
|
||||
f.write("\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
process_pbs_file_message_end
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Hook into the regular Compiler to also write all animation PBS files.
|
||||
#===============================================================================
|
||||
module Compiler
|
||||
module_function
|
||||
|
||||
class << self
|
||||
if !method_defined?(:__new_anims__write_all)
|
||||
alias_method :__new_anims__write_all, :write_all
|
||||
end
|
||||
end
|
||||
|
||||
def write_all
|
||||
__new_anims__write_all
|
||||
Console.echo_h1(_INTL("Writing all animation PBS files"))
|
||||
write_all_battle_animations
|
||||
echoln ""
|
||||
Console.echo_h2(_INTL("Successfully rewrote all animation PBS files"), text: :green)
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,11 @@
|
||||
# TODO: Need a way to recognise when text is being input into something
|
||||
# (Input.text_input) and disable all keyboard shortcuts if so. If only
|
||||
# this class has keyboard shortcuts in it, then it should be okay already.
|
||||
# TODO: When creating a new particle, blacklist the names "User", "Target" and
|
||||
# "SE". Make particles with those names undeletable.
|
||||
# TODO: Remove the particle named "Target" if the animation's focus is changed
|
||||
# to one that doesn't include a target, and vice versa. Do the same for
|
||||
# "User".
|
||||
#===============================================================================
|
||||
class AnimationEditor
|
||||
WINDOW_WIDTH = AnimationEditorLoadScreen::WINDOW_WIDTH
|
||||
|
||||
Reference in New Issue
Block a user