Skip to content

Instantly share code, notes, and snippets.

@UserUnknownFactor
Last active June 10, 2025 08:38
Show Gist options
  • Save UserUnknownFactor/4e700940079109f2430078534f163504 to your computer and use it in GitHub Desktop.
Save UserUnknownFactor/4e700940079109f2430078534f163504 to your computer and use it in GitHub Desktop.
Unpacker/repacker for RPG Maker XP, VX [Ace] games
require 'zlib'
# Some data structures that are common to both VX and VX Ace.
class Rect
def initialize(x, y, width, height)
@x = x
@y = y
@width = width
@height = height
end
class << self
def _load(s)
end
end
end
class Tone
def initialize(red, green, blue, gray = 0.0)
@red = red
@green = green
@blue = blue
@gray = gray
end
def _dump(d = 0)
[@red].pack('d') + [@green].pack('d') + [@blue].pack('d') + [@gray].pack('d')
end
class << self
def _load(s)
red = s[0,8].unpack('d')[0]
blue = s[8,8].unpack('d')[0]
green = s[16,8].unpack('d')[0]
gray = s[24,8].unpack('d')[0]
Tone.new(red, blue, green, gray)
end
end
end
class Color
def initialize(red, green, blue, alpha = 255.0)
@red = red
@green = green
@blue = blue
@alpha = alpha
end
def _dump(d = 0)
[@red].pack('d') + [@green].pack('d') + [@blue].pack('d') + [@alpha].pack('d')
end
class << self
def _load(s)
red = s[0,8].unpack('d')[0]
blue = s[8,8].unpack('d')[0]
green = s[16,8].unpack('d')[0]
alpha = s[24,8].unpack('d')[0]
Color.new(red, blue, green, alpha)
end
end
end
class Table
def initialize(x, y = nil, z = nil)
@dims = 1 + (y.nil?() ? 0:1) + (z.nil?() ? 0:1)
@xsize = x
@ysize = y.nil?() ? 1:y
@zsize = z.nil?() ? 1:z
@data=Array.new(@xsize*@ysize*@zsize, 0)
end
def [](x, y = 0, z = 0)
@data[x + y*@xsize + z*@xsize*@ysize]
end
def []=(*args)
x = args[0]
y = args.size>2 ? args[1]:0
z = args.size>3 ? args[2]:0
v = args.pop
@data[x + y*@xsize + z*@xsize*@ysize]=v
end
def _dump(d = 0)
s = [@dims].pack('L')
s += [@xsize].pack('L') + [@ysize].pack('L') + [@zsize].pack('L')
s += [@xsize*@ysize*@zsize].pack('L')
for z in 0...@zsize
for y in 0...@ysize
for x in 0...@xsize
s += [@data[x + y*@xsize + z*@xsize*@ysize]].pack('S')[0,2]
end
end
end
s
end
attr_reader(:xsize,:ysize,:zsize,:data)
class << self
def _load(s)
dims = s[0,4].unpack('L')[0]
nx = s[4,4].unpack('L')[0]
ny = s[8,4].unpack('L')[0]
nz = s[12,4].unpack('L')[0]
data = []
for i in 10...(s.size/2)
data.push(s[i*2, 2].unpack('S')[0])
end
t = Table.new(nx, (dims >= 2 ? ny:nil), (dims >= 3 ? nz:nil))
n = 0
for z in 0...nz
for y in 0...ny
for x in 0...nx
t[x, y, z] = data[n]
n += 1
end
end
end
t
end
end
end
# Not actually an RPG Maker class, just for convenience.
class Script
def initialize(index, name, text, scripts_path, script_idx)
@index = index
@name = name
@text = Zlib::Inflate.inflate(text).force_encoding('UTF-8')
script_name = "Script" + script_idx.to_s.rjust(3, "0")
@file = scripts_path.join(script_name).sub_ext(".rb")
end
def to_json(*a)
File.binwrite(@file, @text)
File.binwrite(@file.sub_ext('_tran.rb'), @text)
{
'json_class' => self.class.name,
'filename' => @file,
'name' => dump_string(@name),
}.to_json(*a)
end
def self.data_is_script?(data)
data.instance_of?(Array) and data[0].instance_of?(Array) and data[0].length == 3
end
end
#!/usr/bin/env ruby
require 'fileutils'
require 'pathname'
require 'csv'
class RgssPacker
SIGNATURE = "RGSSAD\0"
def initialize(input_dir = 'Data', output_file = 'Game.rgss3a', use_original_keys = true)
@input_dir = input_dir
@output_file = output_file
@use_original_keys = use_original_keys
@index_file = File.join(@input_dir, 'rgss_index.csv')
unless Dir.exist?(@input_dir)
puts "Input directory '#{@input_dir}' not found."
exit 1
end
end
def pack
original_data = load_original_data if @use_original_keys && File.exist?(@index_file)
version = original_data ? original_data[:version] : 3
seed_key = original_data ? original_data[:seed_key] : nil
original_keys = original_data ? original_data[:file_info] : nil
files = collect_files(original_keys)
puts "Found #{files.length} files to pack from #{@input_dir}"
puts "Packing as RGSS v#{version}"
puts "Using original keys from index file; seed key: #{seed_key}" if original_keys
File.open(@output_file, 'wb') do |archive|
# write header
archive.write(SIGNATURE)
archive.write([version].pack('C'))
if version == 3
pack_v3(archive, files, original_keys, seed_key)
elsif version == 1
pack_v1(archive, files, original_keys)
else
raise "Unsupported RGSS version: #{version}"
end
end
puts "Archive created successfully: #{@output_file}"
end
private
class KeyGenerator
def initialize(seed)
@seed = seed
end
def current
@seed
end
def next
key = @seed
@seed = @seed * 7 + 3
key
end
end
def pr_xor_data(data, key_seed)
key_gen = KeyGenerator.new(key_seed)
key = key_gen.next
result = ""
data.bytes.each_with_index do |b, i|
if i > 0 && i % 4 == 0
key = key_gen.next
end
key_byte = (key >> ((i % 4) * 8)) & 0xFF
result << (b ^ key_byte).chr
end
result
end
def pack_v1(archive, files, original_keys)
key_gen = KeyGenerator.new(0xDEADCAFE)
files.each do |file_path|
rel_path = Pathname.new(file_path).relative_path_from(Pathname.new(@input_dir)).to_s.gsub('/', '\\')
file_key = key_gen.current
puts "Packing #{rel_path}..."
name_bytes = rel_path.dup.force_encoding('BINARY').bytes
archive.write([name_bytes.length ^ key_gen.next].pack('L'))
name_bytes.each do |b|
archive.write([(b ^ (key_gen.next & 0xFF))].pack('C'))
end
file_size = File.size(file_path)
archive.write([file_size ^ key_gen.next].pack('L'))
archive.write(pr_xor_data(File.binread(file_path), file_key))
end
end
def pack_v3(archive, files, original_keys, original_seed_key)
seed_key = original_seed_key || 0x55555555
key = seed_key * 9 + 3
archive.write([seed_key].pack('L'))
index_entries = []
files.each do |file_path|
rel_path = Pathname.new(file_path).relative_path_from(Pathname.new(@input_dir)).to_s.gsub('/', '\\')
file_key = if original_keys && original_keys[rel_path]
original_keys[rel_path][:key]
else
0
end
old_file_size = if original_keys && original_keys[rel_path]
original_keys[rel_path][:size]
else
0
end
file_size = File.size(file_path)
index_entries << {
name: rel_path,
name_bytes: rel_path.dup.force_encoding('BINARY').bytes,
key: file_key,
size: file_size,
full_path: file_path,
changed: file_size =! old_file_size
}
end
# 16 bytes per entry (offset, size, key, name_length) + each name's bytes + 4 bytes 0-terminator
index_size = index_entries.sum { |e| 16 + e[:name_bytes].length } + 4
index_pos = archive.pos
archive.write("\0" * index_size)
# write file data
index_entries.each do |entry|
changed = entry[:changed] ? '; file changed' : ''
puts "Packing #{entry[:name]} (key: #{entry[:key]}#{changed})"
entry[:offset] = archive.pos
archive.write(pr_xor_data(File.binread(entry[:full_path]), entry[:key]))
end
# Go back and write the index
archive.pos = index_pos
index_entries.each do |entry|
archive.write([entry[:offset] ^ key].pack('L'))
archive.write([entry[:size] ^ key].pack('L'))
archive.write([entry[:key] ^ key].pack('L'))
archive.write([entry[:name_bytes].length ^ key].pack('L'))
encrypted_name = []
entry[:name_bytes].each_with_index do |b, i|
encrypted_name << (b ^ ((key >> (i % 4 * 8)) & 0xFF))
end
archive.write(encrypted_name.pack('C*'))
end
archive.write([key].pack('L')) # terminator
end
def load_original_data
file_info = {}
file_order = []
version = 3 # default
seed_key = nil
CSV.foreach(@index_file) do |row|
next if row.empty?
if row[0] == '# Version'
version = row[1].to_i
next
elsif row[0] == '# SeedKey'
seed_key = row[1].to_i
next
elsif row[0] == 'filename' || row[0].to_s.start_with?('#')
next
end
# Parse data rows: filename, size, key, offset
if row.length >= 3
filename = row[0]
size = row[1].to_i
key = row[2].to_i
offset = row[3].to_i if row[3]
file_info[filename] = {
size: size,
key: key,
offset: offset
}
file_order << filename
end
end
puts "Loaded #{file_info.length} file records from index (version #{version})"
{
version: version,
file_info: file_info,
seed_key: seed_key,
file_order: file_order
}
rescue => e
puts "Warning: Could not load original data: #{e.message}"
nil
end
def collect_files(original_keys)
all_files = []
Dir.glob(File.join(@input_dir, '**', '*')).each do |path|
next if File.directory?(path)
next if File.basename(path) == 'rgss_index.csv' || File.basename(path) == 'thumbs.db'
all_files << path
end
if original_keys && original_keys[:file_order] && !original_keys[:file_order].empty?
ordered_files = []
original_keys[:file_order].each do |rel_path|
full_path = File.join(@input_dir, rel_path)
if File.exist?(full_path)
ordered_files << full_path
else
puts "Warning: Registered file was removed: #{rel_path}"
end
end
all_files.each do |full_path|
unless ordered_files.include?(full_path)
rel_path = Pathname.new(full_path).relative_path_from(Pathname.new(@input_dir)).to_s
puts "Warning: Adding new file to archive: #{rel_path}"
ordered_files << full_path
end
end
ordered_files
else
all_files.sort
end
end
end
if __FILE__ == $0
input_dir = ARGV[0] || 'Data'
output_file = ARGV[1] || 'Game.rgss3a_modded'
use_original_keys = ARGV[2] != 'false' # defaults to true
packer = RgssPacker.new(input_dir, output_file, use_original_keys)
packer.pack
end
#!/usr/bin/env ruby
require 'fileutils'
require 'csv'
class RgssUnpacker
SIGNATURE = 'RGSS'
def initialize(archive_path = nil, output_dir = 'Data')
@archive_path = archive_path || Dir.glob('*.rgss3a').first
@output_dir = output_dir
if @archive_path.nil?
puts "No #{SIGNATURE} file found in current directory."
exit 1
end
FileUtils.mkdir_p(@output_dir)
end
def unpack
File.open(@archive_path, 'rb') do |file|
signature = file.read(4)
raise "Invalid signature" unless signature == SIGNATURE
if file.read(3) != "AD\0"
raise "Not a valid #{SIGNATURE} archive"
end
version = file.read(1).unpack('C')[0]
@version = version
# For v3, read the seed key
@seed_key = nil
if version == 3
@seed_key = file.read(4).unpack('L')[0]
file.pos = 8
end
entries = case version
when 1 then read_index_v1(file)
when 3 then read_index_v3(file)
else raise "Unsupported #{SIGNATURE} version: #{version}"
end
puts "Found #{entries.length} files in #{SIGNATURE} archive v#{version}"
save_file_index(entries, version)
skipped = 0
extracted = 0
entries.each do |entry|
if extract_entry(file, entry)
extracted += 1
else
skipped += 1
end
end
puts "Extraction complete to #{@output_dir}: #{extracted} extracted, #{skipped} skipped"
end
end
private
class Entry
attr_accessor :name, :offset, :size, :key
end
class KeyGenerator
def initialize(seed)
@seed = seed
end
def current
@seed
end
def next
key = @seed
@seed = @seed * 7 + 3
key
end
end
def save_file_index(entries, version)
index_file = File.join(@output_dir, 'rgss_index.csv')
CSV.open(index_file, 'wb') do |csv|
csv << ["\# #{SIGNATURE} Archive Index"]
csv << ['# Version', version]
csv << ['# Archive', File.basename(@archive_path)]
csv << ['# SeedKey', @seed_key] if @seed_key # for v3
csv << []
csv << ['filename', 'size', 'key', 'offset']
entries.each do |entry|
csv << [entry.name, entry.size, entry.key, entry.offset]
end
end
puts "Saved file index to: #{index_file}"
end
def read_index_v1(file)
key_gen = KeyGenerator.new(0xDEADCAFE)
entries = []
file.pos = 8
until file.eof?
entry = Entry.new
name_length = file.read(4).unpack('L')[0] ^ key_gen.next
name_bytes = file.read(name_length)
entry.name = decrypt_name_v1(name_bytes, key_gen)
entry.size = file.read(4).unpack('L')[0] ^ key_gen.next
entry.offset = file.pos
entry.key = key_gen.current
entries << entry
file.seek(entry.size, IO::SEEK_CUR)
end
entries
end
def read_index_v3(file)
entries = []
file.pos = 8
key = file.read(4).unpack('L')[0] * 9 + 3
key_bytes = []
4.times do |i|
key_bytes << ((key >> (i * 8)) & 0xFF)
end
until file.eof?
offset = file.read(4).unpack('L')[0] ^ key
break if offset == 0
entry = Entry.new
entry.offset = offset
entry.size = file.read(4).unpack('L')[0] ^ key
entry.key = file.read(4).unpack('L')[0] ^ key
name_length = file.read(4).unpack('L')[0] ^ key
name_bytes = file.read(name_length)
entry.name = decrypt_name_v3(name_bytes, key_bytes)
entries << entry
end
entries
end
def decrypt_name_v1(name_bytes, key_gen)
decrypted = []
name_bytes.bytes.each do |b|
decrypted << (b ^ (key_gen.next & 0xFF))
end
sanitize_filename(decrypted.pack('C*'))
end
def decrypt_name_v3(name_bytes, key_bytes)
decrypted = ""
name_bytes.bytes.each_with_index do |b, i|
decrypted << (b ^ key_bytes[i % 4]).chr
end
sanitize_filename(decrypted)
end
def sanitize_filename(raw_name)
if raw_name.force_encoding('UTF-8').valid_encoding?
safe_name = raw_name.force_encoding('UTF-8')
else
begin
safe_name = raw_name.force_encoding('cp932').encode('UTF-8')
rescue
safe_name = raw_name.force_encoding('BINARY').unpack('C*').map { |b|
(b >= 32 && b <= 126) ? b.chr : '_'
}.join
end
end
return safe_name.gsub(/[\:\*\?\"\<\>\|]|\.\.+/, '_')
end
def extract_entry(file, entry)
target_path = File.join(@output_dir, entry.name)
if File.exist?(target_path) && File.size(target_path) == entry.size
puts "Skipped (already exists): #{entry.name}"
return false
end
FileUtils.mkdir_p(File.dirname(target_path))
file.pos = entry.offset
data = file.read(entry.size)
File.open(target_path, 'wb') do |out|
out.write(pr_xor_data(data, entry.key))
end
puts "Extracted: #{entry.name}"
return true
end
def pr_xor_data(data, key_seed)
key_gen = KeyGenerator.new(key_seed)
key = key_gen.next
result = ""
data.bytes.each_with_index do |b, i|
# Get a new key every 4 bytes
if i > 0 && i % 4 == 0
key = key_gen.next
end
key_byte = (key >> ((i % 4) * 8)) & 0xFF
result << (b ^ key_byte).chr
end
result
end
end
if __FILE__ == $0
archive_path = ARGV[0] || 'Game.rgss3a'
output_dir = ARGV[1] || 'Data'
unpacker = RgssUnpacker.new(archive_path, output_dir)
unpacker.unpack
end
require_relative 'common_db'
# This file contains all the RPG data structures for RPG Maker VX.
module RPG
class Actor
def initialize
@id = 0
@name = ""
@class_id = 1
@initial_level = 1
@exp_basis = 25
@exp_inflation = 35
@character_name = ""
@character_index = 0
@face_name = ""
@face_index = 0
@parameters = Table.new(6, 100)
for i in 1..99
@parameters[0,i] = 400+i*50
@parameters[1,i] = 80+i*10
@parameters[2,i] = 15+i*5/4
@parameters[3,i] = 15+i*5/4
@parameters[4,i] = 20+i*5/2
@parameters[5,i] = 20+i*5/2
end
@weapon_id = 0
@armor1_id = 0
@armor2_id = 0
@armor3_id = 0
@armor4_id = 0
@two_swords_style = false
@fix_equipment = false
@auto_battle = false
@super_guard = false
@pharmacology = false
@critical_bonus = false
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Class
def initialize
@id = 0
@name = ""
@position = 0
@weapon_set = []
@armor_set = []
@element_ranks = Table.new(1)
@state_ranks = Table.new(1)
@learnings = []
@skill_name_valid = false
@skill_name = ""
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
class Learning
def initialize
@level = 1
@skill_id = 1
end
end
end
class Map
def initialize(width, height)
@width = width
@height = height
@scroll_type = 0
@autoplay_bgm = false
@bgm = RPG::AudioFile.new
@autoplay_bgs = false
@bgs = RPG::AudioFile.new("", 80)
@disable_dashing = false
@encounter_list = []
@encounter_step = 30
@parallax_name = ""
@parallax_loop_x = false
@parallax_loop_y = false
@parallax_sx = 0
@parallax_sy = 0
@parallax_show = false
@data = Table.new(width, height, 3)
@events = {}
end
def to_json(*a)
{
'json_class' => self.class.name,
'events' => dump_array(@events.sort)
}.to_json(*a)
end
end
class MapInfo
def initialize
@name = ""
@parent_id = 0
@order = 0
@expanded = false
@scroll_x = 0
@scroll_y = 0
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Area
def initialize
@id = 0
@name = ""
@map_id = 0
@rect = Rect.new(0,0,0,0)
@encounter_list = []
@order = 0
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Event
def initialize(x, y)
@id = 0
@name = ""
@x = x
@y = y
@pages = [RPG::Event::Page.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'pages' => @pages
}.to_json(*a)
end
class Page
def initialize
@condition = RPG::Event::Page::Condition.new
@graphic = RPG::Event::Page::Graphic.new
@move_type = 0
@move_speed = 3
@move_frequency = 3
@move_route = RPG::MoveRoute.new
@walk_anime = true
@step_anime = false
@direction_fix = false
@through = false
@priority_type = 0
@trigger = 0
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
class Condition
def initialize
@switch1_valid = false
@switch2_valid = false
@variable_valid = false
@self_switch_valid = false
@item_valid = false
@actor_valid = false
@switch1_id = 1
@switch2_id = 1
@variable_id = 1
@variable_value = 0
@self_switch_ch = "A"
@item_id = 1
@actor_id = 1
end
end
class Graphic
def initialize
@tile_id = 0
@character_name = ""
@character_index = 0
@direction = 2
@pattern = 0
end
end
end
end
class EventCommand
def initialize(code = 0, indent = 0, parameters = [])
@code = code
@indent = indent
@parameters = parameters
end
attr_accessor :code
attr_accessor :indent
attr_accessor :parameters
def to_json(*a)
obj = {}
obj['json_class'] = self.class.name
(type, is_text) = get_event_type(@code)
obj['type'] = type
if is_text then
obj['parameters'] = dump_parameters(@parameters)
end
obj.to_json(*a)
end
end
class MoveRoute
def initialize
@repeat = true
@skippable = false
@wait = false
@list = [RPG::MoveCommand.new]
end
end
class MoveCommand
def initialize(code = 0, parameters = [])
@code = code
@parameters = parameters
end
end
class BaseItem
def initialize
@id = 0
@name = ""
@icon_index = 0
@description = ""
@note = ""
end
def base_to_json(clsname)
{
'json_class' => clsname,
'name' => dump_string(@name),
'description' => dump_string(@description),
'note' => dump_string(@note)
}
end
def base_translate(tran, info)
@name = translate_string("name", @name, tran, info)
@description = translate_string("description", @description, tran, info)
@note = translate_string("note", @note, tran, info)
end
def base_update(tran, info)
tran = update_string("name", @name, tran, info)
tran = update_string("description", @description, tran, info)
update_string("note", @note, tran, info)
end
end
class UsableItem < BaseItem
def initialize
super
@scope = 0
@occasion = 0
@speed = 0
@animation_id = 0
@common_event_id = 0
@base_damage = 0
@variance = 20
@atk_f = 0
@spi_f = 0
@physical_attack = false
@damage_to_mp = false
@absorb_damage = false
@ignore_defense = false
@element_set = []
@plus_state_set = []
@minus_state_set = []
end
def for_opponent?
return [1, 2, 3, 4, 5, 6].include?(@scope)
end
def for_friend?
return [7, 8, 9, 10, 11].include?(@scope)
end
def for_dead_friend?
return [9, 10].include?(@scope)
end
def for_user?
return [11].include?(@scope)
end
def for_one?
return [1, 3, 4, 7, 9, 11].include?(@scope)
end
def for_two?
return [5].include?(@scope)
end
def for_three?
return [6].include?(@scope)
end
def for_random?
return [4, 5, 6].include?(@scope)
end
def for_all?
return [2, 8, 10].include?(@scope)
end
def dual?
return [3].include?(@scope)
end
def need_selection?
return [1, 3, 7, 9].include?(@scope)
end
def battle_ok?
return [0, 1].include?(@occasion)
end
def menu_ok?
return [0, 2].include?(@occasion)
end
end
class Skill < UsableItem
def initialize
super
@scope = 1
@mp_cost = 0
@hit = 100
@message1 = ""
@message2 = ""
end
def to_json(*a)
data = base_to_json(self.class.name)
data['message1'] = dump_string(@message1)
data['message2'] = dump_string(@message2)
data.to_json(*a)
end
end
class Item < UsableItem
def initialize
super
@scope = 7
@price = 0
@consumable = true
@hp_recovery_rate = 0
@hp_recovery = 0
@mp_recovery_rate = 0
@mp_recovery = 0
@parameter_type = 0
@parameter_points = 0
end
def to_json(*a)
base_to_json(self.class.name).to_json(*a)
end
end
class Weapon < BaseItem
def initialize
super
@animation_id = 0
@price = 0
@hit = 95
@atk = 0
@def = 0
@spi = 0
@agi = 0
@two_handed = false
@fast_attack = false
@dual_attack = false
@critical_bonus = false
@element_set = []
@state_set = []
end
def to_json(*a)
base_to_json(self.class.name).to_json(*a)
end
end
class Armor < BaseItem
def initialize
super
@kind = 0
@price = 0
@eva = 0
@atk = 0
@def = 0
@spi = 0
@agi = 0
@prevent_critical = false
@half_mp_cost = false
@double_exp_gain = false
@auto_hp_recover = false
@element_set = []
@state_set = []
end
def to_json(*a)
base_to_json(self.class.name).to_json(*a)
end
end
class Enemy
def initialize
@id = 0
@name = ""
@battler_name = ""
@battler_hue = 0
@maxhp = 10
@maxmp = 10
@atk = 10
@def = 10
@spi = 10
@agi = 10
@hit = 95
@eva = 5
@exp = 0
@gold = 0
@drop_item1 = RPG::Enemy::DropItem.new
@drop_item2 = RPG::Enemy::DropItem.new
@levitate = false
@has_critical = false
@element_ranks = Table.new(1)
@state_ranks = Table.new(1)
@actions = [RPG::Enemy::Action.new]
@note = ""
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'note' => dump_string(@note)
}.to_json(*a)
end
class DropItem
def initialize
@kind = 0
@item_id = 1
@weapon_id = 1
@armor_id = 1
@denominator = 1
end
end
class Action
def initialize
@kind = 0
@basic = 0
@skill_id = 1
@condition_type = 0
@condition_param1 = 0
@condition_param2 = 0
@rating = 5
end
def skill?
return @kind == 1
end
end
end
class Troop
def initialize
@id = 0
@name = ""
@members = []
@pages = [RPG::BattleEventPage.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'pages' => @pages
}.to_json(*a)
end
class Member
def initialize
@enemy_id = 1
@x = 0
@y = 0
@hidden = false
@immortal = false
end
end
class Page
def initialize
@condition = RPG::Troop::Page::Condition.new
@span = 0
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
class Condition
def initialize
@turn_ending = false
@turn_valid = false
@enemy_valid = false
@actor_valid = false
@switch_valid = false
@turn_a = 0
@turn_b = 0
@enemy_index = 0
@enemy_hp = 50
@actor_id = 1
@actor_hp = 50
@switch_id = 1
end
end
end
end
class State
def initialize
@id = 0
@name = ""
@icon_index = 0
@restriction = 0
@priority = 5
@atk_rate = 100
@def_rate = 100
@spi_rate = 100
@agi_rate = 100
@nonresistance = false
@offset_by_opposite = false
@slip_damage = false
@reduce_hit_ratio = false
@battle_only = true
@release_by_damage = false
@hold_turn = 0
@auto_release_prob = 0
@message1 = ""
@message2 = ""
@message3 = ""
@message4 = ""
@element_set = []
@state_set = []
@note = ""
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'message1' => dump_string(@message1),
'message2' => dump_string(@message2),
'message3' => dump_string(@message3),
'message4' => dump_string(@message4),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class Animation
def initialize
@id = 0
@name = ""
@animation1_name = ""
@animation1_hue = 0
@animation2_name = ""
@animation2_hue = 0
@position = 1
@frame_max = 1
@frames = [RPG::Animation::Frame.new]
@timings = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
class Frame
def initialize
@cell_max = 0
@cell_data = Table.new(0, 0)
end
end
class Timing
def initialize
@frame = 0
@se = RPG::AudioFile.new("", 80)
@flash_scope = 0
@flash_color = Color.new(255,255,255,255)
@flash_duration = 5
end
end
end
class CommonEvent
def initialize
@id = 0
@name = ""
@trigger = 0
@switch_id = 1
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
end
class System
def initialize
@game_title = ""
@version_id = 0
@party_members = [1]
@elements = [nil, ""]
@switches = [nil, ""]
@variables = [nil, ""]
@passages = Table.new(8192)
@boat = RPG::System::Vehicle.new
@ship = RPG::System::Vehicle.new
@airship = RPG::System::Vehicle.new
@title_bgm = RPG::BGM.new
@battle_bgm = RPG::BGM.new
@battle_end_me = RPG::ME.new
@gameover_me = RPG::ME.new
@sounds = []
20.times { @sounds.push(RPG::AudioFile.new) }
@test_battlers = []
@test_troop_id = 1
@start_map_id = 1
@start_x = 0
@start_y = 0
@terms = RPG::System::Terms.new
@battler_name = ""
@battler_hue = 0
@edit_map_id = 1
end
def to_json(*a)
{
'json_class' => self.class.name,
'game_title' => dump_string(@game_title),
'terms' => @terms,
'elements' => dump_array(@elements),
'switches' => dump_array(@switches),
'variables' => dump_array(@variables)
}.to_json(*a)
end
class Vehicle
def initialize
@character_name = ""
@character_index = 0
@bgm = RPG::AudioFile.new
@start_map_id = 0
@start_x = 0
@start_y = 0
end
end
class Terms
def initialize
@level = ""
@level_a = ""
@hp = ""
@hp_a = ""
@mp = ""
@mp_a = ""
@atk = ""
@def = ""
@spi = ""
@agi = ""
@weapon = ""
@armor1 = ""
@armor2 = ""
@armor3 = ""
@armor4 = ""
@weapon1 = ""
@weapon2 = ""
@attack = ""
@skill = ""
@guard = ""
@item = ""
@equip = ""
@status = ""
@save = ""
@game_end = ""
@fight = ""
@escape = ""
@new_game = ""
@continue = ""
@shutdown = ""
@to_title = ""
@cancel = ""
@gold = ""
end
def to_json(*a)
{
'level' => dump_string(@level),
'level_a' => dump_string(@level_a),
'hp' => dump_string(@hp),
'hp_a' => dump_string(@hp_a),
'mp' => dump_string(@mp),
'mp_a' => dump_string(@mp_a),
'atk' => dump_string(@atk),
'def' => dump_string(@def),
'spi' => dump_string(@spi),
'agi' => dump_string(@agi),
'weapon' => dump_string(@weapon),
'armor1' => dump_string(@armor1),
'armor2' => dump_string(@armor2),
'armor3' => dump_string(@armor3),
'armor4' => dump_string(@armor4),
'weapon1' => dump_string(@weapon1),
'weapon2' => dump_string(@weapon2),
'attack' => dump_string(@attack),
'skill' => dump_string(@skill),
'guard' => dump_string(@guard),
'item' => dump_string(@item),
'equip' => dump_string(@equip),
'status' => dump_string(@status),
'save' => dump_string(@save),
'game_end' => dump_string(@game_end),
'fight' => dump_string(@fight),
'escape' => dump_string(@escape),
'new_game' => dump_string(@new_game),
'continue' => dump_string(@continue),
'shutdown' => dump_string(@shutdown),
'to_title' => dump_string(@to_title),
'cancel' => dump_string(@cancel),
'gold' => dump_string(@gold)
}.to_json(*a)
end
end
class TestBattler
def initialize
@actor_id = 1
@level = 1
@weapon_id = 0
@armor1_id = 0
@armor2_id = 0
@armor3_id = 0
@armor4_id = 0
end
end
end
class AudioFile
def initialize(name = "", volume = 100, pitch = 100)
@name = name
@pitch = pitch
@volume = volume
end
end
class BGM < AudioFile
@@last = BGM.new
def play
if @name.empty?
Audio.bgm_stop
@@last = BGM.new
else
Audio.bgm_play("Audio/BGM/" + @name, @volume, @pitch)
@@last = self
end
end
def self.stop
Audio.bgm_stop
@@last = BGM.new
end
def self.fade(time)
Audio.bgm_fade(time)
@@last = BGM.new
end
def self.last
@@last
end
end
class BGS < AudioFile
@@last = BGS.new
def play
if @name.empty?
Audio.bgs_stop
@@last = BGS.new
else
Audio.bgs_play("Audio/BGS/" + @name, @volume, @pitch)
@@last = self
end
end
def self.stop
Audio.bgs_stop
@@last = BGS.new
end
def self.fade(time)
Audio.bgs_fade(time)
@@last = BGS.new
end
def self.last
@@last
end
end
class ME < AudioFile
def play
if @name.empty?
Audio.me_stop
else
Audio.me_play("Audio/ME/" + @name, @volume, @pitch)
end
end
def self.stop
Audio.me_stop
end
def self.fade(time)
Audio.me_fade(time)
end
end
class SE < AudioFile
def play
unless @name.empty?
Audio.se_play("Audio/SE/" + @name, @volume, @pitch)
end
end
def self.stop
Audio.se_stop
end
end
end
# Table of what type of event command the event codes corresponds to.
EVENT_COMMAND_CODES = {
0 => ["Empty", false],
101 => ["Show Text Attributes", false],
102 => ["Show Choices", true],
103 => ["Input Number", false],
108 => ["Comment", true],
111 => ["Conditional Branch", false],
112 => ["Loop", false],
113 => ["Break Loop", false],
115 => ["Exit Event Processing"],
117 => ["Call Common Event", false],
118 => ["Label", false],
119 => ["Jump to Label", false],
121 => ["Control Switches", false],
122 => ["Control Variables", false],
123 => ["Control Self Switch", false],
124 => ["Control Timer", false],
125 => ["Change Gold", false],
126 => ["Change Items", false],
127 => ["Change Weapons", false],
128 => ["Change Armor", false],
129 => ["Change Party Member", false],
132 => ["Change Battle BGM", false],
133 => ["Change Battle End ME", false],
134 => ["Change Save Access", false],
135 => ["Change Menu Access", false],
136 => ["Change Encounter", false],
201 => ["Transfer Player", false],
202 => ["Set Vehicle Location", false],
203 => ["Set Event Location", false],
204 => ["Scroll Map", false],
205 => ["Set Move Route", false],
206 => ["Get on/off Vehicle", false],
211 => ["Change Transparency", false],
212 => ["Show Animation", false],
213 => ["Shot Balloon Icon", false],
214 => ["Erase Event", false],
221 => ["Fadeout Screen", false],
222 => ["Fadein Screen", false],
223 => ["Tint Screen", false],
224 => ["Flash Screen", false],
225 => ["Shake Screen", false],
230 => ["Wait", false],
231 => ["Show Picture", false],
232 => ["Move Picture", false],
233 => ["Rotate Picture", false],
234 => ["Tint Picture", false],
235 => ["Erase Picture", false],
236 => ["Set Weather Effects", false],
241 => ["Play BGM", false],
242 => ["Fadeout BGM", false],
245 => ["Play BGS", false],
246 => ["Fadeout BGS", false],
249 => ["Play ME", false],
250 => ["Play SE", false],
251 => ["Stop SE", false],
301 => ["Battle Processing", false],
302 => ["Shop Processing", false],
303 => ["Name Input Processing", false],
311 => ["Change HP", false],
312 => ["Change MP", false],
313 => ["Change State", false],
314 => ["Recover All", false],
315 => ["Change EXP", false],
316 => ["Change Level", false],
317 => ["Change Parameters", false],
318 => ["Change Skills", false],
319 => ["Change Equipment", false],
320 => ["Change Actor Name", true],
321 => ["Change Actor Class", false],
322 => ["Change Actor Graphic", false],
323 => ["Change Vehicle Graphic", false],
331 => ["Change Enemy HP", false],
332 => ["Change Enemy MP", false],
333 => ["Change Enemy State", false],
334 => ["Enemy Recover All", false],
335 => ["Enemy Appear", false],
336 => ["Enemy Transform", false],
337 => ["Show Battle Animation", false],
339 => ["Force Action", false],
340 => ["Abort Battle", false],
351 => ["Open Menu Screen", false],
352 => ["Open Save Screen", false],
353 => ["Game Over", false],
354 => ["Return to Title Screen", false],
355 => ["Script", true],
401 => ["Show Text", true],
402 => ["When", false],
403 => ["When Cancel", false],
404 => ["Choices End", false],
408 => ["Comment More", true],
411 => ["Else", false],
412 => ["Branch End", false],
413 => ["Repeat Above", false],
505 => ["Move Command", false],
601 => ["If Win", false],
602 => ["If Escape", false],
603 => ["If Lose", false],
604 => ["Battle Processing End", false],
605 => ["Shop Item", false],
655 => ["Script More", true]
}
# Given an event code, returns the type of the event and whether it can contain
# something translatable.
def get_event_type(code)
type = EVENT_COMMAND_CODES[code]
type.nil? ? [code, false] : type
end
def merge_event_commands(commands)
merged_cmds = []
i = 0
while i < commands.length
cmd = commands[i]
match_code = case cmd.code
when 401 then 401 # Show Text
when 108 then 408 # Comment
when 355 then 655 # Script
else 0
end
if match_code != 0 and commands[i+1].code == match_code
params = cmd.parameters
while commands[i+1].code == match_code
i += 1
params.concat commands[i].parameters
end
cmd = RPG::EventCommand.new(cmd.code, cmd.indent, params)
end
merged_cmds.push cmd
i += 1
end
merged_cmds
end
def split_event_commands(commands)
split_cmds = []
commands.each do |cmd|
match_code = case cmd.code
when 401 then 401
when 108 then 408
when 355 then 655
else 0
end
if match_code != 0 and cmd.parameters.length > 1
code = cmd.code
cmd.parameters.each do |p|
split_cmds.push RPG::EventCommand.new(code, cmd.indent, [p])
code = match_code
end
else
split_cmds.push cmd
end
end
split_cmds
end
require_relative 'common_db'
# This file contains all the RPG data structures for RPG Maker VX Ace.
module RPG
module ItemMake
# Item used in a recipe (add/remove component)
class Item
attr_accessor :class_id # 0 = item, 1 = weapon, 2 = armor
attr_accessor :item_id # ID of the item
attr_accessor :quantity # Quantity used or produced
def initialize(param = nil)
@class_id = 0
@item_id = 1
@quantity = 1
parse_param(param) if param
end
def parse_param(param)
case param
when Integer
if param >= 1001 && param <= 3999
@class_id = param / 1000 - 1
@item_id = param % 1000
elsif param >= 100101 && param <= 399999
@class_id = param / 100000 - 1
@item_id = (param % 100000) / 100
@quantity = param % 100
else
raise "Invalid encoded item param: #{param}"
end
when String
if param =~ /([iwaIWA])(\d+)(?::(\d+))?/
@class_id = {'i'=>0,'w'=>1,'a'=>2}[Regexp.last_match(1).downcase]
@item_id = Regexp.last_match(2).to_i
@quantity = Regexp.last_match(3) ? Regexp.last_match(3).to_i : 1
else
raise "Invalid item param string format: #{param}"
end
else
raise "Unsupported item param type: #{param.class}"
end
end
def to_json(*a)
{
'json_class' => self.class.name,
'class_id' => @class_id,
'item_id' => @item_id,
'quantity' => @quantity
}.to_json(*a)
end
end
# Recipe definition
class Recipe
attr_accessor :id
attr_accessor :name
attr_accessor :icon_index
attr_accessor :description
attr_accessor :price
attr_accessor :plus_items # Array of RPG::ItemMake::Item
attr_accessor :minus_items # Array of RPG::ItemMake::Item
def initialize
@id = 0
@name = ''
@icon_index = 0
@description = ''
@price = 0
@plus_items = []
@minus_items = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'id' => @id,
'name' => dump_string(@name),
'description' => dump_string(@description),
'price' => @price,
'plus_items' => @plus_items,
'minus_items' => @minus_items
}.to_json(*a)
end
end
# Book (category) of recipes
class Book
attr_accessor :id
attr_accessor :name
attr_accessor :icon_index
attr_accessor :list # Array of Recipe IDs
attr_accessor :display_price
attr_accessor :plus_number
attr_accessor :minus_number
attr_accessor :plus_name
attr_accessor :minus_name
attr_accessor :background_name
attr_accessor :foreground_name
attr_accessor :visible_window
def initialize
@id = 0
@name = ''
@icon_index = 0
@list = []
@display_price = true
@plus_number = 6
@minus_number = 6
@plus_name = ''
@minus_name = ''
@background_name = ''
@foreground_name = ''
@visible_window = true
end
def to_json(*a)
{
'json_class' => self.class.name,
'id' => @id,
'name' => dump_string(@name),
'recipes' => @list,
'display_price' => @display_price
}.to_json(*a)
end
end
end
class BaseItem
def initialize
@id = 0
@name = ''
@icon_index = 0
@description = ''
@features = []
@note = ''
end
class Feature
def initialize(code = 0, data_id = 0, value = 0)
@code = code
@data_id = data_id
@value = value
end
end
end
class Actor < BaseItem
def initialize
super
@nickname = ''
@class_id = 1
@initial_level = 1
@max_level = 99
@character_name = ''
@character_index = 0
@face_name = ''
@face_index = 0
@equips = [0,0,0,0,0]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'nickname' => dump_string(@nickname),
'description' => dump_string(@description),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class Class < BaseItem
def initialize
super
@exp_params = [30,20,30,30]
@params = Table.new(8,100)
(1..99).each do |i|
@params[0,i] = 400+i*50
@params[1,i] = 80+i*10
(2..5).each {|j| @params[j,i] = 15+i*5/4 }
(6..7).each {|j| @params[j,i] = 30+i*5/2 }
end
@learnings = []
@features.push(RPG::BaseItem::Feature.new(23, 0, 1))
@features.push(RPG::BaseItem::Feature.new(22, 0, 0.95))
@features.push(RPG::BaseItem::Feature.new(22, 1, 0.05))
@features.push(RPG::BaseItem::Feature.new(22, 2, 0.04))
@features.push(RPG::BaseItem::Feature.new(41, 1))
@features.push(RPG::BaseItem::Feature.new(51, 1))
@features.push(RPG::BaseItem::Feature.new(52, 1))
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description),
'note' => dump_string(@note),
'learnings' => @learnings
}.to_json(*a)
end
class Learning
def initialize
@level = 1
@skill_id = 1
@note = ''
end
def to_json(*a)
{
'json_class' => self.class.name,
'note' => dump_string(@note)
}.to_json(*a)
end
end
end
class UsableItem < BaseItem
def initialize
super
@scope = 0
@occasion = 0
@speed = 0
@success_rate = 100
@repeats = 1
@tp_gain = 0
@hit_type = 0
@animation_id = 0
@damage = RPG::UsableItem::Damage.new
@effects = []
end
class Damage
def initialize
@type = 0
@element_id = 0
@formula = '0'
@variance = 20
@critical = false
end
end
class Effect
def initialize(code = 0, data_id = 0, value1 = 0, value2 = 0)
@code = code
@data_id = data_id
@value1 = value1
@value2 = value2
end
end
end
class Skill < UsableItem
def initialize
super
@scope = 1
@stype_id = 1
@mp_cost = 0
@tp_cost = 0
@message1 = ''
@message2 = ''
@required_wtype_id1 = 0
@required_wtype_id2 = 0
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description),
'message1' => dump_string(@message1),
'message2' => dump_string(@message2),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class Item < UsableItem
def initialize
super
@scope = 7
@itype_id = 1
@price = 0
@consumable = true
end
def key_item?
@itype_id == 2
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class EquipItem < BaseItem
def initialize
super
@price = 0
@etype_id = 0
@params = [0] * 8
end
end
class Weapon < EquipItem
def initialize
super
@wtype_id = 0
@animation_id = 0
@features.push(RPG::BaseItem::Feature.new(31, 1, 0))
@features.push(RPG::BaseItem::Feature.new(22, 0, 0))
end
def performance
params[2] + params[4] + params.inject(0) {|r, v| r += v }
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class Armor < EquipItem
def initialize
super
@atype_id = 0
@etype_id = 1
@features.push(RPG::BaseItem::Feature.new(22, 1, 0))
end
def performance
params[3] + params[5] + params.inject(0) {|r, v| r += v }
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class Enemy < BaseItem
def initialize
super
@battler_name = ''
@battler_hue = 0
@params = [100,0,10,10,10,10,10,10]
@exp = 0
@gold = 0
@drop_items = Array.new(3) { RPG::Enemy::DropItem.new }
@actions = [RPG::Enemy::Action.new]
@features.push(RPG::BaseItem::Feature.new(22, 0, 0.95))
@features.push(RPG::BaseItem::Feature.new(22, 1, 0.05))
@features.push(RPG::BaseItem::Feature.new(31, 1, 0))
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description),
'note' => dump_string(@note)
}.to_json(*a)
end
class DropItem
def initialize
@kind = 0
@data_id = 1
@denominator = 1
end
end
class Action
def initialize
@skill_id = 1
@condition_type = 0
@condition_param1 = 0
@condition_param2 = 0
@rating = 5
end
end
end
class State < BaseItem
def initialize
super
@restriction = 0
@priority = 50
@remove_at_battle_end = false
@remove_by_restriction = false
@auto_removal_timing = 0
@min_turns = 1
@max_turns = 1
@remove_by_damage = false
@chance_by_damage = 100
@remove_by_walking = false
@steps_to_remove = 100
@message1 = ''
@message2 = ''
@message3 = ''
@message4 = ''
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'message1' => dump_string(@message1),
'message2' => dump_string(@message2),
'message3' => dump_string(@message3),
'message4' => dump_string(@message4),
'note' => dump_string(@note)
}.to_json(*a)
end
end
class Map
def initialize(width, height)
@display_name = ''
@tileset_id = 1
@width = width
@height = height
@scroll_type = 0
@specify_battleback = false
@battleback_floor_name = ''
@battleback_wall_name = ''
@autoplay_bgm = false
@bgm = RPG::BGM.new
@autoplay_bgs = false
@bgs = RPG::BGS.new('', 80)
@disable_dashing = false
@encounter_list = []
@encounter_step = 30
@parallax_name = ''
@parallax_loop_x = false
@parallax_loop_y = false
@parallax_sx = 0
@parallax_sy = 0
@parallax_show = false
@note = ''
@data = Table.new(width, height, 4)
@events = {}
end
def to_json(*a)
{
'json_class' => self.class.name,
'display_name' => dump_string(@display_name),
'note' => dump_string(@note),
'events' => dump_array(@events.sort),
}.to_json(*a)
end
class Encounter
def initialize
@troop_id = 1
@weight = 10
@region_set = []
end
end
end
class MapInfo
def initialize
@name = ''
@parent_id = 0
@order = 0
@expanded = false
@scroll_x = 0
@scroll_y = 0
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Event
def initialize(x, y)
@id = 0
@name = ''
@x = x
@y = y
@pages = [RPG::Event::Page.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'pages' => @pages
}.to_json(*a)
end
class Page
def initialize
@condition = RPG::Event::Page::Condition.new
@graphic = RPG::Event::Page::Graphic.new
@move_type = 0
@move_speed = 3
@move_frequency = 3
@move_route = RPG::MoveRoute.new
@walk_anime = true
@step_anime = false
@direction_fix = false
@through = false
@priority_type = 0
@trigger = 0
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
class Condition
def initialize
@switch1_valid = false
@switch2_valid = false
@variable_valid = false
@self_switch_valid = false
@item_valid = false
@actor_valid = false
@switch1_id = 1
@switch2_id = 1
@variable_id = 1
@variable_value = 0
@self_switch_ch = 'A'
@item_id = 1
@actor_id = 1
end
end
class Condition
def initialize
@switch1_valid = false
@switch2_valid = false
@variable_valid = false
@self_switch_valid = false
@item_valid = false
@actor_valid = false
@switch1_id = 1
@switch2_id = 1
@variable_id = 1
@variable_value = 0
@self_switch_ch = 'A'
@item_id = 1
@actor_id = 1
end
end
class RPG::Event::Page::Graphic
def initialize
@tile_id = 0
@character_name = ''
@character_index = 0
@direction = 2
@pattern = 0
end
end
end
end
class EventCommand
def initialize(code = 0, indent = 0, parameters = [])
@code = code
@indent = indent
@parameters = parameters
end
attr_accessor :code
attr_accessor :indent
attr_accessor :parameters
def to_json(*a)
obj = {}
obj['json_class'] = self.class.name
(type, is_text) = get_event_type(@code, @parameters)
obj['type'] = type
if is_text then
obj['parameters'] = dump_parameters(@parameters)
end
obj.to_json(*a)
end
end
class MoveRoute
def initialize
@repeat = true
@skippable = false
@wait = false
@list = [RPG::MoveCommand.new]
end
end
class MoveCommand
def initialize(code = 0, parameters = [])
@code = code
@parameters = parameters
end
end
class Troop
def initialize
@id = 0
@name = ''
@members = []
@pages = [RPG::Troop::Page.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'pages' => @pages
}.to_json(*a)
end
class Member
def initialize
@enemy_id = 1
@x = 0
@y = 0
@hidden = false
end
end
class Page
def initialize
@condition = RPG::Troop::Page::Condition.new
@span = 0
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
class Condition
def initialize
@turn_ending = false
@turn_valid = false
@enemy_valid = false
@actor_valid = false
@switch_valid = false
@turn_a = 0
@turn_b = 0
@enemy_index = 0
@enemy_hp = 50
@actor_id = 1
@actor_hp = 50
@switch_id = 1
end
end
end
end
class Animation
def initialize
@id = 0
@name = ''
@animation1_name = ''
@animation1_hue = 0
@animation2_name = ''
@animation2_hue = 0
@position = 1
@frame_max = 1
@frames = [RPG::Animation::Frame.new]
@timings = []
end
def to_screen?
@position == 3
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
class Frame
def initialize
@cell_max = 0
@cell_data = Table.new(0, 0)
end
end
class Timing
def initialize
@frame = 0
@se = RPG::SE.new('', 80)
@flash_scope = 0
@flash_color = Color.new(255,255,255,255)
@flash_duration = 5
end
end
end
class Tileset
def initialize
@id = 0
@mode = 1
@name = ''
@tileset_names = Array.new(9).collect{''}
@flags = Table.new(8192)
@flags[0] = 0x0010
(2048..2815).each {|i| @flags[i] = 0x000F}
(4352..8191).each {|i| @flags[i] = 0x000F}
@note = ''
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class CommonEvent
def initialize
@id = 0
@name = ''
@trigger = 0
@switch_id = 1
@list = [RPG::EventCommand.new]
end
def autorun?
@trigger == 1
end
def parallel?
@trigger == 2
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
end
class System
def initialize
@game_title = ''
@version_id = 0
@japanese = true
@party_members = [1]
@currency_unit = ''
@elements = [nil, '']
@skill_types = [nil, '']
@weapon_types = [nil, '']
@armor_types = [nil, '']
@switches = [nil, '']
@variables = [nil, '']
@boat = RPG::System::Vehicle.new
@ship = RPG::System::Vehicle.new
@airship = RPG::System::Vehicle.new
@title1_name = ''
@title2_name = ''
@opt_draw_title = true
@opt_use_midi = false
@opt_transparent = false
@opt_followers = true
@opt_slip_death = false
@opt_floor_death = false
@opt_display_tp = true
@opt_extra_exp = false
@window_tone = Tone.new(0,0,0)
@title_bgm = RPG::BGM.new
@battle_bgm = RPG::BGM.new
@battle_end_me = RPG::ME.new
@gameover_me = RPG::ME.new
@sounds = Array.new(24) { RPG::SE.new }
@test_battlers = []
@test_troop_id = 1
@start_map_id = 1
@start_x = 0
@start_y = 0
@terms = RPG::System::Terms.new
@battleback1_name = ''
@battleback2_name = ''
@battler_name = ''
@battler_hue = 0
@edit_map_id = 1
end
def to_json(*a)
{
'json_class' => self.class.name,
'game_title' => dump_string(@game_title),
'currency_unit' => dump_string(@currency_unit),
'elements' => dump_array(@elements),
'skill_types' => dump_array(@skill_types),
'weapon_types' => dump_array(@weapon_types),
'armor_types' => dump_array(@armor_types),
'terms' => @terms
}.to_json(*a)
end
class Vehicle
def initialize
@character_name = ''
@character_index = 0
@bgm = RPG::BGM.new
@start_map_id = 0
@start_x = 0
@start_y = 0
end
end
class Terms
def initialize
@basic = Array.new(8) {''}
@params = Array.new(8) {''}
@etypes = Array.new(5) {''}
@commands = Array.new(23) {''}
end
def to_json(*a)
{
'json_class' => self.class.name,
'basic' => dump_array(@basic),
'parameters' => dump_array(@params),
'equipment types' => dump_array(@etypes),
'commands' => dump_array(@commands)
}.to_json(*a)
end
end
class TestBattler
def initialize
@actor_id = 1
@level = 1
@equips = [0,0,0,0,0]
end
end
end
class AudioFile
def initialize(name = '', volume = 100, pitch = 100)
@name = name
@volume = volume
@pitch = pitch
end
end
class BGM < AudioFile
@@last = BGM.new
def play(pos = 0)
if @name.empty?
Audio.bgm_stop
@@last = BGM.new
else
Audio.bgm_play('Audio/BGM/' + @name, @volume, @pitch, pos)
@@last = self.clone
end
end
def replay
play(@pos)
end
def self.stop
Audio.bgm_stop
@@last = BGM.new
end
def self.fade(time)
Audio.bgm_fade(time)
@@last = BGM.new
end
def self.last
@@last.pos = Audio.bgm_pos
@@last
end
end
class BGS < AudioFile
@@last = BGS.new
def play(pos = 0)
if @name.empty?
Audio.bgs_stop
@@last = BGS.new
else
Audio.bgs_play('Audio/BGS/' + @name, @volume, @pitch, pos)
@@last = self.clone
end
end
def replay
play(@pos)
end
def self.stop
Audio.bgs_stop
@@last = BGS.new
end
def self.fade(time)
Audio.bgs_fade(time)
@@last = BGS.new
end
def self.last
@@last.pos = Audio.bgs_pos
@@last
end
end
class ME < AudioFile
def play
if @name.empty?
Audio.me_stop
else
Audio.me_play('Audio/ME/' + @name, @volume, @pitch)
end
end
def self.stop
Audio.me_stop
end
def self.fade(time)
Audio.me_fade(time)
end
end
class SE < AudioFile
def play
unless @name.empty?
Audio.se_play('Audio/SE/' + @name, @volume, @pitch)
end
end
def self.stop
Audio.se_stop
end
end
end
# Table of what type of event command the event codes corresponds to.
EVENT_COMMAND_CODES = {
0 => ["Empty", false],
101 => ["Show Text Attributes", false],
102 => ["Show Choices", true],
103 => ["Input Number", false],
104 => ["Select Key Item", false],
105 => ["Show Scrolling Text Attributes", false],
108 => ["Comment", true],
111 => ["Conditional Branch", false],
112 => ["Loop", false],
113 => ["Break Loop", false],
115 => ["Exit Event Processing", false],
117 => ["Call Common Event", false],
118 => ["Label", false],
119 => ["Jump to Label", false],
121 => ["Control Switches", false],
122 => ["Control Variables", false],
123 => ["Control Self Switch", false],
124 => ["Control Timer", false],
125 => ["Change Gold", false],
126 => ["Change Items", false],
127 => ["Change Weapons", false],
128 => ["Change Armor", false],
129 => ["Change Party Member", false],
132 => ["Change Battle BGM", false],
133 => ["Change Battle End ME", false],
134 => ["Change Save Access", false],
135 => ["Change Menu Access", false],
136 => ["Change Encounter", false],
137 => ["Change Formation Access", false],
138 => ["Change Window Color", false],
201 => ["Transfer Player", false],
202 => ["Set Vehicle Location", false],
203 => ["Set Event Location", false],
204 => ["Scroll Map", false],
205 => ["Set Move Route", false],
206 => ["Get on/off Vehicle", false],
211 => ["Change Transparency", false],
212 => ["Show Animation", false],
213 => ["Shot Balloon Icon", false],
214 => ["Erase Event", false],
216 => ["Change Player Followers", false],
217 => ["Gather Followers", false],
221 => ["Fadeout Screen", false],
222 => ["Fadein Screen", false],
223 => ["Tint Screen", false],
224 => ["Flash Screen", false],
225 => ["Shake Screen", false],
230 => ["Wait", false],
231 => ["Show Picture", false],
232 => ["Move Picture", false],
233 => ["Rotate Picture", false],
234 => ["Tint Picture", false],
235 => ["Erase Picture", false],
236 => ["Set Weather Effects", false],
241 => ["Play BGM", false],
242 => ["Fadeout BGM", false],
243 => ["Save BGM", false],
244 => ["Replay BGM", false],
245 => ["Play BGS", false],
246 => ["Fadeout BGS", false],
249 => ["Play ME", false],
250 => ["Play SE", false],
251 => ["Stop SE", false],
261 => ["Play Movie", false],
281 => ["Change Map Display", false],
282 => ["Change Tileset", false],
283 => ["Change Battle Back", false],
284 => ["Change Parallax Back", false],
285 => ["Get Location Info", false],
301 => ["Battle Processing", false],
302 => ["Shop Processing", false],
303 => ["Name Input Processing", false],
311 => ["Change HP", false],
312 => ["Change MP", false],
313 => ["Change State", false],
314 => ["Recover All", false],
315 => ["Change EXP", false],
316 => ["Change Level", false],
317 => ["Change Parameters", false],
318 => ["Change Skills", false],
319 => ["Change Equipment", false],
320 => ["Change Actor Name", true],
321 => ["Change Actor Class", false],
322 => ["Change Actor Graphic", false],
323 => ["Change Vehicle Graphic", false],
324 => ["Change Actor Nickname", true],
331 => ["Change Enemy HP", false],
332 => ["Change Enemy MP", false],
333 => ["Change Enemy State", false],
334 => ["Enemy Recover All", false],
335 => ["Enemy Appear", false],
336 => ["Enemy Transform", false],
337 => ["Show Battle Animation", false],
339 => ["Force Action", false],
340 => ["Abort Battle", false],
351 => ["Open Menu Screen", false],
352 => ["Open Save Screen", false],
353 => ["Game Over", false],
354 => ["Return to Title Screen", false],
355 => ["Script", true],
401 => ["Show Text", true],
402 => ["When", false],
403 => ["When Cancel", false],
404 => ["Choices End", false],
405 => ["Show Scrolling Text", true],
408 => ["Comment More", true],
411 => ["Else", false],
412 => ["Branch End", false],
413 => ["Repeat Above", false],
601 => ["If Win", false],
602 => ["If Escape", false],
603 => ["If Lose", false],
604 => ["Battle Processing End", false],
605 => ["Shop Item", false],
655 => ["Script More", true]
}
# Given an event code, returns the type of the event and whether it can contain
# something translatable.
def get_event_type(code, params)
type = EVENT_COMMAND_CODES[code]
# Control variables can be assigned scripts:
if code == 122 and params[3] == 4 then
type[1] = true
end
type.nil? ? [code, false] : type
end
def merge_event_commands(commands)
merged_cmds = []
i = 0
while i < commands.length
cmd = commands[i]
match_code = case cmd.code
when 401 then 401 # Show Text
when 108 then 408 # Comment
when 355 then 655 # Script
else 0
end
if match_code != 0 and commands[i+1].code == match_code
params = cmd.parameters
while commands[i+1].code == match_code
i += 1
params.concat commands[i].parameters
end
cmd = RPG::EventCommand.new(cmd.code, cmd.indent, params)
end
merged_cmds.push cmd
i += 1
end
merged_cmds
end
def split_event_commands(commands)
split_cmds = []
commands.each do |cmd|
match_code = case cmd.code
when 401 then 401
when 108 then 408
when 355 then 655
else 0
end
if match_code != 0 and cmd.parameters.length > 1
code = cmd.code
cmd.parameters.each do |p|
split_cmds.push RPG::EventCommand.new(code, cmd.indent, [p])
code = match_code
end
else
split_cmds.push cmd
end
end
split_cmds
end
require_relative 'common_db'
# This file contains all the RPG data structures for RPG Maker XP
module RPG
class Actor
def initialize
@id = 0
@name = ""
@class_id = 1
@initial_level = 1
@final_level = 99
@exp_basis = 30
@exp_inflation = 30
@character_name = ""
@character_hue = 0
@battler_name = ""
@battler_hue = 0
@parameters = Table.new(6,100)
for i in 1..99
@parameters[0,i] = 500+i*50
@parameters[1,i] = 500+i*50
@parameters[2,i] = 50+i*5
@parameters[3,i] = 50+i*5
@parameters[4,i] = 50+i*5
@parameters[5,i] = 50+i*5
end
@weapon_id = 0
@armor1_id = 0
@armor2_id = 0
@armor3_id = 0
@armor4_id = 0
@weapon_fix = false
@armor1_fix = false
@armor2_fix = false
@armor3_fix = false
@armor4_fix = false
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Class
def initialize
@id = 0
@name = ""
@position = 0
@weapon_set = []
@armor_set = []
@element_ranks = Table.new(1)
@state_ranks = Table.new(1)
@learnings = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
class Learning
def initialize
@level = 1
@skill_id = 1
end
end
end
class Skill
def initialize
@id = 0
@name = ""
@icon_name = ""
@description = ""
@scope = 0
@occasion = 1
@animation1_id = 0
@animation2_id = 0
@menu_se = RPG::AudioFile.new("", 80)
@common_event_id = 0
@sp_cost = 0
@power = 0
@atk_f = 0
@eva_f = 0
@str_f = 0
@dex_f = 0
@agi_f = 0
@int_f = 100
@hit = 100
@pdef_f = 0
@mdef_f = 100
@variance = 15
@element_set = []
@plus_state_set = []
@minus_state_set = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description)
}.to_json(*a)
end
end
class Item
def initialize
@id = 0
@name = ""
@icon_name = ""
@description = ""
@scope = 0
@occasion = 0
@animation1_id = 0
@animation2_id = 0
@menu_se = RPG::AudioFile.new("", 80)
@common_event_id = 0
@price = 0
@consumable = true
@parameter_type = 0
@parameter_points = 0
@recover_hp_rate = 0
@recover_hp = 0
@recover_sp_rate = 0
@recover_sp = 0
@hit = 100
@pdef_f = 0
@mdef_f = 0
@variance = 0
@element_set = []
@plus_state_set = []
@minus_state_set = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description)
}.to_json(*a)
end
end
class Weapon
def initialize
@id = 0
@name = ""
@icon_name = ""
@description = ""
@animation1_id = 0
@animation2_id = 0
@price = 0
@atk = 0
@pdef = 0
@mdef = 0
@str_plus = 0
@dex_plus = 0
@agi_plus = 0
@int_plus = 0
@element_set = []
@plus_state_set = []
@minus_state_set = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description)
}.to_json(*a)
end
end
class Armor
def initialize
@id = 0
@name = ""
@icon_name = ""
@description = ""
@kind = 0
@auto_state_id = 0
@price = 0
@pdef = 0
@mdef = 0
@eva = 0
@str_plus = 0
@dex_plus = 0
@agi_plus = 0
@int_plus = 0
@guard_element_set = []
@guard_state_set = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'description' => dump_string(@description)
}.to_json(*a)
end
end
class Enemy
def initialize
@id = 0
@name = ""
@battler_name = ""
@battler_hue = 0
@maxhp = 500
@maxsp = 500
@str = 50
@dex = 50
@agi = 50
@int = 50
@atk = 100
@pdef = 100
@mdef = 100
@eva = 0
@animation1_id = 0
@animation2_id = 0
@element_ranks = Table.new(1)
@state_ranks = Table.new(1)
@actions = [RPG::Enemy::Action.new]
@exp = 0
@gold = 0
@item_id = 0
@weapon_id = 0
@armor_id = 0
@treasure_prob = 100
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
class Action
def initialize
@kind = 0
@basic = 0
@skill_id = 1
@condition_turn_a = 0
@condition_turn_b = 1
@condition_hp = 100
@condition_level = 1
@condition_switch_id = 0
@rating = 5
end
end
end
class Troop
def initialize
@id = 0
@name = ""
@members = []
@pages = [RPG::BattleEventPage.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'pages' => @pages
}.to_json(*a)
end
class Member
def initialize
@enemy_id = 1
@x = 0
@y = 0
@hidden = false
@immortal = false
end
end
class Page
def initialize
@condition = RPG::Troop::Page::Condition.new
@span = 0
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
class Condition
def initialize
@turn_valid = false
@enemy_valid = false
@actor_valid = false
@switch_valid = false
@turn_a = 0
@turn_b = 0
@enemy_index = 0
@enemy_hp = 50
@actor_id = 1
@actor_hp = 50
@switch_id = 1
end
end
end
end
class State
def initialize
@id = 0
@name = ""
@animation_id = 0
@restriction = 0
@nonresistance = false
@zero_hp = false
@cant_get_exp = false
@cant_evade = false
@slip_damage = false
@rating = 5
@hit_rate = 100
@maxhp_rate = 100
@maxsp_rate = 100
@str_rate = 100
@dex_rate = 100
@agi_rate = 100
@int_rate = 100
@atk_rate = 100
@pdef_rate = 100
@mdef_rate = 100
@eva = 0
@battle_only = true
@hold_turn = 0
@auto_release_prob = 0
@shock_release_prob = 0
@guard_element_set = []
@plus_state_set = []
@minus_state_set = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Event
def initialize(x, y)
@id = 0
@name = ""
@x = x
@y = y
@pages = [RPG::Event::Page.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'pages' => @pages
}.to_json(*a)
end
class Page
def initialize
@condition = RPG::Event::Page::Condition.new
@graphic = RPG::Event::Page::Graphic.new
@move_type = 0
@move_speed = 3
@move_frequency = 3
@move_route = RPG::MoveRoute.new
@walk_anime = true
@step_anime = false
@direction_fix = false
@through = false
@always_on_top = false
@trigger = 0
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
class Condition
def initialize
@switch1_valid = false
@switch2_valid = false
@variable_valid = false
@self_switch_valid = false
@switch1_id = 1
@switch2_id = 1
@variable_id = 1
@variable_value = 0
@self_switch_ch = "A"
end
end
class Graphic
def initialize
@tile_id = 0
@character_name = ""
@character_hue = 0
@direction = 2
@pattern = 0
@opacity = 255
@blend_type = 0
end
end
end
end
class EventCommand
def initialize(code = 0, indent = 0, parameters = [])
@code = code
@indent = indent
@parameters = parameters
end
attr_accessor :code
attr_accessor :indent
attr_accessor :parameters
def to_json(*a)
obj = {}
obj['json_class'] = self.class.name
(type, is_text) = get_event_type(@code)
obj['type'] = type
if is_text then
obj['parameters'] = dump_parameters(@parameters)
end
obj.to_json(*a)
end
end
class MapInfo
def initialize
@name = ""
@parent_id = 0
@order = 0
@expanded = false
@scroll_x = 0
@scroll_y = 0
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class Map
def initialize(width, height)
@tileset_id = 1
@width = width
@height = height
@autoplay_bgm = false
@bgm = RPG::AudioFile.new
@autoplay_bgs = false
@bgs = RPG::AudioFile.new("", 80)
@encounter_list = []
@encounter_step = 30
@data = Table.new(width, height, 3)
@events = {}
end
def to_json(*a)
{
'json_class' => self.class.name,
'events' => dump_array(@events.sort)
}.to_json(*a)
end
end
class MoveRoute
def initialize
@repeat = true
@skippable = false
@list = [RPG::MoveCommand.new]
end
end
class MoveCommand
def initialize(code = 0, parameters = [])
@code = code
@parameters = parameters
end
end
class Animation
def initialize
@id = 0
@name = ""
@animation_name = ""
@animation_hue = 0
@position = 1
@frame_max = 1
@frames = [RPG::Animation::Frame.new]
@timings = []
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
class Frame
def initialize
@cell_max = 0
@cell_data = Table.new(0, 0)
end
end
class Timing
def initialize
@frame = 0
@se = RPG::AudioFile.new("", 80)
@flash_scope = 0
@flash_color = Color.new(255,255,255,255)
@flash_duration = 5
@condition = 0
end
end
end
class Tileset
def initialize
@id = 0
@name = ""
@tileset_name = ""
@autotile_names = [""]*7
@panorama_name = ""
@panorama_hue = 0
@fog_name = ""
@fog_hue = 0
@fog_opacity = 64
@fog_blend_type = 0
@fog_zoom = 200
@fog_sx = 0
@fog_sy = 0
@battleback_name = ""
@passages = Table.new(384)
@priorities = Table.new(384)
@priorities[0] = 5
@terrain_tags = Table.new(384)
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name)
}.to_json(*a)
end
end
class CommonEvent
def initialize
@id = 0
@name = ""
@trigger = 0
@switch_id = 1
@list = [RPG::EventCommand.new]
end
def to_json(*a)
{
'json_class' => self.class.name,
'name' => dump_string(@name),
'commands' => merge_event_commands(@list)
}.to_json(*a)
end
end
class System
def initialize
@magic_number = 0
@party_members = [1]
@elements = [nil, ""]
@switches = [nil, ""]
@variables = [nil, ""]
@windowskin_name = ""
@title_name = ""
@gameover_name = ""
@battle_transition = ""
@title_bgm = RPG::AudioFile.new
@battle_bgm = RPG::AudioFile.new
@battle_end_me = RPG::AudioFile.new
@gameover_me = RPG::AudioFile.new
@cursor_se = RPG::AudioFile.new("", 80)
@decision_se = RPG::AudioFile.new("", 80)
@cancel_se = RPG::AudioFile.new("", 80)
@buzzer_se = RPG::AudioFile.new("", 80)
@equip_se = RPG::AudioFile.new("", 80)
@shop_se = RPG::AudioFile.new("", 80)
@save_se = RPG::AudioFile.new("", 80)
@load_se = RPG::AudioFile.new("", 80)
@battle_start_se = RPG::AudioFile.new("", 80)
@escape_se = RPG::AudioFile.new("", 80)
@actor_collapse_se = RPG::AudioFile.new("", 80)
@enemy_collapse_se = RPG::AudioFile.new("", 80)
@words = RPG::System::Words.new
@test_battlers = []
@test_troop_id = 1
@start_map_id = 1
@start_x = 0
@start_y = 0
@battleback_name = ""
@battler_name = ""
@battler_hue = 0
@edit_map_id = 1
end
def to_json(*a)
{
'json_class' => self.class.name,
'elements' => dump_array(@elements),
'switches' => dump_array(@switches),
'variables' => dump_array(@variables),
'words' => @words
}.to_json(*a)
end
class Words
def initialize
@gold = ""
@hp = ""
@sp = ""
@str = ""
@dex = ""
@agi = ""
@int = ""
@atk = ""
@pdef = ""
@mdef = ""
@weapon = ""
@armor1 = ""
@armor2 = ""
@armor3 = ""
@armor4 = ""
@attack = ""
@skill = ""
@guard = ""
@item = ""
@equip = ""
end
def to_json(*a)
{
'gold' => dump_string(@gold),
'hp' => dump_string(@hp),
'sp' => dump_string(@sp),
'str' => dump_string(@str),
'dex' => dump_string(@dex),
'agi' => dump_string(@agi),
'int' => dump_string(@int),
'atk' => dump_string(@atk),
'pdef' => dump_string(@pdef),
'mdef' => dump_string(@mdef),
'weapon' => dump_string(@weapon),
'armor1' => dump_string(@armor1),
'armor2' => dump_string(@armor2),
'armor3' => dump_string(@armor3),
'armor4' => dump_string(@armor4),
'attack' => dump_string(@attack),
'skill' => dump_string(@skill),
'guard' => dump_string(@guard),
'item' => dump_string(@item),
'equip' => dump_string(@equip)
}.to_json(*a)
end
end
class TestBattler
def initialize
@actor_id = 1
@level = 1
@weapon_id = 0
@armor1_id = 0
@armor2_id = 0
@armor3_id = 0
@armor4_id = 0
end
end
end
class AudioFile
def initialize(name = "", volume = 100, pitch = 100)
@name = name
@volume = volume
@pitch = pitch
end
end
end
# Table of what type of event command the event codes corresponds to.
EVENT_COMMAND_CODES = {
0 => ["Empty", false],
101 => ["Show Text", true],
102 => ["Show Choices", true],
103 => ["Input Number", false],
104 => ["Change Text Options", false],
105 => ["Button Input Processing", false],
106 => ["Wait", false],
108 => ["Comment", true],
111 => ["Conditional Branch", false],
112 => ["Loop", false],
113 => ["Break Loop", false],
115 => ["Exit Event Processing"],
116 => ["Erase Event"],
117 => ["Call Common Event", false],
118 => ["Label", false],
119 => ["Jump to Label", false],
121 => ["Control Switches", false],
122 => ["Control Variables", false],
123 => ["Control Self Switch", false],
124 => ["Control Timer", false],
125 => ["Change Gold", false],
126 => ["Change Items", false],
127 => ["Change Weapons", false],
128 => ["Change Armor", false],
129 => ["Change Party Member", false],
131 => ["Change Windowskin", false],
132 => ["Change Battle BGM", false],
133 => ["Change Battle End ME", false],
134 => ["Change Save Access", false],
135 => ["Change Menu Access", false],
136 => ["Change Encounter", false],
201 => ["Transfer Player", false],
202 => ["Set Event Location", false],
203 => ["Scroll Map", false],
204 => ["Change Map Settings", false],
205 => ["Change Fog Color Tone", false],
206 => ["Change Fog Opacity", false],
207 => ["Show Animation", false],
208 => ["Change Transparent Flag", false],
209 => ["Set Move Route", false],
210 => ["Wait for Move's Completion", false],
221 => ["Prepare for Transition", false],
222 => ["Execute Transition", false],
223 => ["Change Screen Color Tone", false],
224 => ["Screen Flash", false],
225 => ["Screen Shake", false],
231 => ["Show Picture", false],
232 => ["Move Picture", false],
233 => ["Rotate Picture", false],
234 => ["Change Picture Color Tone", false],
235 => ["Erase Picture", false],
236 => ["Set Weather Effects", false],
241 => ["Play BGM", false],
242 => ["Fade Out BGM", false],
245 => ["Play BGS", false],
246 => ["Fade Out BGS", false],
247 => ["Memorize BGM/BGS", false],
248 => ["Restore BGM/BGS", false],
249 => ["Play ME", false],
250 => ["Play SE", false],
251 => ["Stop SE", false],
301 => ["Battle Processing", false],
302 => ["Shop Processing", false],
303 => ["Name Input Processing", false],
311 => ["Change HP", false],
312 => ["Change SP", false],
313 => ["Change State", false],
314 => ["Recover All", false],
315 => ["Change EXP", false],
316 => ["Change Level", false],
317 => ["Change Parameters", false],
318 => ["Change Skills", false],
319 => ["Change Equipment", false],
320 => ["Change Actor Name", true],
321 => ["Change Actor Class", false],
322 => ["Change Actor Graphic", false],
331 => ["Change Enemy HP", false],
332 => ["Change Enemy SP", false],
333 => ["Change Enemy State", false],
334 => ["Enemy Recover All", false],
335 => ["Enemy Appearance", false],
336 => ["Enemy Transform", false],
337 => ["Show Battle Animation", false],
338 => ["Deal Damage", false],
339 => ["Force Action", false],
340 => ["Abort Battle", false],
351 => ["Call Menu Screen", false],
352 => ["Call Save Screen", false],
353 => ["Game Over", false],
354 => ["Return to Title Screen", false],
355 => ["Script", true],
401 => ["Show Text More", true],
402 => ["When", false],
403 => ["When Cancel", false],
404 => ["Choices End", false],
408 => ["Comment More", true],
411 => ["Else", false],
412 => ["Branch End", false],
413 => ["Repeat Above", false],
509 => ["Move Command", false],
601 => ["If Win", false],
602 => ["If Escape", false],
603 => ["If Lose", false],
604 => ["Battle Processing End", false],
605 => ["Shop Item", false],
655 => ["Script More", true]
}
# Given an event code, returns the type of the event and whether it can contain
# something translatable.
def get_event_type(code)
type = EVENT_COMMAND_CODES[code]
type.nil? ? [code, false] : type
end
def merge_event_commands(commands)
merged_cmds = []
i = 0
while i < commands.length
cmd = commands[i]
match_code = case cmd.code
when 101 then 401 # Show Text
when 108 then 408 # Comment
when 355 then 655 # Script
else 0
end
if match_code != 0 and commands[i+1].code == match_code
params = cmd.parameters
while commands[i+1].code == match_code
i += 1
params.concat commands[i].parameters
end
cmd = RPG::EventCommand.new(cmd.code, cmd.indent, params)
end
merged_cmds.push cmd
i += 1
end
merged_cmds
end
def split_event_commands(commands)
split_cmds = []
commands.each do |cmd|
match_code = case cmd.code
when 101 then 401
when 108 then 408
when 355 then 655
else 0
end
if match_code != 0 and cmd.parameters.length > 1
code = cmd.code
cmd.parameters.each do |p|
split_cmds.push RPG::EventCommand.new(code, cmd.indent, [p])
code = match_code
end
else
split_cmds.push cmd
end
end
split_cmds
end
#!/usr/bin/env ruby
# coding: utf-8
require 'yaml'
require 'zlib'
require_relative 'rmvxace_db'
IGNORE_SMALL = 10
KEEP_EXISTING = false
def get_project_path
project_file = Dir.glob("*.project").first
return nil unless project_file
path = File.open(project_file, 'r') do |file|
file.gets&.strip
end
path
end
translation_out = get_project_path.gsub('\\','/')
if not translation_out
translation_out = "translation_out"
KEEP_EXISTING = true
end
puts "Output directory is #{translation_out}"
if not Dir.exist?(translation_out)
Dir.mkdir(translation_out)
end
data_dir = File.join(translation_out, "Data")
if not Dir.exist?(data_dir)
Dir.mkdir(data_dir)
end
yml_files = Dir.glob(File.join(translation_out, "Data/*.yml"))
yml_files.each do |yml|
next unless File.exist?(yml)
name = File.basename(yml, '.yml')
next if name == 'Scripts'
data = YAML.unsafe_load_file(yml)
out_file = File.join(data_dir, "#{name}.rvdata2")
unless KEEP_EXISTING and File.exist?(out_file)
puts "Repacking Data/#{name}.rvdata2 ..."
File.open(out_file, 'wb') do |file|
file.write(Marshal.dump(data))
end
end
end
unless Dir.exist?('Data/Scripts')
exit
end
puts "Repacking Data/Scripts.rvdata2 ..."
data_old = Marshal.load(File.open('Data/Scripts.rvdata2', 'rb'))
def sanitize_filename(filename)
filename.strip.tap do |name|
name.gsub!(/[\:\*\?\"\<\>\|]|\.\.+/, '_')
end
end
class Array
def row_index(item, column)
self.each_with_index { |row, i| return i if row[column] == item || (column == 1 and sanitize_filename(row[column]) == item) }
return
end
end
def extract_name_and_index(filename)
if match = filename.match(/^(.+?)_(\d+)$/)
[match[1], match[2]]
else
[filename, nil]
end
end
Dir.glob('Data/Scripts/*.rb') do |rb|
name = File.basename(rb, '.rb')
File.open(rb, 'rb') do |file|
script = file.read
next if IGNORE_SMALL > 0 and script.length < IGNORE_SMALL
name, index_ = extract_name_and_index(name)
if index_.nil?
index_ = data_old.row_index(name, 1)
else
index_ = data_old.row_index(index_.to_i, 0)
end
if index_
old_name = data_old[index_][1]
old_script_compressed = data_old[index_][2]
old_script = Zlib::Inflate.inflate(old_script_compressed)
unless old_script == script
puts " Replacing... #{name} @#{index_}"
deflated = Zlib::Deflate.deflate(script)
deflated = deflated.dup.force_encoding("BINARY")
data_old[index_][2] = deflated
end
#puts " #{old_name} @#{index_}; len: #{old_script.length}"
end
end
end
out_scripts = File.join(data_dir, 'Scripts.rvdata2')
File.open(out_scripts, 'wb') do |file|
file.write(Marshal.dump(data_old))
end
#!/usr/bin/env ruby
# coding: cp932
require 'yaml'
require 'zlib'
require_relative 'rmxp_db'
if not File.exists?('translation_out')
Dir.mkdir('translation_out')
end
if not File.exists?('translation_out/Data')
Dir.mkdir('translation_out/Data')
end
[
'translation_out/Data/Actors.yml',
'translation_out/Data/Animations.yml',
'translation_out/Data/Armors.yml',
'translation_out/Data/Classes.yml',
'translation_out/Data/CommonEvents.yml',
'translation_out/Data/Enemies.yml',
'translation_out/Data/Items.yml',
*Dir.glob('translation_out/Data/Map[0-9][0-9][0-9].yml'),
'translation_out/Data/MapInfos.yml',
'translation_out/Data/Skills.yml',
'translation_out/Data/States.yml',
'translation_out/Data/System.yml',
'translation_out/Data/Troops.yml',
'translation_out/Data/Weapons.yml'
].each do |yml|
if not File.exists?(yml); next end
data = ''
data = YAML.load_file(yml)
out_file = 'translation_out/Data/'+File.basename(yml,'.yml')+'.rxdata'
if not File.exists?(out_file)
puts "Packaging... #{out_file}"
File.open(out_file, 'wb') do |file|
file.write(Marshal.dump(data))
end
end
end
if not File.exists?('Data/Scripts'); exit end
puts "Packaging... translation_out/Data/Scripts.rxdata"
data_old = Marshal.load(File.open('Data/Scripts.rxdata', 'rb'))
class Array
def row_index(item, column)
self.each_with_index{|raw, i| return i if raw[column] == item}
return
end
end
Dir.glob('Data/Scripts/*.rb') do |rb|
name = File.basename(rb, '.rb')
File.open(rb, 'rb') do |file|
script = file.read
if script.length < 100; next end
index_ = data_old.row_index(name, 1)
if index_
oldname = data_old[index_][1]
if not Zlib::Inflate.inflate(data_old[index_][2]).eql? script
puts " Replacing... #{name}"
data_old[index_][2] = Zlib::Deflate.deflate(script)
end
end
end
end
File.open('translation_out/Data/Scripts.rxdata', 'wb') do |file|
file.write(Marshal.dump(data_old))
end
#!/usr/bin/env ruby
# coding: utf-8
require 'zlib'
require 'psych'
require 'yaml'
require_relative 'rmvxace_db'
INCLUDE_EMPTY = false
module PsychIntegerParsingPatch
def parse_int string
Integer(string.gsub(/[,_]+/, ''))
end
end
module Psych
module Visitors
class YAMLTree
alias_method :original_visit_String, :visit_String
def visit_String(str)
# or @emitter.scalar(str, nil, nil, true, false, Nodes::Scalar::DOUBLE_QUOTED)
node = original_visit_String(str)
node.quoted = true
node.style = Psych::Nodes::Scalar::DOUBLE_QUOTED
node
end
end
end
end
Psych::ScalarScanner.prepend(PsychIntegerParsingPatch)
def load_rvdata2(filename)
unknown_classes = []
# Temporarily override Object.const_missing to catch undefined classes
Object.singleton_class.class_eval do
alias_method :__orig_const_missing, :const_missing
define_method(:const_missing) do |const_name|
unknown_classes << const_name unless unknown_classes.include?(const_name)
Struct.new(const_name).new
end
end
data = nil
File.open(filename, 'rb') do |f|
data = Marshal.load(f)
end
unless unknown_classes.empty?
puts "Warning: Unknown classes encountered in #{filename}: #{unknown_classes.join(', ')}"
end
data
ensure
# Restore original const_missing
Object.singleton_class.class_eval do
alias_method :const_missing, :__orig_const_missing
remove_method :__orig_const_missing rescue nil
end
end
def save_yaml(file, data)
File.open(file, 'w') do |f|
f.write(YAML.dump(data, line_width: -1))
end
end
if Dir.exist?('Data')
Dir.glob('Data/*.rvdata2').each do |rvdata_filename|
name = File.basename(rvdata_filename, '.rvdata2')
next if name == 'Scripts'
yaml_filename = "Data/#{name}.yml"
if !File.exist?(yaml_filename) && File.exist?(rvdata_filename)
puts "Unpacking... #{name}"
begin
data = load_rvdata2(rvdata_filename)
save_yaml(yaml_filename, data)
rescue => e
puts "Failed to unpack #{name}: #{e.message}"
end
end
end
end
def cleanup_scripts_rb
if !Dir.exist?('Data')
return
end
if Dir.exist?('Data/Scripts')
Dir.glob('Data/Scripts/*.rb') do |file|
File::delete(file)
end
else
Dir.mkdir('Data/Scripts')
end
end
def sanitize_filename(filename)
filename.strip.tap do |name|
name.gsub!(/[\:\*\?\"\<\>\|]|\.\.+/, '_')
end
end
# Scripts
def unpack_scripts
if !Dir.exist?('Data') || !File.exist?('Data/Scripts.rvdata2')
return
end
puts "Unpacking... Scripts"
data = load_rvdata2('Data/Scripts.rvdata2')
name_counts = {}
data.each do |id, name, script|
next if name.empty?
name_counts[name] = (name_counts[name] || 0) + 1
end
indexes = []
data.each do |id, name, script|
if name.empty?
next
end
indexes << [id, name]
filename = if name_counts[name] > 1
# same-named scripts can have different IDs, we account for this by adding them
"Data/Scripts/#{sanitize_filename(name)}_#{sanitize_filename(id.to_s)}.rb"
else
"Data/Scripts/#{sanitize_filename(name)}.rb"
end
file_data = Zlib::Inflate.inflate(script)
if file_data.length > 0 || INCLUDE_EMPTY
#puts "Unpacking... Scripts|#{name}"
File.open(filename, 'wb') do |f|
#f.write "# -*- id: #{id} -*-\n"
f.write file_data
#f.write "\n# -*- END_OF_SCRIPT -*-\n\n"
end
end
end
File.open('Data/Scripts.yml', 'wb') do |file|
file.write(YAML.dump(indexes))
end
end
cleanup_scripts_rb
unpack_scripts
#!/usr/bin/env ruby
# coding: cp932
require 'zlib'
require 'psych'
require 'zaml'
require_relative 'rmxp_db'
module PsychIntegerParsingPatch
def parse_int string
Integer(string.gsub(/[,_]+/, ''))
end
end
Psych::ScalarScanner.prepend(PsychIntegerParsingPatch)
def load_rvdata2(file)
Marshal.load(File.open(file, 'rb'))
end
def save_yaml(file, data)
File.open(file, 'w:cp932') do |f|
f.write(ZAML.dump(data))
end
end
DATA_NAMES = %w{
Actors Animations Armors
Classes CommonEvents Enemies
Items MapInfos Skills
States System
Troops Weapons
}
DATA_NAMES.each do |name|
rvdata_filename = 'Data/' + name + '.rxdata'
yaml_filename = 'Data/' + name + '.yml'
puts "Unpacking... #{name}"
data = load_rvdata2(rvdata_filename)
save_yaml(yaml_filename, data)
end
# Map
Dir.glob('Data/Map[0-9][0-9][0-9].rxdata').each do |rvdata_filename|
yaml_filename = 'Data/' + File.basename(rvdata_filename, ".*") + '.yml'
puts "Unpacking... #{rvdata_filename}"
data = load_rvdata2(rvdata_filename)
save_yaml(yaml_filename, data)
end
def cleanup_scripts_rb
if File.exists?('Data/Scripts')
Dir.glob('Data/Scripts/*.rb') do |file|
File::delete(file)
end
else
Dir.mkdir('Data/Scripts')
end
end
# Scripts
def unpack_scripts
data = load_rvdata2('Data/Scripts.rxdata')
indexes = []
data.each do |id, name, script|
if name.empty?
next
end
#puts "Unpacking... Scripts/#{name}"
indexes << id
File.open('Data/Scripts/'+name+'.rb', 'wb') do |f|
#f.write "# -*- id: #{id} -*-\n"
f.write Zlib::Inflate.inflate(script)
#f.write "\n# -*- END_OF_SCRIPT -*-\n\n"
end
end
File.open('Data/Scripts.yml', 'wb') do |file|
file.write(ZAML.dump(indexes))
end
end
puts "Unpacking... Scripts"
cleanup_scripts_rb
unpack_scripts
#!/bin/env ruby
# encoding: utf-8
#
# ZAML -- A partial replacement for YAML, writen with speed and code clarity
# in mind. ZAML fixes one YAML bug (loading Exceptions) and provides
# a replacement for YAML.dump() unimaginatively called ZAML.dump(),
# which is faster on all known cases and an order of magnitude faster
# with complex structures.
#
# http://github.com/hallettj/zaml
#
# Authors: Markus Roberts, Jesse Hallett, Ian McIntosh, Igal Koshevoy, Simon Chiang
#
require 'yaml'
class ZAML
VERSION = "0.1.3"
#
# Class Methods
#
def self.dump(stuff, where='')
z = new
stuff.to_zaml(z)
where << z.to_s
end
#
# Instance Methods
#
def initialize
@result = []
@indent = nil
@structured_key_prefix = nil
Label.counter_reset
emit('--- ')
end
def nested(tail=' ')
old_indent = @indent
@indent = "#{@indent || "\n"}#{tail}"
yield
@indent = old_indent
end
class Label
#
# YAML only wants objects in the datastream once; if the same object
# occurs more than once, we need to emit a label ("&idxxx") on the
# first occurrence and then emit a back reference (*idxxx") on any
# subsequent occurrence(s).
#
# To accomplish this we keeps a hash (by object id) of the labels of
# the things we serialize as we begin to serialize them. The labels
# initially serialize as an empty string (since most objects are only
# going to be be encountered once), but can be changed to a valid
# (by assigning it a number) the first time it is subsequently used,
# if it ever is. Note that we need to do the label setup BEFORE we
# start to serialize the object so that circular structures (in
# which we will encounter a reference to the object as we serialize
# it can be handled).
#
def self.counter_reset
@@previously_emitted_object = {}
@@next_free_label_number = 0
end
def initialize(obj,indent)
@indent = indent
@this_label_number = nil
@@previously_emitted_object[obj.object_id] = self
end
def to_s
@this_label_number ? ('&id%03d%s' % [@this_label_number, @indent]) : ''
end
def reference
@this_label_number ||= (@@next_free_label_number += 1)
@reference ||= '*id%03d' % @this_label_number
end
def self.for(obj)
@@previously_emitted_object[obj.object_id]
end
end
def new_label_for(obj)
Label.new(obj,(Hash === obj || Array === obj) ? "#{@indent || "\n"} " : ' ')
end
def first_time_only(obj)
if label = Label.for(obj)
emit(label.reference)
else
if @structured_key_prefix and not obj.is_a? String
emit(@structured_key_prefix)
@structured_key_prefix = nil
end
emit(new_label_for(obj))
yield
end
end
def emit(s)
@result << s
@recent_nl = false unless s.kind_of?(Label)
end
def nl(s='')
emit(@indent || "\n") unless @recent_nl
emit(s)
@recent_nl = true
end
def to_s
@result.join
end
def prefix_structured_keys(x)
@structured_key_prefix = x
yield
nl unless @structured_key_prefix
@structured_key_prefix = nil
end
end
################################################################
#
# Behavior for custom classes
#
################################################################
class Object
def to_yaml_properties
instance_variables.sort # Default YAML behavior
end
def zamlized_class_name(root)
"!ruby/#{root.name.downcase}#{self.class == root ? '' : ":#{self.class.name}"}"
end
def to_zaml(z)
z.first_time_only(self) {
z.emit(zamlized_class_name(Object))
z.nested {
instance_variables = to_yaml_properties
if instance_variables.empty?
z.emit(" {}")
else
instance_variables.each { |v|
z.nl
v[1..-1].to_zaml(z) # Remove leading '@'
z.emit(': ')
instance_variable_get(v).to_zaml(z)
}
end
}
}
end
end
################################################################
#
# Behavior for built-in classes
#
################################################################
class NilClass
def to_zaml(z)
z.emit('') # NOTE: blank turns into nil in YAML.load
end
end
class Symbol
def to_zaml(z)
z.emit(self.inspect)
end
end
class TrueClass
def to_zaml(z)
z.emit('true')
end
end
class FalseClass
def to_zaml(z)
z.emit('false')
end
end
class Numeric
def to_zaml(z)
z.emit(self)
end
end
class Regexp
def to_zaml(z)
z.first_time_only(self) { z.emit("#{zamlized_class_name(Regexp)} #{inspect}") }
end
end
class Exception
def to_zaml(z)
z.emit(zamlized_class_name(Exception))
z.nested {
z.nl("message: ")
message.to_zaml(z)
}
end
#
# Monkey patch for buggy Exception restore in YAML
#
# This makes it work for now but is not very future-proof; if things
# change we'll most likely want to remove this. To mitigate the risks
# as much as possible, we test for the bug before appling the patch.
#
if respond_to? :yaml_new and yaml_new(self, :tag, "message" => "blurp").message != "blurp"
def self.yaml_new( klass, tag, val )
o = YAML.object_maker( klass, {} ).exception(val.delete( 'message'))
val.each_pair do |k,v|
o.instance_variable_set("@#{k}", v)
end
o
end
end
end
class String
ZAML_ESCAPES = %w{\x00 \x01 \x02 \x03 \x04 \x05 \x06 \a \x08 \t \n \v \f \r \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f }
def escaped_for_zaml
gsub( /\x5C/, "\\\\\\" ). # Demi-kludge for Maglev/rubinius; the regexp should be /\\/ but parsetree chokes on that.
gsub( /"/, "\\\"" )
end
def to_zaml(z)
z.first_time_only(self) {
num = '[-+]?(0x)?\d+\.?\d*'
case
when self == ''
self.force_encoding('cp932')
z.emit('""')
when (
(self =~ /\A(true|false|yes|no|on|null|off|#{num}(:#{num})*|!|=|~)$/i) or
(self =~ /\A\n* /) or
(self =~ /[\s:]/) or
(self =~ /^[>|][-+\d]*\s/i) or
(self[-1..-1] =~ /\s/) or
(self =~ /[,\[\]\{\}\r\t]|:\s|\s#/) or
(self =~ /\A([-:?!#&*'"]|<<|%.+:.)/)
)
z.emit("\"#{escaped_for_zaml.force_encoding('cp932')}\"")
when self =~ /\n/
self.force_encoding('cp932')
if self[-1..-1] == "\n" then z.emit('|+') else z.emit('|-') end
z.nested { split("\n",-1).each { |line| z.nl; z.emit(line.chomp("\n")) } }
z.nl
else
self.force_encoding('cp932')
z.emit(self)
end
}
end
end
class Hash
def to_zaml(z)
z.first_time_only(self) {
z.nested {
if empty?
z.emit('{}')
else
each_pair { |k, v|
z.nl
z.prefix_structured_keys('? ') { k.to_zaml(z) }
z.emit(': ')
v.to_zaml(z)
}
end
}
}
end
end
class Array
def to_zaml(z)
z.first_time_only(self) {
z.nested {
if empty?
z.emit('[]')
else
each { |v| z.nl('- '); v.to_zaml(z) }
end
}
}
end
end
class Time
def to_zaml(z)
# 2008-12-06 10:06:51.373758 -07:00
ms = ("%0.6f" % (usec * 1e-6)).sub(/^\d+\./,'')
offset = "%+0.2i:%0.2i" % [utc_offset / 3600, (utc_offset / 60) % 60]
z.emit(self.strftime("%Y-%m-%d %H:%M:%S.#{ms} #{offset}"))
end
end
class Date
def to_zaml(z)
z.emit(strftime('%Y-%m-%d'))
end
end
class Range
def to_zaml(z)
z.first_time_only(self) {
z.emit(zamlized_class_name(Range))
z.nested {
z.nl
z.emit('begin: ')
z.emit(first)
z.nl
z.emit('end: ')
z.emit(last)
z.nl
z.emit('excl: ')
z.emit(exclude_end?)
}
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment