Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - ForeverZer0

1
New Projects / [Ruby] FMOD Low-Level API Wrapper
March 22, 2018, 04:52:32 pm
Introduction
I was in need of a good audio API when working on a C# application, and was unable to find an existing library that fit my needs, so I ended up making my own using the FMOD Low-Level API (the predecessor to the legacy FmodEx). After working with FMOD, and learning the ins-and-outs of how it worked, I saw the potential for making a Ruby port as well. Searching online turned up very little as far as any type of wrapper that could ever be considered close to complete, and only had the basic functionality of FMOD implemented. This including existing RPG Maker scripts that wrapped the basic functionality of the old FmodEx, but nothing more. This prompted me to write my own wrapper.

Being my programming experience begin with Chaos Project and old Ruby 1.8.2, I decided to write in a way that would remain compatible with old versions of Ruby, and foregoing using any Ruby gems or functionality that didn't exist in Ruby 1.8.2, and decided to stick with using Win32API to wrap the FMOD functions, and didn't allow myself the convenience of using a new library like Fiddle of FFI to make things simple.

Currently, the project is approaching the alpha stage. Nearly ALL core functions are implemented, but there has been no in-depth testing yet, and there are still a few mostly uncommon functionalities that need worked on. I have spent A LOT of time making documentation for it, as it is a huge API that definitely has a learning curve. If you don't believe me, see the spoiler.

Spoiler: ShowHide


In the future, I will be releasing this as a gem, but as it stands now, the project can be loaded into RPG Maker with a simple "require", and built upon that. I may even create a basic Audio module rewrite with this codebase in the future, but if anyone else feels like doing it, feel free to check out the code and build upon it for RPG Maker Audio module.



Features

  • Support for 20+ audio formats "out of the box", including all the RPG Maker standards (.mp3, .ogg, .mid, .wma, ect.) with support for more via a plugin system using existing FMOD plugins libraries

  • All basic playback control (play, pause, resume, seek, volume, etc, etc.) that you would expect of any audio player.

  • 20+ built-in effects for sound, including pitch shifting, flange, advanced reverb environments, multi-band parametric equalizer, chorus, tremolo, oscillator,  and many, many more.

  • Streaming of all audio formats, not just MP3

  • Full 3D and 2D support, with distance/directional based sound, each with own reverb environment.

  • Support for custom speaker setups, full surround sound support for up to 12 speakers.

  • Driver selection.

  • Supports music tags (ID3, etc) if that's your thing...

  • Much, much more.



As I stated above, this project is still in its infancy, but already far surpasses any existing audio API that I could find for the Ruby language, and because I love you all, I kept it compatible with RPG Maker XP and above.

This post is meant to a preview for a full release, and I would recommend not to port it as it currently is in a completed project. As so much yet is untested to any passable standard, lacks complete protection against segmentation faults, etc.



Download

Scipts

FMOD DLL (Required)

Documentation

Source - Github



Getting Started

Download the scripts, place unzipped directory in RPG Maker project folder along with the downloaded FMOD dll.
Open script editor, add the line:
require File.expand_path('lib/fmod')


Just to give you idea to get started, snippet to play a sound:
sys = FMOD::System.create
sound = sys.create_sound('My Audio File.mp3')
sys.play_sound(sound)


Things to remember, the documentation, which is posted above, is currently lacking a lot of examples, so feel free to download the help file from FMOD's website, which can still be helpful on how to do things until the examples are created.

The documentation is not compiled, but can simply be unzipped and opening the "index" file within, which will open it in your browser.

FMOD object's need disposed! Once you are done using a sound, system, etc, call dispose or release on it to free the underlying handle. Obviously this behavior would be taken care of automatically when ported to RPG Maker. This is a developer's API, not an end-user product.

Below is generic audio module I wrote real quick just as an example as an example. It is already far more capable than the built-in Audio, but really more intended as just boiler-plate code.
Audio Module: ShowHide
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))

require 'fmod'

# Undefine the old Audio module so there is no interference.
if defined? Audio
  Object.send(:remove_const, :Audio)
end

##
# Complete redefinition of the Audio module with additional capabilities.
#
# @note <b>Notes on Pitch</b>
#
#   By default, pitch is changed by simply altering the speed of the sound. If
#   you wish to accomplish true pitch shifting, use the PitchShifter DSP unit,
#   which alters pitch without changing playback speed.
# @author Eric "ForeverZer0" Freed
module Audio

  ##
  # Only change if you have a project that renames the "Game.exe" and "Game.ini"
  EXE = 'Game'

  ##
  # File extensions that will be included for searching audio paths.
  # For performance reasons, only include extensions that your project uses.
  EXTENSIONS = %w[.mp3 .ogg .mid .wma]

  ##
  # Channel names, as symbols. Removing the default names listed here will break
  # backwards compatibility.
  CHANNELS = [:BGM, :BGS, :ME, :SE].freeze

  include FMOD::Enums
  include FMOD::Effects

  ##
  # Retrieves a hash using the symbols located in {CHANNELS} mapping to each
  # channel handle.
  # @return [Hash<Symbol, FMOD::Channel>] The {FMOD::Channel} instances.
  def self.channels
    @channels
  end

  ##
  # Begins playback on the specified channel.
  #
  # File extensions may be omitted. All paths should be relative to the base
  # project directory. RTP directories will also be searched if file is not
  # found locally.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @param looping [Boolean] +true+ to have sound loop; otherwise +false+.
  # @param effects [FMOD::DSP] Optional DSP effects to apply to the sound. This
  #   is more efficient than adding later.
  # @return [FMOD::Channel] The channel instance that is playing the sound.
  def self.play(channel, filename, volume, pitch, looping, *effects)
    if channel_playing?(channel, filename)
      @channels[channel].volume = volume * 0.01
      @channels[channel].pitch = pitch * 0.01
      return
    end
    @sounds[channel].release unless @sounds[channel].nil?
    mode = looping ? MODE[:loop_normal] : MODE[:default]
    @sounds[channel] = @fmod.create_stream(full_path(filename), mode)
    @channels[channel] = @fmod.play_sound(@sounds[channel], nil, true)
    @channels[channel].volume = volume * 0.01
    @channels[channel].pitch = pitch * 0.01
    effects.reverse.each { |dsp| @channels[channel].add_dsp 0, dsp }
    @channels[channel].resume
  end

  ##
  # Creates a DSP unit that can be applied to a channel to alter sound.
  #
  # Optionally applies it to channels if specified.
  # @param type [Integer, Class] Either an integer value specified in the
  #   {DSP_TYPE} enumeration, or a class that is defined in the {FMOD::Effects}
  #   namespace.
  # @param channels [Symbol] Any number of channel names defined in {CHANNELS}
  #   to apply the effect to.
  # @return [FMOD::DSP] The created DSP effect.
  def self.create_effect(type, *channels)
    @effects << @fmod.create_dsp(type)
    channels.each { |channel| add_effect(channel, @effects.last) }
    @effects.last
  end

  ##
  # Adds an existing DSP effect to a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param effect [FMOD::DSP] AN existing DSP effect.
  # @return [void]
  def self.add_effect(channel, effect)
    return unless channel_valid?(channel) && effect.is_a?(FMOD::DSP)
    @channels[channel].add_dsp(0, effect)
  end

  ##
  # Removes an existing DSP effect from a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param effect [FMOD::DSP, Integer] Either the DSP effect instance to remove,
  #   or the index into the channel's DSP chain to remove.
  # @return [void]
  def self.remove_effect(channel, effect)
    return unless channel_valid?(channel)
    @channels[channel].remove_dsp(effect)
  end

  ##
  # Retrieves the loaded effect on the channel at the given index.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param index [Integer] Index into the channel's DSP chain to retrieve the
  #   desired effect.
  # @return [FMOD::DSP, nil] The desired DSP effect, or +nil+ if channel or
  #   index was invalid.
  def self.effect(channel, index)
    return unless channel_valid?(channel)
    @channels[channel][index]
  end

  ##
  # Returns an array of created DSP effects.
  # @param channel [Symbol] If specified, returns only effects currently applied
  #   to the channel.
  # @return [Array<FMOD::DSP>] Array of all effects created by the user.
  def self.effects(channel = nil)
    return @effects if channel.nil?
    return unless channel_valid?(channel)
    @channels[channel].dsps
  end

  ##
  # Retrieves the current volume of a sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Integer] The current volume, a value typically between 0 and 100.
  def self.volume(channel)
    channel_valid?(channel) ? (@channels[channel].volume * 100).to_i : 0
  end

  ##
  # Changes the volume of a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param volume [Integer] The volume to set, typically a value between 0 and
  #   100, though amplification is supported.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.change_volume(channel, volume)
    return unless channel_valid?(channel)
    @channels[channel].volume  = volume * 0.01
    @channels[channel]
  end

  ##
  # Retrieves the current pitch of a sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Integer] The current pitch, a value between 50 and 200.
  def self.pitch(channel)
    channel_valid?(channel) ? (@channels[channel].pitch * 100).to_i : 100
  end

  ##
  # Changes the pitch of a channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param pitch [Integer] The pitch to set, a value between 50 (one octave
  #   down) and 200 (one octave up).
  # @note Out of range values will be clamped.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.change_pitch(channel, pitch)
    return unless channel_valid?(channel)
    @channels[channel].pitch = pitch * 0.01
    @channels[channel]
  end

  ##
  # Retrieves the current playback position of the sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Integer] The current position, in milliseconds.
  def self.position(channel)
    channel_valid?(channel) ? @channels[channel].position : 0
  end

  ##
  # Moves the playback position to the specified location in the sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param ms [Integer] The playback position, in milliseconds, to set.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.seek(channel, ms)
    return unless channel_valid?(channel)
    @channels[channel].seek(ms)
  end

  ##
  # Stops playback on the specified channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.stop(channel)
    return unless channel_valid?(channel)
    @channels[channel].stop
  end

  ##
  # Pauses playback on the specified channel.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.pause(channel)
    return unless channel_valid?(channel)
    @channels[channel].pause
  end

  ##
  # Resumes a previously paused channel, or does nothing if channel is invalid
  # or not paused.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Channel, nil] The channel object if channel was valid; otherwise
  #   +nil+.
  def self.resume(channel)
    return unless channel_valid?(channel)
    @channels[channel].resume
  end

  ##
  # This is same as "time-stretching", changing the speed of playback without
  # effecting the pitch of the sound.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param speed [Float] A values between 0.5 (half speed) and 2.0 (double
  #   speed).
  # @return [void]
  def self.tempo(channel, speed)
    return unless channel_valid?(channel)
    speed = [0.5, speed, 2.0].sort[1]
    pitch_shifter = @channels[channel].dsps.find { |dsp| dsp.type == 13 }
    if pitch_shifter.nil?
      pitch_shifter = create_effect(13, channel)
      pitch_shifter.fft_size = 4096
    end
    @channels[channel].pitch = speed
    pitch_shifter.pitch = 1.0 / speed
  end

  ##
  # Changes the playback speed of a sound. Altering the speed also changed the
  # pitch.
  #
  # Negative values are accepted to play a sound backwards.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param hz [Numeric] The frequency, in Hz, to play the channel at. Default
  #   speed is +44100+ Hz, so +-44100+ is normal speed backwards, +22050+ is
  #   half speed, +88200+ is double, etc.
  def self.frequency(channel, hz)
    return unless channel_valid?(channel)
    @channels[channel].frequency = hz
  end

  ##
  # Transitions volume on the specified channel to the a target volume of the
  # given number of milliseconds.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param time [Integer] Length of time in milliseconds the transition will
  #   occur over.
  # @param target_volume [Integer] The target volume to transition to, typically
  #   a value between 0 and 100, though amplification is supported.
  # @note When volume is transitioned to +0+, the channel will be stopped when
  #   after reaching that volume.
  def self.fade(channel, time, target_volume = 0)
    return unless channel_valid?(channel)
    volume = @channels[channel].volume - (target_volume * 0.01)
    frames = time / Graphics.frame_rate
    increment = volume / frames
    @fade_points.delete_if { |fade| fade[0] == channel }
    @fade_points.push [channel, frames, increment, target_volume]
  end

  ##
  # Checks if the specified channel is still valid and references an existing
  # FMOD Channel. FMOD reuses channels automatically and can "steal" them for
  # higher priority sounds when necessary, or more commonly after a sound has
  # completed playing and the channel is stopped.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @return [Boolean] +true+ if channel is still valid; otherwise +false+.
  def self.channel_valid?(channel)
    return false if @channels[channel].nil?
    @channels[channel].valid?
  end

  ##
  # Checks if the given channel is currently playing. If a filename is
  # specified, additionally checks if that is the file being played.
  # @param channel [Symbol] The name of the channel being played, a value
  #   defined in the {CHANNELS} array.
  # @param filename [String, nil] A filename to check if is playing on the
  #   channel, or +nil+ to ignore.
  # @return [Boolean] Returns +true+ if the channel is playing, and additionally
  #   if the filename is playing if specified; otherwise +false+.
  def self.channel_playing?(channel, filename = nil)
    return false unless channel_valid?(channel)
    if @channels[channel].playing?
      name = @channels[channel].sound.name
      return File.basename(name, File.extname(name)) == File.basename(filename)
    end
    false
  end

  ##
  # Initializes the Audio module.
  # @api private
  # @return [void]
  def self.initialize
    return if defined? @fmod
    @fmod = FMOD::System.create
    build_paths
    @found = {}
    @sounds = {}
    @channels = {}
    @effects = []
    @fade_points = []
  end

  ##
  # Updates the FMOD system object and fading.
  # @note This is called automatically each frame with each Graphics update,
  #   there is no need to call this method manually.
  # @api private
  # @return [void]
  def self.update
    @fmod.update
    @fade_points.delete_if do |fade|
      if channel_valid?(fade[0])
        @channels[fade[0]].volume -= fade[2]
        fade[1] -= 1
        if fade[1] <= 0
          @channels[fade[0]].stop if fade[3] == 0
          return true
        end
        return false
      end
      true
    end
  end

  ##
  # Builds the search paths that will be used for searching audio files,
  # including RTP paths defined in the projects .ini file.
  # @return [void]
  # @api private
  def self.build_paths
    ini = Win32API.new('kernel32','GetPrivateProfileString','pppplp','l')
    ini.call(EXE, 'Library', '', lib = "\0" * 256, 256, "./#{EXE}.ini")
    lib.delete!("\0")
    rtp_path = Win32API.new(lib, 'RGSSGetRTPPath', 'l', 'l')
    path_with_rtp = Win32API.new(lib, 'RGSSGetPathWithRTP', 'l', 'p')
    @search_paths = [Dir.getwd]
    (1..3).each do |i|
      id = rtp_path.call(i)
      rtp = path_with_rtp.call(id)
      next if rtp == ''
      @search_paths << rtp.gsub('\\', '/')
    end
  end

  ##
  # Returns the full path from a relative path. File extension may be omitted,
  # in which case any matching file that has an extension in the {EXTENSIONS}
  # setting will be retrieved.
  #
  # Searches locally first, and RTP directories if not found.
  # @param relative_path [String] A filename relative to the base directory.
  # @api private
  # @return [String] The full path to the file, including found extension.
  # @raise [Errno::ENOENT] Raised when a matching file cannot be found.
  # @note Once found, filenames are cached to reduce overhead on repeated calls.
  def self.full_path(relative_path)
    key = relative_path.to_sym
    if @found.has_key?(key)
      return @found[key]
    end
    catch :found do
      @search_paths.each do |dir|
        if File.extname(relative_path) != ''
          filename = File.join(dir, relative_path)
          next unless FileTest.exists?(filename)
          @found[key] = filename
          break
        else
          EXTENSIONS.each do |ext|
            filename = File.join(dir, relative_path + ext)
            next unless FileTest.exists?(filename)
            @found[key] = filename
            throw :found
          end
        end
      end
    end
    # noinspection RubyResolve
    raise Errno::ENOENT, relative_path unless @found.has_key?(key)
    @found[key]
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts BGM playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.bgm_play(filename, volume = 100, pitch = 100)
    play(:BGM, filename, volume, pitch, true)
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts BGS playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.bgs_play(filename, volume = 100, pitch = 100)
    play(:BGS, filename, volume, pitch, true)
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts ME playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.me_play(filename, volume = 100, pitch = 100)
    play(:ME, filename, volume, pitch, false)
  end

  ##
  # @deprecated Use {Audio.play}. Backwards compatibility for existing Audio.
  # Starts SE playback. Sets the file name, volume, and pitch in turn.
  #
  # Also automatically searches files included in RGSS-RTP.
  # @param filename [String] Relative filename to play. File extensions may be
  #   omitted.
  # @param volume [Integer] Volume level. By default this within range of +0+
  #   to +100+, but FMOD supports amplification and higher values.
  # @param pitch [Integer] Pitch level. +50+ to +200+.
  # @return [void]
  def self.se_play(filename, volume = 100, pitch = 100)
    play(:SE, filename, volume, pitch, false)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops BGM playback.
  def self.bgm_stop
    stop(:BGM)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops BGS playback.
  def self.bgs_stop
    stop(:BGS)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops ME playback.
  def self.me_stop
    stop(:ME)
  end

  ##
  # @deprecated Use {Audio.stop}. Backwards compatibility for existing Audio.
  # Stops SE playback.
  def self.se_stop
    stop(:SE)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the BGM volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.bgm_fade(time, target_volume = 0)
    fade(:BGM, time, target_volume)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the BGS volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.bgs_fade(time, target_volume = 0)
    fade(:BGS, time, target_volume)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the ME volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.me_fade(time, target_volume = 0)
    fade(:ME, time, target_volume)
  end

  ##
  # @deprecated Use {Audio.fade}. Backwards compatibility for existing Audio.
  # Transitions the SE volume (up or down) to the target volume over the
  # specified interval.
  # @param time [Integer] The time in milliseconds for the transition to be
  #   applied. The volume will be adjusted on a per-frame basis.
  # @return [void]
  def self.se_fade(time, target_volume = 0)
    fade(:SE, time, target_volume)
  end

  ##
  # Disposes the Audio module, releasing handles to the underlying objects.
  # @return [void]
  # @note It is strongly recommended not to alter or interfere with this call.
  # @api private
  def self.finalize
    @channels.each_value { |channel| channel.stop if channel.valid? }
    @effects.each { |dsp| dsp.release }
    @sounds.each_value { |sound| sound.release }
    @fmod.release
  end

  private_class_method :build_paths
  private_class_method :full_path
  private_class_method :finalize

  initialize
