class Demiurge::Tmx::TmxCache

A TmxCache loads and remembers TMX file maps from the tmx gem. For a variety of reasons, it's not great to reload TMX files every time we need to know about them, but it can also be a problem to store a copy of the parsed version every time and place we use it. Caching is, of course, a time-honored solution to this problem.

@since 0.3.0

Attributes

root_dir[R]

@return [String] Root directory the cache was created relative to

Public Class Methods

new(options = {}) click to toggle source

Create the TmxCache

@param options [Hash] Options @option options [String] :root_dir The root directory to read TMX and TSX files relative to @return [void] @since 0.3.0

# File lib/demiurge/tmx.rb, line 273
def initialize(options = {})
  @root_dir = options[:root_dir] || Dir.pwd
end

Public Instance Methods

clear_cache() click to toggle source

Clear the TMX cache. Remove all existing entries. This can potentially break existing TMX-based objects. Objects often refer repeatedly back into the cache to avoid duplicating structures, and to pick up new changes to those structures.

@return [void] @since 0.3.0

# File lib/demiurge/tmx.rb, line 284
def clear_cache
  @tile_cache = {}
  nil
end
root_dir=(new_root) click to toggle source

Change the root directory this cache uses. Note that any existing TMX files with a different root would presumably change, so this clears the cache as a side effect.

@param new_root [String] The new directory to use as TMX root for this cache @return [void] @since 0.3.0

# File lib/demiurge/tmx.rb, line 296
def root_dir=(new_root)
  clear_cache
  @root_dir = new_root
  nil
end
tmx_entry(layout_type, layout_filename) click to toggle source

Return a TMX entry for the given layout type and filename. A “layout type” is something like normal TMX (called “tmx”) or Manasource-style (called “manasource”.) But additional types are likely to be added in future versions of Pixiurge.

@param layout_type [String] The subtype of TMX file; currently may be one of “tmx” or “manasource” @param layout_filename [String] The path to the TMX file relative to the tile cache's TMX root directory @return [Hash] The TMX cache entry, in an internal and potentially changeable format @api private @since 0.3.0

# File lib/demiurge/tmx.rb, line 312
def tmx_entry(layout_type, layout_filename)
  @tile_cache ||= {}
  @tile_cache[layout_type] ||= {}
  if @tile_cache[layout_type][layout_filename]
    return @tile_cache[layout_type][layout_filename]
  end

  if layout_type == "manasource"
    @tile_cache[layout_type][layout_filename] = sprites_from_manasource_tmx(layout_filename)
  elsif layout_type == "tmx"
    @tile_cache[layout_type][layout_filename] = sprites_from_tmx(layout_filename)
  else
    raise "A TMX location must have a known type of layout (tmx or manasource), not #{layout_type.inspect}!"
  end

  @tile_cache[layout_type][layout_filename]
end

Private Instance Methods

animations_from_tilesets(tilesets) click to toggle source

Extract the animations included in the TMX file

@since 0.1.0

# File lib/demiurge/tmx.rb, line 410
def animations_from_tilesets tilesets
  tilesets.flat_map do |tileset|
    (tileset["tiles"] || []).map do |tile|
      p = tile["properties"]
      if p && p["animation-frame0"]
        section = 0
        anim = []

        while p["animation-frame#{section}"]
          section_hash = {
            "frame" => p["animation-frame#{section}"].to_i + tileset[:firstgid],
            "duration" => p["animation-delay#{section}"].to_i
          }
          anim.push section_hash
          section += 1
        end
        { "tile_anim_#{tile["id"].to_i + tileset[:firstgid]}" => anim }
      else
        nil
      end
    end.compact
  end.inject({}, &:merge)
end
sprites_from_manasource_tmx(filename) click to toggle source

This is to support TMX files for ManaSource, ManaWorld, Land of Fire, Source of Tales and other Mana Project games. It can't be perfect since there's some variation between them, but it can follow most major conventions.

Load a TMX file and calculate additional information like animations from the given properties. Assume this TMX file obeys ManaSource conventions such as fields for exits and names for layers.

@since 0.1.0

# File lib/demiurge/tmx.rb, line 343
def sprites_from_manasource_tmx(filename)
  entry = sprites_from_tmx filename

  stack_layers = entry["map"]["layers"].select { |layer| layer["type"] == "tilelayer" }

  # Remove the collision layer, add as separate collision top-level entry
  collision_index = stack_layers.index { |l| l["name"].downcase == "collision" }
  collision_layer = stack_layers.delete_at collision_index if collision_index

  # Some games make this true/false, others have separate visibility
  # or swimmability in it. In general, we'll just expose the data.
  entry["collision"] = collision_layer["data"] if collision_layer

  # Remove the heights layer, add as separate heights top-level entry
  heights_index = stack_layers.index { |l| ["height", "heights"].include?(l["name"].downcase)  }
  heights_layer = stack_layers.delete_at heights_index if heights_index
  entry["heights"] = heights_layer

  fringe_index = stack_layers.index { |l| l["name"].downcase == "fringe" }
  raise ::Demiurge::Errors::TmxFormatError.new("No Fringe layer found in ManaSource TMX File #{filename.inspect}!", "filename" => filename) unless fringe_index
  stack_layers.each_with_index do |layer, index|
    # Assign a Z value based on layer depth, with fringe = 0 as a special case
    layer["z"] = (index - fringe_index) * 10.0
  end

  entry
end
sprites_from_tmx(filename) click to toggle source

Load a TMX file and JSONify it. This includes its various tilesets, such as embedded tilesets and TSX files. Do not assume this TMX file obeys any particular additional conventions beyond basic TMX format.

@since 0.1.0

# File lib/demiurge/tmx.rb, line 377
def sprites_from_tmx(filename)
  cache_entry = {}

  # This recursively loads things like tileset .tsx files, so we
  # change to the root dir.
  Dir.chdir(@root_dir) do
    tmx_map = Tmx.load filename
    filename.sub!(/\.tmx\Z/, ".json")
    cache_entry["map"] = MultiJson.load(tmx_map.export_to_string(:filename => filename, :format => :json))
  end

  tiles = cache_entry["map"]
  cache_entry["tilesets"] = tiles["tilesets"]

  # Add entries to the top-level cache entry, but not inside the "map" structure
  cache_entry["tmx_name"] = File.basename(filename).split(".")[0]
  cache_entry["name"] = tiles["name"] || cache_entry["tmx_name"]
  cache_entry["filename"] = filename
  cache_entry["dir"] = filename.split("/")[0..-2].join(".")
  cache_entry["animations"] = animations_from_tilesets cache_entry["tilesets"]
  cache_entry["objects"] = tiles["layers"].flat_map { |layer| layer["objects"] || [] }

  # Copy most-used properties into top-level cache entry
  ["width", "height", "tilewidth", "tileheight"].each do |property|
    cache_entry[property] = tiles[property]
  end

  cache_entry
end