end

module Graphics

  class << self

    ##
    # Original update method for Graphics.
    # @return [void]
    alias update_audio update

    ##
    # Piggybacks the Graphics update of the Audio module. Updating is necessary
    # for fading and some other less common functions.
    # @return [void]
    def update
      Audio.update
      update_audio
    end
  end
end


module Kernel
  alias finalize_audio exit
  def exit(*args)
    begin
      Audio.send(:finalize)
    ensure
      finalize_audio(*args)
    end
  end
end








6
News / Suggestions / Feedback / Forum Error?
November 26, 2015, 02:30:40 pm
Am I the only one who is getting this?
I see this on 3 different devices at the top of every page...

Spoiler: ShowHide
7
So I was thinking at trying my hand at developing for Android, but I don't even know where I will find the time to do so.
Anyways, I had an idea of either myself, or someone more experienced, developing an Android app for CP, which could be better than than simply using a browser.

Any thoughts?
8
General Discussion / RTP Paths in the Registry (RMXP)
August 15, 2014, 11:07:08 pm
Are the RTP paths stored in the same location in the registry on all machines and versions of Windows?

On my machine, Win7 x64, I installed RMXP in compatibility mode for Windows XP SP3, and the RTP paths can be found at:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Enterbrain\RGSS\RTP


I was just wondering if there was different setups that could cause this to be different, such as whether the machine is 32 or 64 bit, the operating system (XP, Vista, Seven, Eight), and if installing in compatibility mode can change it.

I seem to remember that 32-bit machines are different, but I don't have a VM created at the moment, so I am not sure.
9
I assumed this was relatively simple, but as it turned out, its not. Now I ask that before you reply, I fully understand what is says in the manual, and I am not some noob who needs the basics explained to me, I am referring to rendering the textures on the device. I am also fully aware that the more recent object gets drawn first if they are equal.

I am running into a few minor issues with Z coordinates for this XNA/IronRuby engine I am making. The only place I really see this problem is with the default battle system. Here is what it appears as:

Spoiler: ShowHide


As you can see, everything appears fine except for the enemy being placed above the @actor_command_window.

I have tried a couple different ways of factoring sprite.z and viewport.z, by simply adding them together as one way. I was incorrect in this assumption, this is the way that I have been calculating, but have run into problems here, as the screenshot demonstrates. The enemy sprites have a Z of 304 on their own viewport that has a Z of 0. The @actor_command_window has a Z of 100 (inherited from Window_Base) without a viewport, so 0 would be added to it to get it's final depth. That makes a final tally of the enemy sprites having a Z of 304, and the @actor_command_window having a Z of 100. This is the reason the enemy sprites are on top, so simply adding them is not the correct answer.


I assumed next that drawing was ordered by the viewports, and the sprite's z only pertained to that particular viewport. For example, if there are two sprites, sprite A with z of 10, and sprite B with z of 0, but sprite B's viewport.z was higher than sprite A's viewport.z, sprite B would still appear on top. I was incorrect in this assumption as well, which can be demonstrated with this little snippet in RMXP.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 50
sprite_A = Sprite.new(viewport)
sprite_A.z = 0
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

If that logic was correct, the second sprite should be under the first, since it does not have a viewport, and the first sprite's viewport has a Z of 50.

So I began testing how they relate within RMXP, and I am just getting more confused.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 50
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

No matter which order the sprites are created in, this causes the second sprite to always be on top, even though it does not have a viewport,  and the first one does with a Z of 50, and both sprite's have the same Z value.

Now, lets change the first sprites viewport to 100, and change nothing else.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 100
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100

The second sprite appears on top, but only because it was created more recently, as you can test by changing the creation order. This leads me to believe that the viewport's Z doesn't do a damn thing, since it appears it wasn't even factored in to the drawing, and only the order the sprites were created in.

So let's test this theory, I will increase the viewport's Z very high, and lower the sprite's Z.
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 1000
sprite_A = Sprite.new(viewport)
sprite_A.z = 50
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100


Oh wait, nope, that's not it, that makes the first sprite, with a lower Z, higher than the second sprite, simply because it's viewport was higher. But that didn't work earlier when we tried it with this snippet:
Spoiler: ShowHide
viewport = Viewport.new(0, 0, 640, 480)
viewport.z = 100
sprite_A = Sprite.new(viewport)
sprite_A.z = 100
sprite_A.bitmap = RPG::Cache.character('001-Fighter01', 0)

sprite_B = Sprite.new
sprite_B.bitmap = RPG::Cache.character('002-Fighter02', 0)
sprite_B.z = 100


So what the fuck? Does it simply take the higher of the two between the a sprite's viewport's Z, and it's own Z, then use that?  Or is there some magical, undocumented Z value added when a sprite does not have a viewport?

I don't know, maybe I am just getting burned out and need to take a break so I can step back and see this more clearly, but this has been my issue for the all of yesterday and today, and I have not gotten anywhere on it.
10
Chat / Goodbye, Opera (and μTorrent, too!)
August 12, 2014, 06:32:04 am
For the past 4-5 years, I have been a loyal user of Opera (technically Opera Next...), but that time has come to an end. For the past year or so, the direction Opera has taken in development has made it too far removed from the reason I loved it the first place. Gone are all the great features that I loved.

It seems like since they made the switch to Chromium, things have turned to the worst. I don't blame the engine, it is is superior in my opinion, and the switch did drastically help with Opera's biggest fallback at the time, which was compatibility. But around the same time, they begin to turn it into a browser for "the common people", and begin removing all the great features that allowed me to tweak it to just how I liked. The "settings" have turned into an extremely basic couple of options that really don't allow you to customize anything (Internet Explorer has more options...). I was thinking the other day, what exactly does Opera have that makes it better than the competition, and sadly, my answer was "nothing".  The few things that set it apart, such as Speed Dial, are commonplace in all the other browsers now, or a simple add-on which supports even more customization to the look and feel of it than Opera ever offered.

So, with a heavy heart, I have made my switch to Chrome as my daily driver. I have already found a Speed Dial extension that suits  my needs, and I have found a great deal of other extensions that have already improved my browsing experience beyond what Opera has done in a long time. I have not noticed any difference in speed, both are top-performers (both use the same rendering engine), and I have found a lot more support and interest in Chrome.

On another note, I have also since abandoned μTorrent.  It used to be the best torrent client out there, without anything else even coming close in my opinion, but now it is turning into nothing but ad-ware. Since version 2.2.1 (it is now 3.4.2), I don't think anything really positive has been added to it, just different ways to annoy users with ads.  Sure, I can go into the "advanced settings" and with the help of Google manage to turn off the right things to make the ads go away, but its the very principle of it all, and the direction the company has taken that turns me off to it.  If anyone else feels the same way, I have found another great client over at SourceForge called qBittorrent. It is minimalistic, and aims at reproducing the good old μTorrent without all the extra crap. I have found that it performs equally well, has all the same options that I like, comes with no ads, and is even open-source.

In the end, I am happy with the new software I am using, but I just find it sad the direction developers have taken with their products, and turned them from masterpieces, to complete crap that alienates the users that their product targets. No longer will I remain supporting software when this is the direction taken. I would rather give my support to developers who strive for satisfaction of their target audience, and not how much crap can they cram into it to annoy people enough to purchase the "premium" edition.
11
Sea of Code / [XNA 4.0] WTF is going on here?
August 10, 2014, 10:27:03 am
As most everyone knows by now, I am creating a new engine using XNA.
I am getting a very strange bug that I cannot figure out to save my life, and it is really starting to irritate me. As I mentioned in the Shoutbox a few days ago, I was having problems loading very large tilesets. As it turned out, XNA has some a maximum texture size of 2048x2048 using the Reach profile, and a maximum texture size of 4096x4096 on HiDef profile.

I changed the profile to HiDef (goodbye Windows Phone compatibility!), which vastly increased the size of the textures I can use. As far as RMXP is concerned, the largest images that are going to be used are going to be tilesets. Some of these tilesets have a height larger than 4096 pixels obviously, so in order for them to be used, I have to modify the image internally when it loads to fit within the 4096x4096 bounds. For testing purposes, I have been using Inquisitor's Medieval Exterior tileset, since it is one that I know off the top of my head to be one of the largest.

All these images are fairly large, so be warned.

Original Image (256 x 16704): ShowHide


Now, here it is loaded internally. Basically the width just gets expanded and overflow greater than 4096 pixels gets moved to columns. This is the actual texture as it is loaded and used to draw a tilemap.

Internal Image (1280 x 4096): ShowHide


Pretty straightforward conversion, and easy to work with, just have to do a quick calculation to adjust coordinates for over-sized source images.
This works perfectly with the exception of a bug that I cannot find or understand. For some stupid reason, a white border is being placed around some of the tiles, specifically the bottom of the last column (where actual image data is contained, not the extreme bottom), and the right side of some tiles in the next to last column, where beside it is blank data.

Example 1
The damn tents along the right side of the next to last column.
What it is doing: ShowHide

Source: ShowHide


As you can see, there are no white pixels there to be copied. I checked the coordinates, they are correct, its just adding white there. I have tried clearing the GraphicsDevice with all sorts of colors, no change. I have tried changing the blend color, and all I get is white with a tint of whatever color I choose, not just the color, so it's reading that portion of the texture as white.

Example 2
Basically the same thing, just a different area of the tileset, this time at the very bottom. I added the "signature" section of the tileset to a map, and here is how it comes out.
What it is doing: ShowHide

Source: ShowHide


I am at a loss for this one, and thus far, it seems to ONLY do it with them specific areas, the rest of the map looks perfect.
Any ideas?
12
So remember a few years ago when I mentioned I wanted to build a new and improved game engine that holds compatibility with existing RMXP projects, using XNA and IronRuby? I dubbed it the "Zer0 Division Engine", and it was the inspiration that started ARC? In case you forgot.

Well, while working on Rpg.NET, I was still getting that feeling that all the shortfalls of RMXP were holding me back, so during the past few days, I have had some free time, and I decided to take a quick try at seeing what it would take to create my original idea, before it became ARC and changed to something completely different.

I just wanted to let you all know I have it running. There is still loads of things to do, but I have successfully re-created RGSS, and have a game running smoothly on the screen, with rewritten Graphics, Audio, and Input. The tilemap rewrite is superb, can can run any resolution smoothly, without any of the priority issues I ran into in my Ruby rewrite, and the Ruby interpreter puts RMXP's to shame.

I still have lots to do before I start posting anything up, namely font rendering looks like garbage at the moment, and I need to create a custom build of IronRuby to remove the "superclass mismatch" error we ran into with ARC, which was something added into later versions of Ruby.

Once I get this running stable, the sky is the limit with what can be done.
13
I haven't run any benchmarks or anything yet, so I am kind of just asking this out of my ass, but here goes.
I am working on the Input module for Rpg.NET, so keep in mind this is not being performed in Ruby, so there are some things that can be done that are not possible with a script. Here is my debate, which of these two directions to take it:

Direction 1 : GetKeyboardState
This was my original thought. Every time Input.update is called, invoke GetKeyboardState, enumerate the buffer the key states were copied to, and internally calculate and set flags in arrays to store which keys are triggered, released, repeated, etc. This is more or less just the same formula as Custom Controls in ToA, but written in C#.

After thinking about it, I really don't care for this approach. It is perfectly acceptable as a Ruby script, since the options there are more limited, but I am not a fan of the the need to enumerate 256 keys every time the update method is called, and perform a bunch of checks on each key to determine what to do about each.

Direction 2 : WindowProc
I already have a WndProc function in place that receives any message posted to the window, including key presses, key releases, mouse buttons, mouse wheel, mouse move, and just about anything else you could imagine that has an effect on the window.

My theory is, instead of checking the state of the keyboard every frame, I can simply update the states of the keys as the messages arrive, and do nothing in the meantime. That means no enumerating 256 every frame, and it has the obvious advantage of being an "all-in-one" method that can allow me to seamlessly integrate mouse input, as well as other anything else I want to implement.


My question is, are there any disadvantages of the second approach over the first?
14
Rpg.NET
Authors: ForeverZer0
Version: 0.28A
Type: Managed RGSS API
Key Term: Scripting Tool



Introduction

Rpg.NET is an API built using the .NET Framework for both improving performance and enhancing the RPG Maker series (XP, VX, and VXA). Contained within are various functions and classes that extend the ability of RPG Maker in areas such as graphics, audio, and Windows API interop. Included is a Ruby script that is the wrapper around the library, so it can be used as any other script within your game.


Features

Graphics

  • Lag-Free Particle Engine

  • Save Bitmap as *.png, *.jpg, *.bmp, *.gif

  • Load Bitmap from *.psd, *.tif

  • Get/Set Bitmap Hue

  • Get/Set Bitmap Saturation

  • Get/Set Bitmap Contrast

  • Get/Set Bitmap Brightness

  • Get/Set Bitmap Gamma

  • Bitmap#draw_text_vertical

  • Bitmap#draw_arc

  • Bitmap#draw_bezier

  • Bitmap#draw_beziers

  • Bitmap#draw_closed_curve

  • Bitmap#draw_curve

  • Bitmap#draw_ellipse

  • Bitmap#draw_line

  • Bitmap#draw_lines

  • Bitmap#draw_polygon

  • Bitmap#draw_pie

  • Bitmap#fill_closed_curve

  • Bitmap#fill_ellipse

  • Bitmap#fill_pie

  • Bitmap#fill_polygon

  • Bitmap#fill_gradient_rect

  • Bitmap#fill_gradient_ellipse

  • Bitmap#fill_gradient_polygon

  • Bitmap#color_mask

  • Bitmap Effect - Invert

  • Bitmap Effect - Pixelate

  • Bitmap Effect - Change RGBA Color Components

  • Bitmap Effect - Soften

  • Bitmap Effect - Gaussian Blur

  • Bitmap Effect - Sharpen (Multiple algorithms)

  • Bitmap Effect - Edge-Detect (Multiple algorithms)

  • Bitmap Effect - Emboss (Multiple algorithms)

  • Bitmap Effect - High Pass

  • Bitmap Effect - Grayscale

  • Bitmap Effect - Poster Effect

  • Bitmap Effect - Bitonal

  • Bitmap Effect - Median Filter

  • Bitmap Effect - Tint

  • Bitmap Effect - Color Balance

  • Bitmap Effect - Solarise



Audio

  • Get duration of audio files, in milleseconds (*.mid not supported)

  • Get duration of audio files, in frames (*.mid not supported)



Utility

  • Get RTP Path(s)

  • Get Full-Path to Resource File

  • Change Window Resolution

  • Show/Hide Output Console

  • Screenshot Window (with or without window frame)




Screenshots

Original Image


Hue Change: ShowHide

Hue: 60


Hue: 120


Hue: 180


Hue: 240


Hue: 300


Saturation Change: ShowHide

Saturation: 0


Saturation: 50


Saturation: 100 (Default)


Saturation: 150


Saturation: 200


Brightness Change: ShowHide

Brightness: 0


Brightness: 25


Brightness: 50


Brightness: 100 (Default)


Brightness: 150


Brightness: 175


Brightness: 200


Contrast Change: ShowHide

Contrast: 1


Contrast: 50


Contrast: 100 (Default)


Contrast: 150


Contrast: 200


Contrast: 250


Contrast: 300


Contrast: 350


Contrast: 400


Gamma Change: ShowHide

Gamma: 1


Gamma: 50


Gamma: 100 (Default)


Gamma: 150


Gamma: 200


Gamma: 250


Gamma: 300


Gamma: 350


Gamma: 400


Bitmap Effect - Bitonal: ShowHide

Converts an image to two colors only. The dark and light colors are specified with a threshold to determine what a "dark" color and what a "light" color are.





Bitmap Effect - Blur: ShowHide

There are numerous types of blur filters included, and a custom filter that allows you to specify the factor applied, but here are a few examples:








Edge Detect: ShowHide

Uses convolution filters to detect edges between pixels. There are numerous types of filters to use, and likely going to be many more added to get different effects, but here are some of the built-in ones, including both colored and grayscaled versions:















Bitmap Effect - Emboss: ShowHide

Uses convolution filters to apply an emboss effect. There are numerous types of filters to use, here are some of the built-in ones, including both colored and grayscaled versions:












Bitmap Effect - Gaussian Blur: ShowHide

I implemented the methods to calculate kernels for Gaussian Blurs, therefore you are not forced to only use built-in blurs, but can customize them for your own images, specifying the kernel size and weight. Some examples:

Kernel Size: 3, Weight 11.0


Kernel Size: 5, Weight 5.5


Kernel Size: 9, Weight 13.0


Kernel Size: 11, Weight 7.0


Bitmap Effect - Median Filter: ShowHide

A Median Filter can be applied to images in order to achieve image smoothing or image noise reduction. The Median Filter in contrast to most image smoothing methods, to a degree exhibits edge preservation properties. Here are some examples, including grayscaled versions:

Matrix Size: 3



Matrix Size: 5



Matrix Size: 7



Bitmap Effect - Pixelate: ShowHide

Allows you to define the pixel size of a Bitmap, removing detail.

Pixel Size: 2


Pixel Size: 4


Pixel Size: 8


Bitmap Effect - Sharpen: ShowHide

There are numerous filters to sharpen images, but here are a few examples of the effect's achieved:








Bitmap Effect - Solarise: ShowHide

Image Solarisation can be described as a form of Image inversion/colour inversion. A distinction can be made  between solarisation and colour inversion when taking into regard threshold values implemented when performing image solarisation.

R:0 G:200 B:32


R:96 G:255 B:255


R:128 G:255 B:192


R:192 G:224 B:64


Bitmap Effect - Tint: ShowHide

Allows you to add a tint to a Bitmap by specifying percentage of each color component to increase.

R:0% G:45% B:15%


R:10% G:20% B:30%


R:32% G:64% B:0%


Bitmap Effect - Color Balance: ShowHide

Applies the color balance to the image, using the specified color.

R:0 G:200 B:32


R:96 G:256 B:256


R:128 G:255 B:192


R:192 G:224 B:64


Bitmap Effect - Color Mask: ShowHide

Gets a mask from the specified color and threshold.

R:0 G:0 B:0 A:0, Threshold: 0


R:228 G:83 B:63 A:255, Threshold: 32


R:228 G:167 B:16 A:255, Threshold: 48


Bitmap Effect - High Pass: ShowHide

Runs a high-pass filter across the Bitmap. Here are examples of both normal and grayscale:



Bitmap Effect - Grayscale: ShowHide



Bitmap Effect - Invert: ShowHide



Bitmap Effect - Poster: ShowHide



Bitmap Effect - Sepia: ShowHide



Bitmap Effect - Soften: ShowHide





Demo


Rpg.NET Developer Release Demo (v.0.25)


Script

Inside demo. (v.0.25)

Version 0.28A
Script
Rpg.NET.dll (701 kB)



Instructions

As this is not currently a stable release build, I would not suggest using it in a release game at this current time. Although everything within at this time will remain in it, and you can begin to utilize what it has in your game, there are numerous improvements that need to be done,


Compatibility

Should work with RPG Maker XP, VX, and VXA, although testing has only been done with RPG Maker XP. Before final release, all versions will be tested to ensure compatibility.

Requires Microsoft .NET Framework 3.5. This is standard on Windows 7 and higher operating systems, and USUALLY installed on Windows XP and Vista systems if updated normally. If distributing with your game, I would suggest providing the above link just in case.


Credits and Thanks


  • ForeverZer0, for the library and script

  • All those on Chaos-Project who have offered ideas on functions to implement, and those who will continue to do so




Author's Notes

There are still quite a few things to implement, I wouldn't even consider this library 1/4 finished at the current time, so please keep that in mind. I have many planned features that have been suggested yet to implement, as well as many others not yet mentioned, such as Input and Audio extensions, Tilemap rewrites to support greater resolutions, and additional Graphic functions. I would appreciate if you have any ideas, to please share them, I am compiling a list, and slowly working through them, this release is simply to show how progress is coming, and to inspire new ideas.

If you encounter any bugs and/or crashes, please report them here in this thread so that they can be addressed. As I stated earlier, testing is currently only being performed on RPG Maker XP, so there may be errors with VX and VXA that I am currently unaware of.

I would like to hear about your experience as far as performance goes with the graphical functions. I have done my best to maintain the highest degree of performance with Bitmap manipulation, using only pointers to unmanaged memory wherever possible. As of right now, the only functions that do not implement this are the hue, saturation, contrast, brightness, and gamma methods, they use a color matrix, but I am in the midst of re-writing them functions to use unmanaged methods, and compare benchmarks.

The particle generators are rather lame at the moment, and only three very basic types currently exist. I was working more on just getting the underlying engine to work and perform efficiently than develop nice effects, but expect more to come in that area in future releases.
15
As I personally prefer to develop using C# and targeting the .NET Framework, I was wondering about other opinions. Usually I simply just target the minimum framework that makes sense for my project, and don't worry too much about it, since I am developing for the end-user, and my personal belief is that it's not a big deal to have someone update their system to a modern framework. Now though, I am developing for an API that can be used for other developers, and will require to be distributed with their projects, so I feel like it's important to get the view of others.

In the past few years, the .NET Framework, or more specifically, the development for it have drastically increased. A typical Windows XP machine will have version 2.0 installed on it, so that's basically the minimum version used in active development. That's not to say that it will be the highest version on an XP machine, only that it is what will be on it by default, if the user has decided not to keep up on their updates. Windows Vista comes with version 3.0, and Windows 7 comes with version 3.5.

.NET Framework Version 4.0 is not installed on any machine by default, but it is a common update. This is my personal version of choice, as it has a lot of nice added features to make development easier, and is not uncommon on any machine. Version 4.5 came out with Windows 8, but I doubt I will be targeting it any time soon, as it is pretty much only common on Windows 8 PC's, which yeah, we all know by now the story on that...

More or less, I am just curious. I would hate to stick to only using version 2.0 to cater to the die-hard XP users who refuse to install updates, and not be able to develop how I want, but I don't want to develop an API that other developers won't use because it MIGHT make the end-user install an update first.

If you could share your thoughts, it would be greatly appreciated and help guide my development.
16
RPG Maker Scripts / [WIP] Rpg.NET
July 14, 2014, 05:25:55 pm
    With the discover of how to export methods from managed .NET assemblies, and now the discover of how to manipulate RGSS Bitmaps from within a managed .NET assembly, I can now do all sorts of things that I have only dreamed of before, using my favorite programming language: C#.

    For the past few days, I have been working on a library to use in conjunction with script that can add all sorts of various improvements and enhancements to RPG Maker. So far I have implemented a few general "utility" methods, and have been focusing a lot on extending the Bitmap class.

    So far, this is what I have:

Utility: ShowHide

  • Setting the resolution

  • Getting the handle to the game window

  • Getting the game library

  • Getting the game title

  • Getting the RTP name(s) and path(s)

  • Toggling the console on/off to display output

  • Opening a script window to place script calls while the game is running. (Better than the script I wrote long ago...)

  • Getting list of installed fonts on the system

  • Using the system font picker to change the game font



Bitmap Class: ShowHide

  • Added methods to get/set the saturation, brightness, contrast, and gamma

  • Save a bitmap as *.jpg, *.png, *.gif, *.tiff, *.bmp

  • Implemented draw_arc function

  • Implemented draw_bezier function

  • Implemented draw_curve function

  • Implemented draw_ellipse function

  • Implemented draw_line function

  • Implemented draw_pie function

  • Implemented draw_closed_curve function

  • Implemented draw_polygon function

  • Implemented fill_pie function

  • Implemented fill_ellipse function

  • Implemented fill_polygon function

  • Implemented fill_closed_curve function

  • Additional image formats (*.tif, *.gif, *.psd, etc.)

  • A "snapshot" function that returns a copy of the current client area of the game as a Bitmap

  • Reading PhotoShop's *.psd format and creating Bitmap's from it



I have found some very efficient ways of altering RGSS Bitmaps, and I am quite pleased with the performance thus far. I have not really encountered any type of lag or performance loss while testing yet, and I look forward to seeing how much performance I can get from it.  I have devised a way of copying the RGSS Bitmap data to .NET Bitmaps through a combination of using Window's "MoveMemory" function and the .NET Bitmap's "LockBits" to just shift around memory without any actual casting to types, etc. That combined with all that is available in .NET's Graphics class and the use of a ColorMatrix, there are some neat things that can be done that Ruby cannot perform efficiently.



What I was hoping to get from this thread was some feedback and ideas on other things that could be implemented. I already have a few ideas that I already have plans for, or have already begin implementing, which I will list here, but please feel free to share your thoughts.

Planned or Partially Implemented: ShowHide


[li[Anti-aliasing[/li]
[li]Tilemap rewrite to finally make a good custom resolution system without the performance loss and graphical errors[/li]
[li]Animated color fills (invert, rainbow, etc.)[/li]
[li]HUD (because it updates every frame performance can be an issue in Ruby)[/li]
[li]Tilemap effects (zoom, hue change, etc.)[/li]
[li]Mouse-wheel functions. (Actually already partially implemented a new Input API)[/li]
[li]Change window icon[/li]
[li]lighting / shadows[/li]
[li]Make some suggestions to see them here![/li]
[/list]


Possible: ShowHide


  • particle effects

  • outlines or tooltips (think quest icons from MMORPGs, teammate outlines from Left4Dead, etc.)

  • Networking


[/list]
17
Prevent Window Deactivation
Authors: ForeverZer0
Version: 1.1
Type: Game Utility
Key Term: Game Utility



Introduction

Normally, when the RGSS Player window is no longer the foreground window, the game becomes "paused", and only the music continues while the game is suspended. This small library will prevent that action, and the game will not be suspended.


Features


  • Compatible with ALL RPG Maker version

  • Very small (only 10.8 kB)

  • Very efficient, does not constantly check every millisecond, simply filters Windows message events being processed on the window

  • Two lines of actual Ruby code




Screenshots

None.


Demo

None.


Script

Download NoDeactivate.dll and place in your game folder.

The following two lines of code will need added to main:
Win32API.new('NoDeactivate.dll', 'BeginMonitor', '', '').call

Win32API.new('NoDeactivate.dll', 'EndMonitor', '', '').call

RMXP: ShowHide
#==============================================================================
# ** Main
#------------------------------------------------------------------------------
#  After defining each class, actual processing begins here.
#==============================================================================

begin
 Win32API.new('NoDeactivate.dll', 'BeginMonitor', '', '').call
 # Prepare for transition
 Graphics.freeze
 # Make scene object (title screen)
 $scene = Scene_Title.new
 # Call main method as long as $scene is effective
 while $scene != nil
   $scene.main
 end
 # Fade out
 Graphics.transition(20)
rescue Errno::ENOENT
 # Supplement Errno::ENOENT exception
 # If unable to open file, display message and end
 filename = $!.message.sub("No such file or directory - ", "")
 print("Unable to find file #{filename}.")
ensure
 Win32API.new('NoDeactivate.dll', 'EndMonitor', '', '').call
end

RMVX: ShowHide
#==============================================================================
# ** Main
#------------------------------------------------------------------------------
#  After defining each class, actual processing begins here.
#==============================================================================

begin
 Win32API.new('NoDeactivate.dll', 'BeginMonitor', '', '').call
 Graphics.freeze
 $scene = Scene_Title.new
 $scene.main while $scene != nil
 Graphics.transition(30)
rescue Errno::ENOENT
 filename = $!.message.sub("No such file or directory - ", "")
 print("Unable to find file #{filename}.")
ensure
 Win32API.new('NoDeactivate.dll', 'EndMonitor', '', '').call
end

RMVX Ace: ShowHide
#==============================================================================
# ** Main
#------------------------------------------------------------------------------
#  This processing is executed after module and class definition is finished.
#==============================================================================

Win32API.new('NoDeactivate.dll', 'BeginMonitor', '', '').call

rgss_main { SceneManager.run }

Win32API.new('NoDeactivate.dll', 'EndMonitor', '', '').call




Instructions

See above.


Compatibility

Should now be compatible with ALL version of RGSS, including RMXP, RMVX, and RMVX Ace.
If you are having difficulties getting it to function correctly, first try to copy the library defined in Game.ini to the path defined there.

Although the likelihood is near nothing, scripts that completely change the Input module COULD have an effect, though I am not aware of any existing Input module rewrite that does.


Credits and Thanks


  • ForeverZer0, for the "script" and library.




Author's Notes

Please report any bugs or suggestions.

Source code is available at SourceForge.
In order to post-process the library after build, you will need to recompile the assembly with the Rpg.NET Export Tool I had previously made.
18
Development Tools / [C#] Move-Route Recorder
June 21, 2014, 01:36:40 pm
Move-Route Recorder (XP, VX, VXA)
Author: ForeverZer0
Version: 1.0
Type: Move-Route Tool



Introduction

This is a simple tool for easily creating large event move routes without the need to keep track of the current location in your head. It records the cursor movement as you move the selection rectangle on the map editor screen, and can then playback the movement into an event's move route list.


Features


  • Works with RPG Maker XP, VX, and VXA

  • Simple GUI and user-friendly

  • Very lightweight

  • Portable, no installation required




Screenshots/Video

Spoiler: ShowHide


Spoiler: ShowHide



Download

MoveRouteRecorder.exe (194 kB)

Source code available at SourceForge.



Credits and Thanks


  • ForeverZer0, for the application




Author's Notes

Please report any suggestions or bugs that you may find, thanks for using!
19
Development Tools / [C#] Rpg.NET Export Tool
May 18, 2014, 10:17:54 pm
Rpg.NET Export Tool
Authors: ForeverZer0
Version: 1.0
Type: .NET Post-Built Tool



Introduction

This application is designed to allow you to create Microsoft .NET assemblies that can interop with RGSS using Ruby's Win32API. Assemblies built that target the .NET Framework are managed code, and are unable to be invoked using Ruby. This application performs a post-build process on your existing .NET projects that allows them to be invoked with Win32API, just like that of an unmanaged C/C++ library. Since many on this forum are C# (and other .NET Framework languages) programmers, this should hopefully open up many new possibilities for us that have since moved on from RGSS due to it's simplicity, but still have an active interest in it.



Features


  • Extremely simple to create managed .NET assemblies that can interop with Ruby

  • Easily modify and use existing projects

  • Only need to add a single method attribute to functions that you want to export





Download

.NET Framework v.4.0
Rpg.NET Export.zip (116 KB)

Requires Microsoft .NET Framework 4.0

.NET Framework v.2.0
Rpg.NET Export.zip (116 KB)

This version of the framework is already pre-installed on Windows XP SP3 and above.

Contains everything needed to built projects with exported functions.

  • RpgExport.exe
    Main tool that performs the compilation

  • RpgExport.exe.config
    Configuration file for the RpgExport tool. Can be modified manually, but not required

  • RpgExportAttribute.dll
    Library that needs to be referenced by your project, and contains the method attribute. The dependency will be removed during the post-build process, so this file will NOT need to be deployed with your project



Source Code
Source Forge



Instructions

Step 1
Download the above file, and unzip to anywhere on your computer.

Step 2
Create a new project, targeting whichever framework you prefer. Remember that pretty much every Window's computer out there will have .NET Framework installed already on it, but higher versions such as 3.5 and 4.0 may not be, and could possibly require the end-user to install before your assembly will work on it.

After creating your project, make a reference to RpgExportAttribute.dll.

Spoiler: ShowHide


Step 3
Now create your code as you please. The only important factor to implement is that the methods must be marked as "static", and they need decorated with the "RpgExport" attribute that is imported from the RpgExportAttribute.dll. The only required argument with the attribute is the name that it will be as exported as, which can be different than the actual method name. Following is a rather simple example:

Spoiler: ShowHide


The name assigned within the "RpgExport" attribute is the name that will be invoked with Ruby's Win32API class.

Step 4
Now build your project, and open up the RpgExport.exe tool.
Either drag-drop the created assembly onto the form, type in the full path to the file, or use the dialog to set the assembly as the input assembly.

Spoiler: ShowHide


Here is a quick explanation of the parameters that can be set in the application:

  • IL Assembler Location
    This is the location of "ilasm.exe", selected from list of versions found on the system. I personally have not found any difference in which version is used, and have always just used the highest version available.

  • IL Disassembler Location
    This is the location of "ildasm.exe", selected from list of versions found on the system. Again, I have not found any difference in which is used.

  • Input Assembly
    The input assembly that will be recompiled

  • Output Assembly
    This is the output path of the assembly. If blank, the input assembly will simply be overwritten.

  • Architecture
    This is the target CPU architecture the assembly will target. Typically "Any CPU" will work just fine.

  • Optimize
    This is the same as checking "Optimize" in the within the Build Properties of the project, and is the default of any "Release" build. There really isn't any reason this should not be used.



Step 5
After making any changes you need, click "Export" and the project will be rebuilt. Copy this output assembly to your RPG Maker project's game folder, and you are ready for a test.

Spoiler: ShowHide


Step 6
Within RPG Maker, create the necessary Win32API object, and invoke your new method!

Spoiler: ShowHide


Spoiler: ShowHide




Tips and Tricks

I will add  more to this section later, and explain more on the few things that follow just to get you started.

Invoke Ruby Code From .NET Assembly

Include "System.Runtime.InteropServices" in a class, and use P/Invoke to exectute Ruby code. The only little hiccup I have run in to so far is that ensure any class that implements importing unmanaged DLLs is marked as "internal", and not the within a class you are exporting functions from. To see what I mean, look at the following two pictures.

Spoiler: ShowHide


Spoiler: ShowHide


Win32API.new('RpgNet.dll', 'SayHello', 'p', '').call('ForeverZer0')


Encrypting/Compressing Scripts

Yup, this is actually quite simple, a very good encryption, but does require the use of one other post-built tool. I will explain more tomorrow within the next day or so.



Notes

Feel free to report any bugs, issues, or comments, I will try to answer anything that I can.
Please give credit if using in your project!
20
Development Tools / [C#] MouseBot
March 22, 2014, 05:00:15 pm
MouseBot
Authors: ForeverZer0
Version: 1.1
Type: Input Simulator



Introduction

Recently I found need for a utility to automate mouse/input on my PC while I was away, and I employed the use of GhostMouse.  Unfortunately, this program is not free to use, and is only a limited trial unless purchased.  Although this can be bypassed easily, it inspired me to create my own application to do the same, so I introduce to you: MouseBot!

MouseBot is a very lightweight and simple program that comes packed with features to automate mouse movement, clicking, keystrokes, and text input. Simply build a list of commands with the user-friendly interface, and start the system to have them execute without your need to perform them. The possibilities are endless, and can be used for whatever you may require, such as automatically level-grinding in a game, fooling a web server to think that someone is still present to prevent a timeout, or whatever you can think of.


Features


  • Commands allow for clicking, pressing, or releasing any mouse button/key

  • Configure to simulate Ctrl, Alt, and/or shift during keystrokes

  • Automatic text input

  • Configurable delays between actions, down to one millisecond

  • Automated mouse movement

  • Save/Load settings and commands

  • Configurable hotkeys for starting/stopping execution

  • And more!




Screenshots

Main Interface: ShowHide

Options Window: ShowHide

Command Window: ShowHide

Tray Icon: ShowHide



Download

Application
Source Code


Compatibility

Requires Microsoft .NET Framework 4.0 or higher.

Any suggestions or bug reports are greatly appreciated!


License

Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)
21
RMXP Script Database / [XP] Memory Font Loader
January 08, 2014, 08:46:25 am
Memory Font Loader
Authors: ForeverZer0
Version: 1.1
Type: Game Utility
Key Term: Game Utility



Introduction

Small script that allows for loading and using fonts into memory at run-time without the need for installing them on the operating system. The advantage of this script over others, such as Wachunga's font-install script, is that it does not require a administrator privileges to use the fonts, nor a restart of the game after running.



Features


  • Automatically load all fonts found in user-defined folder

  • Supports TrueType (*.ttf) and OpenType(*.otf) fonts

  • Does not require Administrator privileges

  • Does not require a game restart

  • Plug & play




Screenshots

None.


Demo

None.


Script

Spoiler: ShowHide

#=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
# Memory Font Loader
# Author: ForeverZer0
# Version: 1.1
# Date: 12.17.2014
#=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
#                             VERSION HISTORY
#=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
# v.1.0   1.8.2014
#   - Original Release
# v.1.1   12.17.2014
#   - Improved compatibility on different systems by adding "AddFontResource"
#     as backup call if "AddFontResourceEx" fails
#=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
#
# Introduction:
#
#   Small script that allows for loading and using fonts into memory at run-time
#   without the need for installing them on the operating system. The advantage
#   of this script over others, such as Wachunga's font-install script, is that
#   it does not require a administrator privileges to use the fonts, nor a
#   restart of the game after running.
#
# Features:
#
#   - Automatically load all fonts found in user-defined folder
#   - Supports TrueType (*.ttf) and OpenType(*.otf) fonts
#   - Does not require Administrator privileges
#   - Does not require a game restart
#
# Instructions:
#   
#   - Place script anywhere above main
#   - Some optional configuration below with explanation
#   - Use the script call "Font.add(PATH_TO_FILE)" to add a font.
#   - If using auto-load, create a folder for the fonts ("Fonts" by default),
#     and place all font files within it. Loading will be done automatically.
#
# Compatibility:
#
#   - No known compatibility issues with any other script.
#
# Credits/Thanks:
#
#   - ForeverZer0, for the script
#   - Duds, for pointing out the "AddFontResource" fix
#
#=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=

#===============================================================================
# ** Font
#-------------------------------------------------------------------------------
# The font class. Font is a property of the Bitmap class.
#===============================================================================
class Font
 
  #-----------------------------------------------------------------------------
  #                            CONFIGURATION
  #-----------------------------------------------------------------------------
 
  # This value (true/false) indicates if all fonts in the specified fonts folder
  # will be automatically loaded when the game is started.
  AUTO_LOAD = true
   
  # Set this to the name of the folder, relative to the game directory, where
  # the font files are contained. This folder will be searched for font files
  # when using the auto-load feature
  FONT_FOLDER = 'Fonts'
 
  #-----------------------------------------------------------------------------
  #                           END CONFIGURATION
  #-----------------------------------------------------------------------------
 
  #-----------------------------------------------------------------------------
  # * Constants
  #-----------------------------------------------------------------------------
  WM_FONTCHANGE = 0x001D
  HWND_BROADCAST = 0xFFFF
  AddFontResource = Win32API.new('gdi32', 'AddFontResource', ['P'], 'L')
  AddFontResourceEx = Win32API.new('gdi32', 'AddFontResourceEx', 'PLL', 'L')
  SendMessage = Win32API.new('user32', 'SendMessage', 'LLLL', 'L')
  SendNotifyMessage = Win32API.new('user32', 'SendNotifyMessage', 'LLLL', 'L')
  #-----------------------------------------------------------------------------
  # * Class Variables
  #-----------------------------------------------------------------------------
  @@memory_fonts = []
  #-----------------------------------------------------------------------------
  # * Load font(s) into memory
  #     filename  : Full path to font file. Accepts multiple filenames.
  #-----------------------------------------------------------------------------
  def self.add(*filenames)
    filenames.each {|f|
      next if @@memory_fonts.include?(f)
      if AddFontResourceEx.call(f, 16, 0) > 0 || AddFontResource.call(f) > 0
        @@memory_fonts.push(f)
      end
    }
    SendNotifyMessage.call(HWND_BROADCAST, WM_FONTCHANGE, 0, 0)
    SendMessage.call(HWND_BROADCAST, WM_FONTCHANGE, 0, 0)
  end
  #-----------------------------------------------------------------------------
  # * Automatically seach and load all font files found in the font folder.
  #-----------------------------------------------------------------------------
  def self.auto_load
    dir = FONT_FOLDER == '' ? Dir.pwd : File.join(Dir.pwd, FONT_FOLDER)
    return unless File.directory?(dir)
    files = Dir.glob(File.join(dir, '*.{ttf,otf}'))
    self.add(*files)
  end
end

# Load fonts if using auto-load
if Font::AUTO_LOAD
  Font.auto_load
end



Instructions

Place script anywhere above main. Some optional configuration and instructions within the script.


Compatibility

No known compatibility issues, should have been solved with version 1.1.  


Credits and Thanks


  • ForeverZer0, for the script

  • Duds, for pointing out the "AddFontResource" fix




Author's Notes

Please feel free to ask for support, post bugs, or anything else. Enjoy!
22
Okay, so I decided to play around with RMXP out of boredom, first time in over a year, and I ran into something confusing.
Now, keep in mind that I am extremely rusty when it comes to RMXP, and it is taking time even remembering every last detail again...

So here's the problem/issue/interesting thing/misinformation/whatever you want to call it that I found. In the RMXP help manual, it says this about the different rank values:

Quote from: From the RMXP Help Manual: RPG::Classelement_ranks
Level of elemental effectiveness. 1-dimensional array using element IDs as subscripts (Table), with 6 levels (0: A, 1: B, 2: C, 3: D, 4: E, 5: F).

state_ranks
Level of status effectiveness. 1-dimensional array using status IDs as subscripts (Table), with 6 levels (0: A, 1: B, 2: C, 3: D, 4: E, 5: F).


Now, as I read that, it says a value of "C" in the editor should be "2". Correct?
Then why is it that if, using a default project where every rank is set to "C" in the editor, the actual values are 3's?

I simply just ran this little snippet after loading a game:
    table = $data_classes[1].element_ranks
    array = []
    (0..table.xsize).each {|i| array.push(table[i]) }
    p array


I don't know if I used to know why this was, if this is a bug, just a typo in the manual, or what. Any clues for an old scripter who forgets everything?


23
Development Tools / RPG Scan Tool - Beta Testers
August 18, 2012, 02:10:02 am
I took a small break from ARC (was getting a bit burned out on it), and for the past few days have been making another RPG tool.  Its a "scanner" for RPG projects that performs in-depth analysis and error checking on a project. It is a complete with a host of tools to automate project optimization.

Here are some of its features (there are more, but here's the highlights)

  • Compatible with XP, VX, and VXA

  • In depth scan of every map, event, and event command. Checks every event command in the game to ensure specified graphics and audio exists, as well as make sure associated data is still valid. For example, it checks that all transfer events connect to existing maps, checks that "Change Weapon" actually points to an existing weapon, etc, ect. I spent a lot of time and was very thorough. Literally every type of event command in all three RPG versions that this can be done with are checked.

  • Automatically finds and can copy every used RTP resource to the project locally for distribution. This only works when the resources are used in the database, events, or event commands. You will still need to double-check custom scripts for any RTP resources they may use.

  • Option to search and find all unused local resources and delete them. The same script restriction applies.

  • Finds and deletes hidden thumbnail.db files (Windows XP only). These are hidden files used for the icon cache by windows to generate thumbnails faster in Explorer. They would normally get packaged with your game without you ever knowing it and add size to the file.

  • Automatic copying of appropriate RGSS###.dll to the local directory for distribution

  • Script compression by using a stringer Zlib compression, removing whitespace, and removing comments. This can cut the size down by close to half in some cases, but is really only appropriate for a game ready for distribution since the formatting gets hard to read.

  • Integrated tool to find and convert JPG and BMP files to PNG, which are optimal for RM

  • Integrated PNG compressor, which can compress the PNG IDAT stream much more, as well as strip useless data from the file, all without losing any quality whatsoever. I have seen this method shave off as mush as 90% from PNG file sizes, so its quite handy for quickly saving space.

  • integrated tool to convert WAV, WMA, and MP3 files to OGG, which is a nice, lightweight format good for RM. There are options for target sampling rate, bitrate, and number of channels you would like to output the file as.

  • A few other minor things such as compressing the Game.exe file to shave off 20 kb, etc, etc.



The point of this post is that I should finish it up in the next day or so, and I would appreciate some people who are willing to beta-test. There are a lot of events, and three engines that need tested to ensure it catches errors as its supposed to do, as well as performs copying of RTP resources without missing any, doesn't screw up scripts when it compresses them, and other stuff. You get the idea.

If you would like to participate, just make a reply stating you would like to, and I when its ready, I will upload a copy for you to download. I will be happy to add all beta-tester to the credits as well, so anyone who uses it will be able to see your contribution to it. I'm really only looking for 3-4 people, and its on a first-come, first-serve basis. If you have a project of pretty good size to test it on, that would be a plus, but I have no problem with, even encourage, to download and run on some projects that people have shared around the community. I will supply more information to beta-tester via PM on specific things to be on the lookout for, so it mainly just requires altering around some database and event stuff within a project, running the program, and seeing if it catches errors that have been created. Nothing really hard.
24
I have been searching for the past two nights for a command line tool that can do the following:


  • Read .wma, .mp3, .wav

  • Convert above formats to .ogg



Now, this would not seem like to hard of a thing, but apparently it is. It also has to meet the following requirements:

  • Windows (there are a few good ones for Linux, but none I can find for Windows)

  • Offers the above formats and abilities without codecs needing installed on host machine

  • Free



The most promising I could find is Sound eXchange. It has the ability to process the above formats, but not out of the bag. I can't for the life of me seem to get ogg support working it.

Its getting rather frustrating actually. I am not even asking that it has MIDI support, which is the usual deal-breaker when it comes to audio libraries. No, my problem is finding a library with good OGG support, which is a patent-free, open-source, widely spread, and popular format. Linux seems to have all these to themselves...

EDIT:
Forgot to mention, ffmpeg was looking good too, but once again, the OGG support without having an installed codec was stopping me. I found a few builds that could read it, but not encode to it.
25
Resource Requests / Sprite Request
July 27, 2012, 11:25:27 pm
I need a character sprite, please.
Here is a sample of the battler graphic, so if someone could please make a character sprite that closely resembles it, that would be great.

Spoiler: ShowHide
26
RMXP Script Database / [XP][VXA] Localization
July 26, 2012, 09:39:00 pm
Localization
Authors: ForeverZer0, KK20
Version: 1.1
Type: Multilingual Game Tool
Key Term: Game Utility



Introduction

This script adds support for creating games that support multiple languages. The files for each culture are in .ini format and located externally so that they can be easily translated and used by simply dropping the language file into the game's "Localization" folder.


Features


  • Uses IDs to get strings from external .ini files, which can be easily edited in any text editor, such as Notepad

  • Supports special characters used by non-English languages

  • Included message commands to simplify making your multilingual game, you use the command the same as you would the normal switch and variable commands

  • Also supports using in the Bitmap#draw_text methods so that custom scripts that draw onto Sprite bitmaps instead of Windows will still work without the need for customization

  • Automatically saves/loads the current culture




Screenshots

None.


Demo

[XP] Demo Link (Thank Google for the wonderful translations...)


Script

#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
# Localization
# Author: ForeverZer0
# Version: 1.1
# Date: 7.27.12
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
#                             VERSION HISTORY
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
# v.1.0  7.26.12
#   - Initial release
# v.1.1  7.27.12
#   - Added ability to use any string as the ID instead of just numbers
#   - Added an alert message when a string is not found in the localization
#     file and the game is being ran in DEBUG mode
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
#
# Introduction:
#   This script adds support for creating games that support multiple languages.
#   The files for each culture are located externally so that they can be easily
#   translated and used by simply dropping the language file into the games
#   "Localization" folder.
#
# Features:
#   - Uses IDs to get strings from external .ini files, which can be easily
#     edited in any text editor such as Notepad
#   - Supports special characters used by non-English languages
#   - Included message commands to simplify making your multilangual game, you
#     use the command the same as you would the normal switch and variable
#     commands
#   - Also supports using in the Bitmap#draw_text methods so that custom scripts
#     that draw onto Sprite bitmaps instead of Windows will still work without
#     the need for customization
#   - Automatically saves/loads the current culture
#
# Instructions:
#   - Place the script anywhere in the editor
#   - Define the name of the language directory and default culture below
#   - The names of the cultures can be anything, I would stick to the convention
#     of naming files in the normal convention of "language-region" initials,
#     such as "en-US" (English, USA), "es-ES" (Spanish, Spain), etc., etc.
#   - Each language file should be a file with an .INI extension. If you are
#     unfamiliar with how .ini files are written, do a quick Google search, they
#     are quite simple.
#   - The .ini file should have only one section, which is the name of the
#     culture.
#             ex. [en-US]
#             ex. [es-ES]
#   - After that, you can simply begin to write the id-value pairs of each
#     string used in the culture.
#             ex. 1=This is a string
#             ex. 2=This is a string with ID 2
#             ex. GameIntro_01=This is a string with ID "GameIntro_01"
#   - Most importantly, the files must be saved in UTF-8 WITHOUT BOM. BOM stands
#     for "byte-order marker" and is a two byte value that is written to UTF8
#     files as a marker to other applications that they are encoded in UTF8.
#     WinAPI does not support this, so it must be saved without it. The text
#     will not appear if you fail to do this.
#   - Simply drop the completed files into the Localization folder
#   - To change the culture in game, use the following script command:
#         Localization.culture = CULTURE
#     CULTURE should be a string value that is the name of the file, without
#     extension
#             ex. Localization.culture = "en-US"
#  
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

#===============================================================================
# ** Localization
#-------------------------------------------------------------------------------
# Static module for reading localized strings from language files.
#===============================================================================

module Localization
 #-----------------------------------------------------------------------------
 # * Constants
 #-----------------------------------------------------------------------------
 DIRECTORY = "Localization"
 DEFAULT_CULTURE = 'en-US'
 #-----------------------------------------------------------------------------
 # * Public instance variables.
 #-----------------------------------------------------------------------------
 attr_reader :culture
 attr_reader :filename
 #-----------------------------------------------------------------------------
 # * Initialize the localization module
 #-----------------------------------------------------------------------------
 def self.init
   @ini = Win32API.new('kernel32', 'GetPrivateProfileString', 'PPPPLP', 'L')
   unless File.directory?(DIRECTORY)
     Dir.mkdir(DIRECTORY)
   end
   path = ".\\#{DIRECTORY}\\CurrentCulture.txt"
   self.culture = File.exists?(path) ? IO.readlines(path)[0] : DEFAULT_CULTURE
 end
 #-----------------------------------------------------------------------------
 # * Set the current culture
 #-----------------------------------------------------------------------------
 def self.culture=(value)
   filename = ".\\#{DIRECTORY}\\#{value}.ini"
   if File.exists?(filename)
     @culture = value
     @filename = filename
     path = ".\\#{DIRECTORY}\\CurrentCulture.txt"
     File.open(path, 'wb') {|file| file.write(@culture) }
   else
     print "Cannot find language file for \"#{value}\"\n" +
       "Please ensure that the file \"#{filename}\" exists."
   end
 end
 #-----------------------------------------------------------------------------
 # * Read the string with the given ID of the current culture
 #-----------------------------------------------------------------------------
 def self.read(id)
   text = "\0" * 256
   @ini.call(@culture, id, '', text, 256, @filename)
   text.delete!("\0")
   if text == '' && $DEBUG
     print "No string defined!\nKey: \"#{id}\"\nCulture: \"#{@culture}\""
   end
   return text
 end
 #-----------------------------------------------------------------------------
 # * Parses a string for localization codes and returns it
 #-----------------------------------------------------------------------------
 def self.localize(string)
   if string != nil
     return string.gsub(/\\[Ll][Oo][Cc]\[(.+)\]/) { self.read($1) }
   end
 end
end

#===============================================================================
# ** Game_Temp
#===============================================================================

class Game_Temp
 #-----------------------------------------------------------------------------
 # * Overrids the setter for message text to localize the "Show Text" command
 #-----------------------------------------------------------------------------
 def message_text=(string)
   @message_text = Localization.localize(string)
 end
end

#===============================================================================
# ** Bitmap
#===============================================================================

class Bitmap
 #-----------------------------------------------------------------------------
 # * Replaces text based on the current culture
 #-----------------------------------------------------------------------------
 alias localized_draw_text draw_text
 def draw_text(*args)
   args.each_index {|i|
     args[i] = Localization.localize(args[i]) if args[i].is_a?(String) }
   localized_draw_text(*args)
 end
end

# Initialize static variables used in the module
Localization.init


#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
# Localization
# Edit for VX-ACE by KK20
# Author: ForeverZer0
# Version: 1.1
# Date: 7.26.12
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
#                             VERSION HISTORY
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
# v.1.0  7.26.12
#   - Initial release
# v.1.1  7.27.12
#   - Added ability to use any string as the ID instead of just numbers
#   - Added an alert message when a string is not found in the localization
#     file and the game is being ran in DEBUG mode
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
#
# Introduction:
#   This script adds support for creating games that support multiple languages.
#   The files for each culture are located externally so that they can be easily
#   translated and used by simply dropping the language file into the games
#   "Localization" folder.
#
# Features:
#   - Uses IDs to get strings from external .ini files, which can be easily
#     edited in any text editor such as Notepad
#   - Supports special characters used by non-English languages
#   - Included message commands to simplify making your multilangual game, you
#     use the command the same as you would the normal switch and variable
#     commands
#   - Also supports using in the Bitmap#draw_text methods so that custom scripts
#     that draw onto Sprite bitmaps instead of Windows will still work without
#     the need for customization
#   - Automatically saves/loads the current culture
#
# Instructions:
#   - Place the script anywhere in the editor
#   - Define the name of the language directory and default culture below
#   - The names of the cultures can be anything, I would stick to the convention
#     of naming files in the normal convention of "language-region" initials,
#     such as "en-US" (English, USA), "es-ES" (Spanish, Spain), etc., etc.
#   - Each language file should be a file with an .INI extension. If you are
#     unfamiliar with how .ini files are written, do a quick Google search, they
#     are quite simple.
#   - The .ini file should have only one section, which is the name of the
#     culture.
#             ex. [en-US]
#             ex. [es-ES]
#   - After that, you can simply begin to write the id-value pairs of each
#     string used in the culture.
#             ex. 1=This is a string
#             ex. 2=This is a string with ID 2
#             ex. GameIntro_01=This is a string with ID "GameIntro_01"
#   - Most importantly, the files must be saved in UTF-8 WITHOUT BOM. BOM stands
#     for "byte-order marker" and is a two byte value that is written to UTF8
#     files as a marker to other applications that they are encoded in UTF8.
#     WinAPI does not support this, so it must be saved without it. The text
#     will not appear if you fail to do this.
#   - Simply drop the completed files into the Localization folder
#   - To change the culture in game, use the following script command:
#         Localization.culture = CULTURE
#     CULTURE should be a string value that is the name of the file, without
#     extension
#             ex. Localization.culture = "en-US"
#   
#+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

#===============================================================================
# ** Localization
#-------------------------------------------------------------------------------
# Static module for reading localized strings from language files.
#===============================================================================

module Localization
  #-----------------------------------------------------------------------------
  # * Constants
  #-----------------------------------------------------------------------------
  DIRECTORY = "Localization"
  DEFAULT_CULTURE = 'en-US'
  #-----------------------------------------------------------------------------
  # * Public instance variables.
  #-----------------------------------------------------------------------------
  attr_reader :culture
  attr_reader :filename
  #-----------------------------------------------------------------------------
  # * Initialize the localization module
  #-----------------------------------------------------------------------------
  def self.init
    @ini = Win32API.new('kernel32', 'GetPrivateProfileString', 'PPPPLP', 'L')
    unless File.directory?(DIRECTORY)
      Dir.mkdir(DIRECTORY)
    end
    path = ".\\#{DIRECTORY}\\CurrentCulture.txt"
    self.culture = File.exists?(path) ? IO.readlines(path)[0] : DEFAULT_CULTURE
  end
  #-----------------------------------------------------------------------------
  # * Set the current culture
  #-----------------------------------------------------------------------------
  def self.culture=(value)
    filename = ".\\#{DIRECTORY}\\#{value}.ini"
    if File.exists?(filename)
      @culture = value
      @filename = filename
      path = ".\\#{DIRECTORY}\\CurrentCulture.txt"
      File.open(path, 'wb') {|file| file.write(@culture) }
    else
      print "Cannot find language file for \"#{value}\"\n" +
        "Please ensure that the file \"#{filename}\" exists."
    end
  end
  #-----------------------------------------------------------------------------
  # * Read the string with the given ID of the current culture
  #-----------------------------------------------------------------------------
  def self.read(id)
    text = "\0" * 256
    @ini.call(@culture, id, '', text, 256, @filename)
    text.delete!("\0")
    if text == '' && $DEBUG
      print "No string defined!\nKey: \"#{id}\"\nCulture: \"#{@culture}\""
    end
    return text
  end
  #-----------------------------------------------------------------------------
  # * Parses a string for localization codes and returns it
  #-----------------------------------------------------------------------------
  def self.localize(string)
    if string != nil
      return string.gsub(/\\[Ll][Oo][Cc]\[(.+)\]/) { self.read($1) }
    end
  end
end

#===============================================================================
# ** Game_Message
#===============================================================================

class Game_Message
  alias localized_text_add add
  def add(text)
    text = Localization.localize(text)
    strings = text.split("\\n")
    while strings.size > 0
      localized_text_add(strings.shift)
    end
  end
end

#===============================================================================
# ** Window_Base
#===============================================================================

class Window_Base
  alias localize_draw_text_ex draw_text_ex
  def draw_text_ex(x, y, text)
    text = Localization.localize(text)
    localize_draw_text_ex(x, y, text)
  end
end

#===============================================================================
# ** Bitmap
#===============================================================================

class Bitmap
  #-----------------------------------------------------------------------------
  # * Replaces text based on the current culture
  #-----------------------------------------------------------------------------
  alias localized_draw_text draw_text
  def draw_text(*args)
    args.each_index {|i|
      args[i] = Localization.localize(args[i]) if args[i].is_a?(String) }
    localized_draw_text(*args)
  end
end

# Initialize static variables used in the module
Localization.init



Instructions

Please see script for instructions.  The demo contains examples that will make much it much clearer.

How to Create a Language File: ShowHide

  • I highly suggest using Notepad++ to edit and create language files. It makes changing text encoding a simple click of the mouse (which is required), and syntax highlighting for .INI files. The rest of these instructions will be assuming you are using it.

  • Open up Notepad++ and create a new file. I strongly suggest to stick to the normal convention of naming files in the LANGUAGE-REGION format, where LANGUAGE and REGION are two-letter initials for their respective values. ex. "en-US", "en-GB", "fr-FR", etc., etc.

  • In the top menu bar, click "Encoding" and select "Encode in UTF-8 without BOM"

  • Create a section in the .ini file with the same name as the file (see image below)

  • Begin adding your id-string pairs into the file. The number/word for the IDs are arbitrary, but the they will be the numbers/word used to get the required strings throughout your game, so I recommend you use an order that helps you stay organized.

  • Save the file with an ".ini" extension by choosing "File" -> "Save As...", and selecting "MS ini file" from the combo box.

  • Drop the file into your game's "Localization" folder and set the current culture to the name you used, and your done.



How to use Localization in Your Game: ShowHide

  • Very simple. Pretty much anywhere you want to use a localized string, simply use the "\loc[ID]" command in place of the text, where the ID is obviously the ID specified in the language file.

  • This command can be used in "Show Text", "Show Choices", etc. commands, as well as within normal "draw_text" commands used by scripts. It can be used within the database editor for names of classes, words, elements, etc. Pretty much everywhere and anywhere. You may have to do a little search and replace of the few values that are hard-coded within the default scripts.

  • To change the current language, simply use the following command:

# This is an example, specify whatever culture you have defined
Localization.culture = 'en-US'





Compatibility

No known compatibility issues. Custom Message systems may require slight modification, though have not been tested. I can offer support for to making them compatible on a "per system" basis.


Credits and Thanks


  • ForeverZer0, for the script

  • KK20, for the VXA edit




Author's Notes

Please report bugs/issues so that may be resolved.
Feel free to request compatibility fixes if you have a Custom Message System that does not work with the script.
27
ARC Welder / ARCed.NET
July 14, 2012, 07:29:16 pm




Overview
As Blizzard has already mentioned, a Microsoft .NET version of the editor will be released as a precursor to the cross-platform version written in Python. This implementation is primarily being built for the Legacy edition as a means to get out a public release, since .NET-based applications can be rapidly developed. Since the .NET Framework is Windows platform dependent, this application will be as well, but fear not, this is not meant to be the "official" version of ARCed, more of just a tie-me-over until the cross-platform implementation is complete.

As I already mentioned, this editor is being built for the purpose of the "Legacy" edition of ARC. If you are not sure of what "Legacy is, it basically a version that is RMXP emulation, but with a few of the features the full ARC engine at your disposal as well. In essence, it is merely a improved version of RMXP, with better performance, and some cool extras. The full release will be loaded with more goodies, but Legacy basically provides a means to pick up where you left off with your RMXP project and continue development.

As was already mentioned in Blizzard's announcement, ARCed.NET will require some dependencies to be installed on the host machine in order to run:


You do not need to install these ahead of time, the ARCed.NET installer will install them for you if they are not present on your machine, but ARCed.NET also has the ability to run portably from a USB stick or folder anywhere on your hard drive, so you may opt to install them and simply run the application in place of using the ARCed.NET installer. The XNA Redistributable dependency may not be required by release time, but currently it is, so I have included here. As the editor approaches BETA, I will be refining builds to hopefully limit the dependencies to only the .NET Framework.



Features
As I mentioned, although the Legacy edition is more or less an RMXP emulator, there are still a number of improvements that were made, merely because of restrictions that were placed on you in RMXP's editor. These restrictions have been removed, and some new features that you always wanted in the RMXP version have been added. Here is a list of some of the CURRENT features that have already been included:


  • Advanced docking GUI. If you are unfamiliar with what this is/means, it is user interface that allows you to move, resize, and dock nearly any window wherever you like in the application. You can arrange the layout of windows and tools to whatever your personal preference is and what allows your to be the most productive. These layouts are saved on a per-project basis, so next time you load that project, all the windows you had open when it was closed will be opened and lain out just like you last left them. On top of that, most controls within each window can be resized, such as expanding a list of names so that it displays more text, etc..

  • Restrictions for max values have been eliminated (to a degree). No longer are you forced to use special scripts just to get beyond the 999 mark for maximum of data types, or increasing actor/enemy parameters beyond a certain point. By default, these max values have had the upper limit raised tenfold, but if that is still not enough, there is a configuration file that allows you to increase it more.

  • Templates for projects and scripts. Have a basic project that you wanted to be able to reuse as a base for another project? Simply save it as a template, and the next time you go to create a new project, simply choose it from the list of templates, and a new project will be created using it as a base. The same can be done with scripts. This means no more typing out headers and sections listing features for scripters you like to use templates when they release their scripts. ;)

  • Automatic backups. There are various settings that allow you to set up automatic backups for your project. You can define what data types to save, such as .rxdata, scripts, maps, or combinations thereof, as well as how often. Currently there are options to create backups at timed intervals, whenever data is saved, or during test runs. There is a small built-in utility that makes backing up and restoring a breeze, which I have dubbed the "ARChiver". :)

  • Advanced script editor that allows for settings of syntax style, colors, auto-complete, etc. (Based off Gemini with improvements made). Also allows for opening multiple source editors simultaneously and placing side-by-side, or wherever you decide to dock/float them.

  • Powerful find/replace for script editors that allows searching using wildcards, regular expressions, and many other flags that can be applied.

  • Advanced color selector that allows you to select colors in the RGB and HSV color space, as well as pick colors from anywhere on the screen.

  • Loading and using fonts from files instead of installing them to maintain portability, and for those who do not privileges to install.

  • Settings for fonts, colors, and style of the windows in the editor so that you can have it look however you like.

  • Better preview of graphics, such as previewing fog scrolling, opacity, etc. in the editor instead of having to test play to see effect.

  • Full plugin system that allows other .NET developers to easily create extensions. Simply make a reference to the ARCed.NET editor, write your code logic using all the imported data to do what you want, and build. The .exe/.dll can then be dropped into the plugin folder and will be accessible within the main editor. All publicly accessible properties/methods are fully documented and have Intellisense for Visual Studio users to increase productivity.

  • Loads of other minor tweaks that are too numerous to lister here, such as batch selection of tiles while editing tileset passages/priorities, advanced charting controls for setting actor parameters, and many more!





Release
I do not want to give a definitive release date just yet, but it will likely be within one month (mid-August). ARCed.NET is nearing BETA, with only two somewhat "large" things left to implement (map editor and event builder) before final tweaking and improving begins. The map editor is pretty easy, just requires a fair amount of code, and the same can be said of the event builder. For the first release, it will likely be not much more than what RMXP has, but it is possible I will come up with something a bit more user-friendly and have implemented before them. No guarantees.



Screenshots/Videos
Spoiler: ShowHide
Haha, not yet. Will make some within the next day or so.
28
Core Development / Troubles with Animations...
July 07, 2012, 11:04:44 pm
Well, I have gotten down to the last two panels: Animations and System. The System panel is a cinch, so I figured I would work on the Animations panel to get the more difficult of the two out of the way. As I mentioned before, Animations.arc fails to load, I presume from the Table class size issue that Blizzard discovered. Now, as far as I am can see/understand, my Table class does handle 0 sized tables appropriately, so I am at a bit of a loss as to what the issue is, because it still does not load.

I was wondering if either of you two could take a look at it and see if you see something that I don't with the logic. I can't seem to find what the problem is. I get an error that the byte identifier is unrecognized, so I assume that the _load method is reading too little or too many bytes and the stream position is getting screwed up.

It would make creating/debugging the Animations panel SOOOO much easier if I real data to be working with instead of trying to build a few complete animations at runtime just to test with.
29
Core Development / ARCed.NET
June 30, 2012, 01:50:36 pm
As was talked about in the other topic, I created a .NET based variation of the editor. I am committing all my work sometime today, but in the meantime I can tell you two a bit about it.

Dependencies (End-user needs installed)

  • Microsoft .NET 4.0 ("Full" for developers. I haven't tested, but users should only require the Client Profile)

  • Windows XP or higher



External Native Libraries (Included)

  • 7z.dll or 7z64.dll, the libraries used for 7zip

  • SciLexer or SciLexer64, the native Scintilla libraries



.NET Assemblies (Included)

  • Dejavu.dll, a undo/redo framework I have yet to implement, but it makes that pretty easy.

  • SevenZipSharp.dll, a wrapper for the native 7zip libraries

  • Microsoft.Dynamic.dll, assembly that allows for "dynamic" types that basically allows you to work with data of unknown types without compiler issues

  • Microsoft.Xna.Framework.dll, the primary XNA library for rendering

  • Microsoft.Xna.Framework.Graphics.dll, a secondary XNA containing various types used for rendering



With the exception of the .NET Framework needing installed on the host machine, the application is completely portable, and will run off a USB stick without need for further installation. I would still like to have it as an installed program though, since if you are running portably, file associations cannot be made without modifying the Registry. This means there would be no "click project to open" or associated icons, the user would need to manually open the editor and load a project, or drop a project on top of the editor icon. BTW, I have setting file associations and icons done, they can only be made if the editor is run as Administrator obviously, which it also checks for. Making the app portable for the end-user will be nothing more than copy-pasting the install directory to a USB stick or whatever though, so thats a plus. The application stores data in Appdata/Roaming/ARCed.NET", so to create 100% portability, I will need to include a setting to offer the choice to store settings locally in the program directory. Small thing there.

Features (Implemented)

  • Ryex's cool idea for using "templates" to create projects. I am keeping the template files compressed, and using 7zip to extract them if they are chosen from the "New Project" dialog. In the "File" tab of the main menu strip, there is an option to "Save as Template", which will compress and create a copy of the current project to create a new template to use for new projects. There is a "default" project that is embedding in the program, so that will always be available, and there won't be any worries of screwing up new project creation.

  • Script templates. I went a step farther and also included script templates, meaning you can do the same as above when creating new scripts. Pretty much like what VS does when you choose "New Class...", etc.

  • Backups (another Ryex idea ;) ), an automatic system for creating backups. There are various options on when to save (on play test, on every save, at intervals, etc), and options on what to save (scripts, database, maps, etc). The backups are compressed and stored locally with the project. They are given a GUID for a name. I would have worried about actual filenames, but GUIDs are much simpler, since the only real factor needed to differentiate is creation times of the backups. There is a small manager for it that allows for setting max backups created, as well as deleting and restoring. I dubbed it the "ARChive Utility".

  • I am using a free and open-source docking library I found on SourceForge called DockPanel Suite. I made various edits to support default windows sizes for floating windows, etc, and implemented a skin manager that allows for changing colors and fonts. It provides the VS type of docking functionality we want.

  • I am using Scintilla.NET again for the editor, same as I did for Gemini. I made various improvements to it, and it still have more plans for it, which I will list in the "Planned Features" section I am going to write after this one.

  • I am including settings for nearly everything. Most of these can be ignored, and are mainly aesthetic changes, but its a nice touch. Script style settings for fonts and colors of data types is fully configurable, same as it was in Gemini, although I made a different form for it.

  • Plugin system. The plugin system is extremely simple to use. I am storing all important data used in the editor and project within a few public static classes. All someone needs to do is create a form in any .NET language (C#, Visual C++, or Visual Basic, etc.), make a reference to the main editor, and have their form inherit from the dock form and IPlugin interface. All project, editor, windows, and settings that a plugin would have business altering are easily accessible through these class. They can simply do what they need through them. The editor will handle everything else. Just throw the finished dll or exe into the plugin folder and loads up when the editor is started. These plugins are "registered" with the editor, which can then pick them apart with Reflection to tell anything you need to know about them.

  • I have created loads of custom controls for the editor as well. Many of these are for reducing redundancies, but there are many that also provide a rich-GUI experience for the editor. I created a custom ColorPicker that replaces the boring default Windows one. It supports RGB and HSV, contains a built in capture picker that lets the user pick a color from the screen. There is also a custom FontPicker. The font picker was a bit of a necessity. I implemented loading and using fonts from a file so that they didn't need installed (the portability thing). GDI+ requires only OpenType fonts and the standard dialog does not contain filters for that. On top of that, even when you forced enumeration of "memory fonts", the dialog would throw an error when you selected them and chose "OK". I researched this a bit, and according to Microsoft, they are aware of the issue, but have no plans on ever fixing it, since it lies so deep in the API that to change it would cause only more bugs. Its a small thing, and I have overcome it already, so no problem there.

  • Serialization. This was a bitch to do. I managed to implement this with the use of Reflection, and I am proud to say the results are outstanding. It is quite capable of reading and writing the ARC data format with great speed. After I dropped IronRuby, I knew this would be one of the largest obstacles to overcome, and I finally got it working flawlessly after a 2 days. I still need to alter the Table class to what Blizz mentioned about 0 sizes. It cannot currently load Animations.arc due to that, but it has no problem reading and dumping Tables under other circumstances. I just haven't got around to it yet.

  • RTP and local resources have been full implemented. Both audio and graphics are loaded as a "GameResource", and there is a central class that handles accessing them. There are many different methods and properties to get all resources of certain types, whether it be all local, all rtp, all graphics, all local audio, all local SE, all RTP characters, etc, etc. You get the point. In the "picker" dialogs when users select them, they are given markers, just like in RMXP, to differentiate their location. Local resources are listed first, same as RMXP. I also implemented FileSystemWatchers to detect changes to these directories and refresh itself accordingly without having to worry about it.

  • There are loads of other minor things I have not mentioned here. I have built it to be extremely dynamic as a whole. Practically nothing "hard-coded" in, and where it is they are mainly placeholders. This will allow for expansion into whatever is needed. For example, instead of have 5 slots for actor equipment, the slots are created at runtime from reading a configuration (which is a simple placeholder for now). The configuration simply tells what label and "type" to include in its slot, and the rest is done automatically from there. If we decide we want to have 3 equippable accessories, all it takes is to simple add a some extra configuration. All the logic to add to the form and automatically alter the data is already there without having to code anything else. Same thing with parameters. You basically just add a configuration that says X's label is SPD, its associated data type to effect is a property called "spd", and everything else is done. The forms adds a numeric up-down box labeled "SPD" to the form, and when changed, it alters a parameter of the current game object that is named "spd". There would be obvious caveats that would need addressed if this configuration was open to the user, such as memory corruption, but it makes altering things for a dev to be a simple matter of 5 seconds.



Planned Features (not/partially implemented)("*" Denotes a requirement before completion)

  • Tilemap editor, although I already know exactly how to do it. Its not just implemented yet. I have already created a C# version a while back that uses GDI+ when I made the MapConverter project. It will be quite simple to use the same logic in an embedded XNA panel. (Already implemented embedding XNA panels into WinForms, so no problem there) (*)

  • Snippet manager for the script editor

  • Creation of a full plugin manager that allows for viewing information about and control of plugins, etc.

  • XAL wrapper for using the library within the editor. (*)

  • Finish the last few database forms (Tilemap, Animation, and System)(*)

  • Implement an "EventBuilder" class that creates RPG.EventCommands from code values and parameter arrays. (Already finished that parser that does the obvious to display in the event editor windows)(*)

  • Improve handling of creating/saving/loading/renaming script files

  • Make some improvements to make a few things more modular that I omitted

  • There is a more detailed TODO.txt file that I am committing, as well as plenty of TODO comments throughout the code I will be committing.


There are a number of things I am sure I am forgetting to mention, or need explained fuller, but I just wanted to give you guys a general idea of what's going on so far, so that I after I commit, you have some type of initial knowledge of what the hell is going on. If you have any questions or concerns, please don't hesitate to say so. I have been solo on this without having a second person there to call out major flaws I may be overlooking, so anything you have to say is important.
30
Programming / Scripting / Web / Visual Studio 2012
June 05, 2012, 12:50:50 pm
Has anyone tried it out yet?

Its in release candidate state, so it might be worth some trying out if your interested.
I might finally get around to checking it out later today.

Here's a link to the site.
http://www.microsoft.com/visualstudio/11/en-us/downloads