summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2021-04-09 22:37:49 -0400
committerDavid Thompson <dthompson2@worcester.edu>2021-04-09 22:45:33 -0400
commite05aa45f672eb49312449d359a9e345223741b19 (patch)
tree1a00e89b1116546c2fb93e294870f9005fea46dc
parent5c919ae2dfaee36d689b2c2bedee1d2745bd2d6d (diff)
graphics: Rewrite tile map module.
-rw-r--r--Makefile.am2
-rw-r--r--chickadee/graphics/tile-map.scm926
-rw-r--r--chickadee/graphics/tiled.scm500
-rw-r--r--doc/api.texi25
-rw-r--r--examples/images/serene-village.pngbin0 -> 122591 bytes
-rw-r--r--examples/images/tiles.pngbin100846 -> 0 bytes
-rw-r--r--examples/maps/example.tmx348
-rw-r--r--examples/maps/serene-village.tsx84
-rw-r--r--examples/tile-map.scm (renamed from examples/tiled.scm)59
9 files changed, 1336 insertions, 608 deletions
diff --git a/Makefile.am b/Makefile.am
index d397f3a..a853edd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -77,7 +77,7 @@ SOURCES = \
chickadee/graphics/sprite.scm \
chickadee/graphics/9-patch.scm \
chickadee/graphics/font.scm \
- chickadee/graphics/tiled.scm \
+ chickadee/graphics/tile-map.scm \
chickadee/graphics/particles.scm \
chickadee/graphics/phong.scm \
chickadee/graphics/pbr.scm \
diff --git a/chickadee/graphics/tile-map.scm b/chickadee/graphics/tile-map.scm
new file mode 100644
index 0000000..8984691
--- /dev/null
+++ b/chickadee/graphics/tile-map.scm
@@ -0,0 +1,926 @@
+;;; Chickadee Game Toolkit
+;;; Copyright © 2018, 2020, 2021 David Thompson <dthompson2@worcester.edu>
+;;;
+;;; Chickadee is free software: you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published
+;;; by the Free Software Foundation, either version 3 of the License,
+;;; or (at your option) any later version.
+;;;
+;;; Chickadee is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program. If not, see
+;;; <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Tile map renderer and Tiled map format loader.
+;;
+;;; Code:
+
+(define-module (chickadee graphics tile-map)
+ #:use-module (chickadee)
+ #:use-module (chickadee math matrix)
+ #:use-module (chickadee math rect)
+ #:use-module (chickadee math vector)
+ #:use-module (chickadee graphics blend)
+ #:use-module (chickadee graphics buffer)
+ #:use-module (chickadee graphics color)
+ #:use-module (chickadee graphics engine)
+ #:use-module (chickadee graphics shader)
+ #:use-module (chickadee graphics sprite)
+ #:use-module (chickadee graphics texture)
+ #:use-module (chickadee graphics viewport)
+ #:use-module (chickadee utils)
+ #:use-module (ice-9 match)
+ #:use-module ((rnrs base) #:select (mod))
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-43)
+ #:use-module (sxml simple)
+ #:use-module (sxml xpath)
+ #:export (animation-frame?
+ animation-frame-tile
+ animation-frame-duration
+
+ animation?
+ animation-frames
+ animation-duration
+
+ tile?
+ tile-id
+ tile-texture
+ tile-animation
+ tile-properties
+
+ make-tileset
+ tileset?
+ tileset-name
+ tileset-first-gid
+ tileset-size
+ tileset-tile-width
+ tileset-tile-height
+ tileset-rows
+ tileset-columns
+ tileset-properties
+
+ make-map-tile
+ map-tile?
+ map-tile-ref
+ map-tile-flipped-horizontally?
+ map-tile-flipped-vertically?
+ map-tile-flipped-diagonally?
+
+ make-tile-layer
+ tile-layer?
+ tile-layer-name
+ tile-layer-width
+ tile-layer-height
+ tile-layer-chunk-size
+ tile-layer-properties
+ tile-layer-ref
+ tile-layer-set!
+
+ object-layer?
+ object-layer-name
+ object-layer-objects
+ object-layer-properties
+
+ polygon?
+ polygon-points
+
+ map-object?
+ map-object-id
+ map-object-name
+ map-object-type
+ map-object-shape
+ map-object-properties
+
+ make-tile-map
+ tile-map?
+ tile-map-orientation
+ tile-map-width
+ tile-map-height
+ tile-map-tile-width
+ tile-map-tile-height
+ tile-map-tilesets
+ tile-map-layers
+ tile-map-properties
+ tile-map-rect
+ tile-map-layer-ref
+ point->tile
+ load-tile-map
+ draw-tile-map
+ draw-tile-map*))
+
+
+;;;
+;;; Tileset
+;;;
+
+(define-record-type <animation-frame>
+ (make-animation-frame texture duration)
+ animation-frame?
+ (texture animation-frame-texture)
+ (duration animation-frame-duration))
+
+(define-record-type <animation>
+ (%make-animation frames duration)
+ animation?
+ (frames animation-frames)
+ (duration animation-duration))
+
+(define (make-animation atlas first-gid frame-spec)
+ (let ((frames (map (match-lambda
+ ((id duration)
+ (let ((texture (texture-atlas-ref atlas
+ (- id first-gid))))
+ (make-animation-frame texture duration))))
+ frame-spec)))
+ (%make-animation (list->vector frames)
+ (fold (lambda (frame memo)
+ (+ (animation-frame-duration frame) memo))
+ 0 frames))))
+
+(define (animation-frame-for-time animation time)
+ (let* ((time (mod time (animation-duration animation)))
+ (frames (animation-frames animation)))
+ (let loop ((i 0)
+ (t 0))
+ (let* ((frame (vector-ref frames i))
+ (d (animation-frame-duration frame)))
+ (if (< time (+ t d))
+ frame
+ (loop (+ i 1) (+ t d)))))))
+
+(define-record-type <tile>
+ (make-tile id texture animation properties)
+ tile?
+ (id tile-id)
+ (texture tile-texture)
+ (animation tile-animation)
+ (properties tile-properties))
+
+(define (animated-tile? tile)
+ (animation? (tile-animation tile)))
+
+(define (tile-frame-for-time tile time)
+ (let ((animation (tile-animation tile)))
+ (and animation (animation-frame-for-time animation time))))
+
+(define-record-type <tileset>
+ (%make-tileset name first-gid tile-width tile-height
+ rows columns atlas tiles properties)
+ tileset?
+ (name tileset-name)
+ (first-gid tileset-first-gid)
+ (tile-width tileset-tile-width)
+ (tile-height tileset-tile-height)
+ (rows tileset-rows)
+ (columns tileset-columns)
+ (atlas tileset-atlas)
+ (tiles tileset-tiles)
+ (properties tileset-properties))
+
+(define* (make-tileset texture tile-width tile-height #:key
+ (first-gid 1) (margin 0) (spacing 0)
+ (name "anonymous") (properties '())
+ (custom-tiles '()))
+ (call-with-values (lambda ()
+ (texture-tileset-dimensions texture tile-width tile-height
+ #:margin margin
+ #:spacing spacing))
+ (lambda (columns rows)
+ (let* ((atlas (split-texture texture tile-width tile-height
+ #:margin margin
+ #:spacing spacing))
+ (tiles (make-vector (texture-atlas-size atlas))))
+ (for-range ((i (vector-length tiles)))
+ (let* ((id (+ first-gid i))
+ (custom (or (assv-ref custom-tiles id) '()))
+ (animation (assq-ref custom 'animation))
+ (properties (assq-ref custom 'properties))
+ (tile (make-tile id (texture-atlas-ref atlas i)
+ (and animation (make-animation atlas first-gid animation))
+ (or properties '()))))
+ (vector-set! tiles i tile)))
+ (%make-tileset name first-gid tile-width tile-height rows columns
+ atlas tiles properties)))))
+
+(define (tileset-size tileset)
+ (texture-atlas-size (tileset-atlas tileset)))
+
+(define (tileset-ref tileset i)
+ (vector-ref (tileset-tiles tileset) (- i (tileset-first-gid tileset))))
+
+
+;;;
+;;; Object Layer
+;;;
+
+(define-record-type <object-layer>
+ (%make-object-layer name objects properties)
+ object-layer?
+ (name object-layer-name)
+ (objects object-layer-objects)
+ (properties object-layer-properties))
+
+(define-record-type <polygon>
+ (make-polygon points)
+ polygon?
+ (points polygon-points))
+
+(define-record-type <map-object>
+ (%make-map-object id name type shape properties)
+ map-object?
+ (id map-object-id)
+ (name map-object-name)
+ (type map-object-type)
+ (shape map-object-shape)
+ (properties map-object-properties))
+
+
+;;;
+;;; Chunk
+;;;
+
+(define-record-type <map-tile>
+ (%make-map-tile tile flipped-horizontally? flipped-vertically?
+ flipped-diagonally?)
+ map-tile?
+ (tile map-tile-ref)
+ (flipped-horizontally? map-tile-flipped-horizontally?)
+ (flipped-vertically? map-tile-flipped-vertically?)
+ (flipped-diagonally? map-tile-flipped-diagonally?))
+
+(define* (make-map-tile tile #:key flipped-horizontally?
+ flipped-vertically? flipped-diagonally?)
+ (%make-map-tile tile flipped-horizontally? flipped-vertically?
+ flipped-diagonally?))
+
+(define-record-type <chunk>
+ (%make-chunk size tile-x tile-y tile-width tile-height anim-tiles
+ anim-frames tiles rebuild-geometry? geometry)
+ chunk?
+ (size chunk-size)
+ (tile-x chunk-tile-x)
+ (tile-y chunk-tile-y)
+ (tile-width chunk-tile-width)
+ (tile-height chunk-tile-height)
+ (anim-tiles chunk-anim-tiles)
+ (anim-frames chunk-anim-frames)
+ (tiles chunk-tiles)
+ (rebuild-geometry? chunk-rebuild-geometry? set-chunk-rebuild-geometry!)
+ (geometry chunk-geometry))
+
+(define %default-chunk-size 30)
+
+(define-geometry-type <chunk-vertex>
+ chunk-vertex-ref
+ chunk-vertex-set!
+ chunk-vertex-append!
+ (position vec2)
+ (texture vec2))
+
+(define (make-chunk size tile-x tile-y tile-width tile-height)
+ (let ((tiles (make-vector size)))
+ (for-range ((y size))
+ (let ((row (make-vector size)))
+ (for-range ((x size))
+ (vector-set! row x #f))
+ (vector-set! tiles y row)))
+ (%make-chunk size tile-x tile-y tile-width tile-height
+ (make-hash-table) (make-hash-table) tiles #t
+ (make-hash-table))))
+
+(define (chunk-anim-decrement chunk tile)
+ (let* ((t (chunk-anim-tiles chunk))
+ (n (hashq-ref t tile 0)))
+ (cond
+ ((= n 0)
+ #f)
+ ((= n 1)
+ (hashq-remove! t tile)
+ (hashq-remove! (chunk-anim-frames chunk) tile))
+ (else
+ (hashq-set! t tile (- n 1))))))
+
+(define (chunk-anim-increment chunk tile)
+ (let ((t (chunk-anim-tiles chunk)))
+ (hashq-set! t tile (+ (or (hashq-ref t tile) 0) 1))))
+
+(define (chunk-bounds-check chunk x y)
+ (let ((n (chunk-size chunk)))
+ (unless (and (>= x 0) (>= y 0) (< x n) (< y n))
+ (error "chunk index out of bounds" x y))))
+
+(define (chunk-ref chunk x y)
+ (chunk-bounds-check chunk x y)
+ (vector-ref (vector-ref (chunk-tiles chunk) y) x))
+
+(define (chunk-ensure-geometry chunk texture)
+ (let ((chunk-size (chunk-size chunk))
+ (geometry-table (chunk-geometry chunk)))
+ (unless (hashq-ref geometry-table texture)
+ (let ((g (make-geometry <chunk-vertex>
+ (* chunk-size chunk-size 4)
+ #:index-capacity
+ (* chunk-size chunk-size 6))))
+ (hashq-set! geometry-table texture g)))))
+
+(define (chunk-set! chunk x y tile)
+ (let ((tiles (chunk-tiles chunk)))
+ (chunk-bounds-check chunk x y)
+ (let ((existing-tile (chunk-ref chunk x y)))
+ (when (and existing-tile (animated-tile? (map-tile-ref existing-tile)))
+ (chunk-anim-decrement chunk (map-tile-ref existing-tile))))
+ (vector-set! (vector-ref tiles y) x tile)
+ (when tile
+ (let* ((tile* (map-tile-ref tile))
+ (texture (texture-parent (tile-texture tile*))))
+ (when (animated-tile? tile*)
+ (chunk-anim-increment chunk tile*))
+ (chunk-ensure-geometry chunk texture)))
+ (set-chunk-rebuild-geometry! chunk #t)))
+
+(define (chunk-rebuild-geometry! chunk)
+ (let* ((anim-frames (chunk-anim-frames chunk))
+ (t (chunk-geometry chunk))
+ (n (chunk-size chunk))
+ (tw (exact->inexact (chunk-tile-width chunk)))
+ (th (exact->inexact (chunk-tile-height chunk)))
+ (x0 (* (chunk-tile-x chunk) tw))
+ (y0 (* (chunk-tile-y chunk) th)))
+ (hash-for-each (lambda (texture geometry)
+ (geometry-begin! geometry))
+ t)
+ (for-range ((x n) (y n))
+ (let ((tile (chunk-ref chunk x y)))
+ (when tile
+ (let* ((tile* (map-tile-ref tile))
+ (texture (if (animated-tile? tile*)
+ (animation-frame-texture
+ (hashq-ref anim-frames tile*))
+ (tile-texture tile*)))
+ (geometry (hashq-ref t (texture-parent texture)))
+ (vertex-offset (geometry-vertex-count geometry <chunk-vertex>))
+ (x1 (+ x0 (* x tw)))
+ (y1 (+ y0 (* y th)))
+ (x2 (+ x1 tw))
+ (y2 (+ y1 th))
+ (texcoords (texture-gl-tex-rect texture))
+ (s1 (rect-left texcoords))
+ (t1 (rect-bottom texcoords))
+ (s2 (rect-right texcoords))
+ (t2 (rect-top texcoords))
+ (flip-h? (map-tile-flipped-horizontally? tile))
+ (flip-v? (map-tile-flipped-vertically? tile))
+ (flip-d? (map-tile-flipped-diagonally? tile))
+ (as (cond
+ ((and flip-d? flip-h?) s2)
+ ((and flip-d? flip-v?) s1)
+ (flip-h? s2)
+ (else s1)))
+ (at (cond
+ ((and flip-d? flip-h?) t1)
+ ((and flip-d? flip-v?) t2)
+ (flip-v? t2)
+ (else t1)))
+ (bs (cond
+ ((and flip-d? flip-h?) s2)
+ ((and flip-d? flip-v?) s1)
+ (flip-h? s1)
+ (else s2)))
+ (bt (cond
+ ((and flip-d? flip-h?) t2)
+ ((and flip-d? flip-v?) t1)
+ (flip-v? t2)
+ (else t1)))
+ (cs (cond
+ ((and flip-d? flip-h?) s1)
+ ((and flip-d? flip-v?) s2)
+ (flip-h? s1)
+ (else s2)))
+ (ct (cond
+ ((and flip-d? flip-h?) t2)
+ ((and flip-d? flip-v?) t1)
+ (flip-v? t1)
+ (else t2)))
+ (ds (cond
+ ((and flip-d? flip-h?) s1)
+ ((and flip-d? flip-v?) s2)
+ (flip-h? s2)
+ (else s1)))
+ (dt (cond
+ ((and flip-d? flip-h?) t1)
+ ((and flip-d? flip-v?) t2)
+ (flip-v? t1)
+ (else t2))))
+ (chunk-vertex-append! geometry
+ (x1 y1 as at)
+ (x2 y1 bs bt)
+ (x2 y2 cs ct)
+ (x1 y2 ds dt))
+ (geometry-index-append! geometry
+ vertex-offset
+ (+ vertex-offset 3)
+ (+ vertex-offset 2)
+ vertex-offset
+ (+ vertex-offset 2)
+ (+ vertex-offset 1))))))
+ (hash-for-each (lambda (texture geometry)
+ (geometry-end! geometry))
+ t)))
+
+(define-graphics-variable tile-map-chunk-shader
+ (strings->shader
+ "
+#ifdef GLSL330
+layout (location = 0) in vec2 position;
+layout (location = 1) in vec2 tex;
+#elif defined(GLSL130)
+in vec2 position;
+in vec2 tex;
+#elif defined(GLSL120)
+attribute vec2 position;
+attribute vec2 tex;
+#endif
+#ifdef GLSL120
+varying vec2 fragTex;
+#else
+out vec2 fragTex;
+#endif
+uniform mat4 mvp;
+
+void main(void) {
+ fragTex = tex;
+ gl_Position = mvp * vec4(position.xy, 0.0, 1.0);
+}
+"
+ "
+#ifdef GLSL120
+varying vec2 fragTex;
+#else
+in vec2 fragTex;
+#endif
+#ifdef GLSL330
+out vec4 fragColor;
+#endif
+uniform sampler2D colorTexture;
+uniform vec4 tint;
+
+void main (void) {
+#ifdef GLSL330
+ fragColor = texture(colorTexture, fragTex) * tint;
+#else
+ gl_FragColor = texture2D(colorTexture, fragTex) * tint;
+#endif
+}
+"))
+
+(define (draw-chunk-geometry texture geometry matrix tint)
+ (with-graphics-state ((g:texture-0 texture))
+ (shader-apply* (graphics-variable-ref tile-map-chunk-shader)
+ (geometry-vertex-array geometry)
+ 0
+ (geometry-index-count geometry)
+ #:tint tint
+ #:mvp matrix)))
+
+(define (draw-chunk chunk matrix blend-mode tint time)
+ (let ((anim-tiles (chunk-anim-tiles chunk))
+ (anim-frames (chunk-anim-frames chunk)))
+ (hash-for-each (lambda (tile count)
+ (let ((old-frame (hashq-ref anim-frames tile))
+ (new-frame (tile-frame-for-time tile time)))
+ (unless (eq? old-frame new-frame)
+ (hashq-set! anim-frames tile new-frame)
+ (set-chunk-rebuild-geometry! chunk #t))))
+ anim-tiles))
+ (when (chunk-rebuild-geometry? chunk)
+ (chunk-rebuild-geometry! chunk)
+ (set-chunk-rebuild-geometry! chunk #f))
+ (with-graphics-state ((g:blend-mode blend-mode))
+ (hash-for-each (lambda (texture geometry)
+ (draw-chunk-geometry texture geometry matrix tint))
+ (chunk-geometry chunk))))
+
+
+;;;
+;;; Tile Layer
+;;;
+
+(define-record-type <tile-layer>
+ (%make-tile-layer name width height properties chunk-size chunks)
+ tile-layer?
+ (name tile-layer-name)
+ (width tile-layer-width)
+ (height tile-layer-height)
+ (properties tile-layer-properties)
+ (chunk-size tile-layer-chunk-size)
+ (chunks tile-layer-chunks))
+
+(define* (make-tile-layer width height tile-width tile-height #:key
+ (name "anonymous")
+ (properties '())
+ (chunk-size %default-chunk-size))
+ (let* ((h (ceiling (/ height chunk-size)))
+ (chunks (make-vector h)))
+ (for-range ((y h))
+ (let* ((w (ceiling (/ width chunk-size)))
+ (row (make-vector w)))
+ (for-range ((x w))
+ (vector-set! row x (make-chunk chunk-size
+ (* x chunk-size)
+ (* y chunk-size)
+ tile-width tile-height)))
+ (vector-set! chunks y row)))
+ (%make-tile-layer name width height properties chunk-size chunks)))
+
+(define (tile-layer-bounds-check layer x y)
+ (unless (and (>= x 0) (>= y 0)
+ (< x (tile-layer-width layer))
+ (< y (tile-layer-height layer)))
+ (error "tile layer coordinates out of bounds" layer x y)))
+
+(define (call-with-chunk layer x y proc)
+ (tile-layer-bounds-check layer x y)
+ (let ((n (tile-layer-chunk-size layer)))
+ (proc (vector-ref (vector-ref (tile-layer-chunks layer)
+ (floor (/ y n)))
+ (floor (/ x n)))
+ (modulo x n)
+ (modulo y n))))
+
+(define (tile-layer-ref! layer x y)
+ (call-with-chunk layer x y
+ (lambda (chunk cx cy)
+ (chunk-ref chunk cx cy))))
+
+(define (tile-layer-set! layer x y tile)
+ (call-with-chunk layer x y
+ (lambda (chunk cx cy)
+ (chunk-set! chunk cx cy tile))))
+
+(define (draw-tile-layer layer matrix x1 y1 x2 y2 blend-mode tint time)
+ (let ((width (tile-layer-width layer))
+ (height (tile-layer-height layer))
+ (chunk-size (tile-layer-chunk-size layer))
+ (chunks (tile-layer-chunks layer)))
+ ;; Render only the visible chunks.
+ (for-range ((x (ceiling (/ x2 chunk-size))
+ (floor (/ x1 chunk-size)))
+ (y (ceiling (/ y2 chunk-size))
+ (floor (/ y1 chunk-size))))
+ (draw-chunk (vector-ref (vector-ref chunks y) x)
+ matrix blend-mode tint time))))
+
+
+;;;
+;;; Tile Map
+;;;
+
+(define-record-type <tile-map>
+ (%make-tile-map orientation width height tile-width tile-height
+ tilesets layers properties)
+ tile-map?
+ (orientation tile-map-orientation)
+ (width tile-map-width)
+ (height tile-map-height)
+ (tile-width tile-map-tile-width)
+ (tile-height tile-map-tile-height)
+ (tilesets tile-map-tilesets)
+ (layers tile-map-layers)
+ (properties tile-map-properties))
+
+(define* (make-tile-map width height tile-width tile-height #:key
+ (orientation 'orthogonal) (tilesets '())
+ (layers '()) (properties '()))
+ "Make a tile map that is WIDTH x HEIGHT tiles in size and each tile
+is TILE-WIDTH x TILE-HEIGHT pixels in size. TILESETS is a list of
+tilesets to be associated with the map. LAYERS is a list of object
+and/or tile layers, sorted from bottom to top. PROPERTIES is an alist
+of arbitrary custom data to associate with the map. Currently, only
+the default ORIENTATION value of 'orthogonal' is supported."
+ (unless (eq? orientation 'orthogonal)
+ (error "unsupport tile map orientation" orientation))
+ (%make-tile-map orientation width height tile-width tile-height
+ tilesets (list->vector layers) properties))
+
+(define (tile-map-layer-ref tile-map name)
+ "Return the map layer named NAME."
+ (define (layer-name layer)
+ (if (tile-layer? layer)
+ (tile-layer-name layer)
+ (object-layer-name layer)))
+ (let ((layers (tile-map-layers tile-map)))
+ (let loop ((i 0))
+ (cond
+ ((= i (vector-length layers))
+ #f)
+ ((string=? name (layer-name (vector-ref layers i)))
+ (vector-ref layers i))
+ (else
+ (loop (+ i 1)))))))
+
+(define (point->tile tile-map x y)
+ "Translate the pixel coordinates (X, Y) into tile coordinates."
+ (values (inexact->exact (floor (/ x (tile-map-tile-width tile-map))))
+ (inexact->exact (floor (/ y (tile-map-tile-height tile-map))))))
+
+(define* (load-tile-map file-name #:key (chunk-size %default-chunk-size))
+ "Load the Tiled TMX formatted map in FILE-NAME."
+ (define map-directory
+ (if (absolute-file-name? file-name)
+ (dirname file-name)
+ (string-append (getcwd) "/" (dirname file-name))))
+ (define (scope file-name)
+ (string-append map-directory "/" file-name))
+ (define* (attr node name #:optional (parse identity))
+ (let ((result ((sxpath `(@ ,name *text*)) node)))
+ (if (null? result)
+ #f
+ (parse (car result)))))
+ (define (parse-color-channel s start)
+ (/ (string->number (substring s start (+ start 2)) 16) 255.0))
+ (define (parse-property node)
+ (let ((name (attr node 'name string->symbol))
+ (type (or (attr node 'type string->symbol) 'string))
+ (value (attr node 'value)))
+ (cons name
+ (match type
+ ((or 'string 'file) value)
+ ('bool (not (string=? value "false")))
+ ((or 'int 'float) (string->number value))
+ ('color
+ (make-color (parse-color-channel value 3)
+ (parse-color-channel value 5)
+ (parse-color-channel value 7)
+ (parse-color-channel value 1)))
+ (_ (error "unsupported property type" type))))))
+ (define (parse-image node)
+ (let ((source (attr node 'source))
+ (trans (attr node 'trans)))
+ (load-image (scope source)
+ #:transparent-color (and trans (string->color trans)))))
+ (define (invert-tile-id id first-gid rows columns)
+ (let ((id* (- id first-gid)))
+ (+ (* (- rows (floor (/ id* columns)) 1)
+ columns)
+ (modulo id* columns)
+ first-gid)))
+ (define (parse-frame node first-gid rows columns)
+ (let ((tile-id (attr node 'tileid string->number))
+ (duration (attr node 'duration string->number)))
+ (list (+ first-gid (invert-tile-id tile-id 0 rows columns))
+ (/ duration 1000.0))))
+ (define (parse-tiles nodes first-gid rows columns)
+ (let ((frames (sxpath '(animation frame)))
+ (properties (sxpath '(properties property))))
+ (fold (lambda (node memo)
+ (let ((id (+ first-gid
+ (invert-tile-id (attr node 'id string->number)
+ 0 rows columns))))
+ (cons `(,id . ((animation . ,(map (lambda (f)
+ (parse-frame f first-gid
+ rows columns))
+ (frames node)))
+ (properties . ,(map parse-property
+ (properties node)))))
+ memo)))
+ '()
+ nodes)))
+ (define (first-gid node)
+ (attr node 'firstgid string->number))
+ (define (parse-tileset node first-gid)
+ (let* ((name (attr node 'name))
+ (tile-width (attr node 'tilewidth string->number))
+ (tile-height (attr node 'tileheight string->number))
+ (margin (or (attr node 'margin string->number) 0))
+ (spacing (or (attr node 'spacing string->number) 0))
+ (texture (parse-image ((sxpath '(image)) node)))
+ (tiles (call-with-values
+ (lambda ()
+ (texture-tileset-dimensions texture
+ tile-width
+ tile-height
+ #:margin margin
+ #:spacing spacing))
+ (lambda (columns rows)
+ (parse-tiles ((sxpath '(tile)) node) first-gid rows columns))))
+ (properties (map parse-property
+ ((sxpath '(properties property)) node))))
+ (make-tileset texture tile-width tile-height
+ #:margin margin
+ #:spacing spacing
+ #:name name
+ #:first-gid first-gid
+ #:properties properties
+ #:custom-tiles tiles)))
+ (define (parse-external-tileset node)
+ (let* ((first-gid (attr node 'firstgid string->number))
+ (source (scope (attr node 'source)))
+ (tree (call-with-input-file source xml->sxml)))
+ (parse-tileset (car ((sxpath '(tileset)) tree)) first-gid)))
+ (define (parse-tileset* node)
+ (if (attr node 'source)
+ (parse-external-tileset node)
+ (parse-tileset node (first-gid node))))
+ (define (tile-gid->map-tile raw-gid tilesets x y tile-width tile-height)
+ ;; The top 3 bits of the tile gid are flags for various types of
+ ;; flipping.
+ (let* ((flipped-horizontally? (> (logand raw-gid #x80000000) 0))
+ (flipped-vertically? (> (logand raw-gid #x40000000) 0))
+ (flipped-diagonally? (> (logand raw-gid #x20000000) 0))
+ ;; Remove the upper 3 bits to get the true tile id.
+ (gid (logand raw-gid #x1FFFFFFF))
+ (tileset (find (lambda (t)
+ (and (>= gid (tileset-first-gid t))
+ (< gid (+ (tileset-first-gid t)
+ (tileset-size t)))))
+ tilesets))
+ (tw (tileset-tile-width tileset))
+ (th (tileset-tile-height tileset))
+ (first-gid (tileset-first-gid tileset))
+ (rows (tileset-rows tileset))
+ (columns (tileset-columns tileset))
+ (id (invert-tile-id gid first-gid rows columns)))
+ (make-map-tile (tileset-ref tileset id)
+ #:flipped-horizontally? flipped-horizontally?
+ #:flipped-vertically? flipped-vertically?
+ #:flipped-diagonally? flipped-diagonally?)))
+ (define (tile-gids->map-tiles gids width height tilesets)
+ (let ((tiles (make-vector (* width height))))
+ (let y-loop ((y 0)
+ (rows (reverse gids))) ; invert y
+ (when (< y height)
+ (match rows
+ ((row . rest)
+ (let x-loop ((x 0)
+ (columns row))
+ (when (< x width)
+ (match columns
+ ((gid . rest)
+ (vector-set! tiles
+ (+ (* width y) x)
+ (if (zero? gid)
+ #f
+ (tile-gid->map-tile gid tilesets
+ x y width height)))
+ (x-loop (+ x 1) rest)))))
+ (y-loop (+ y 1) rest)))))
+ tiles))
+ (define (parse-csv lines width height tilesets)
+ (let ((gids (map (lambda (line)
+ (filter-map (lambda (s)
+ (and (not (string-null? s))
+ (string->number s)))
+ (string-split line #\,)))
+ (take (drop (string-split lines #\newline) 1) height))))
+ (tile-gids->map-tiles gids width height tilesets)))
+ (define (parse-layer-data node width height tilesets)
+ (let ((encoding (attr node 'encoding string->symbol))
+ (data (car ((sxpath '(*text*)) node))))
+ (match encoding
+ ('csv (parse-csv data width height tilesets))
+ (_ (error "unsupported tile layer encoding" encoding)))))
+ (define (parse-tile-layer node tile-width tile-height tilesets)
+ (let* ((name (attr node 'name))
+ (width (attr node 'width string->number))
+ (height (attr node 'height string->number))
+ (tiles (parse-layer-data ((sxpath '(data)) node)
+ width height tilesets))
+ (properties (map parse-property
+ ((sxpath '(properties property)) node)))
+ (layer (make-tile-layer width height tile-width tile-height
+ #:name name
+ #:properties properties
+ #:chunk-size chunk-size)))
+ (for-range ((x width)
+ (y height))
+ (tile-layer-set! layer x y (vector-ref tiles (+ (* y width) x))))
+ layer))
+ (define (parse-polygon node pixel-height)
+ (make-polygon
+ (list->vector
+ (map (lambda (s)
+ (match (string-split s #\,)
+ ((x y)
+ (vec2 (string->number x)
+ (- pixel-height (string->number y))))))
+ (string-split (attr node 'points) #\space)))))
+ (define (parse-object node pixel-height)
+ (let* ((id (attr node 'id string->number))
+ (name (attr node 'name))
+ (type (attr node 'type string->symbol))
+ (x (attr node 'x string->number))
+ (y (- pixel-height (attr node 'y string->number)))
+ (width (attr node 'width string->number))
+ (height (attr node 'height string->number))
+ (shape (if (and width height)
+ (make-rect x y width height)
+ (parse-polygon (car ((sxpath '(polygon)) node))
+ pixel-height)))
+ (properties (map parse-property
+ ((sxpath '(properties property)) node))))
+ (%make-map-object id name type shape properties)))
+ (define (parse-object-layer node pixel-height)
+ (let ((name (attr node 'name))
+ (objects (map (lambda (node)
+ (parse-object node pixel-height))
+ ((sxpath '(object)) node)))
+ (properties (map parse-property
+ ((sxpath '(properties property)) node))))
+ (%make-object-layer name objects properties)))
+ (let* ((tree (call-with-input-file file-name xml->sxml))
+ (m ((sxpath '(map)) tree))
+ (version (attr m 'version))
+ (orientation (attr m 'orientation string->symbol))
+ (width (attr m 'width string->number))
+ (height (attr m 'height string->number))
+ (tile-width (attr m 'tilewidth string->number))
+ (tile-height (attr m 'tileheight string->number))
+ (properties ((sxpath '(map properties property)) tree))
+ (tilesets (map parse-tileset* ((sxpath '(map tileset)) tree)))
+ (layers ((node-or (sxpath '(map layer))
+ (sxpath '(map objectgroup)))
+ tree)))
+ (make-tile-map width height tile-width tile-height
+ #:orientation orientation
+ #:tilesets tilesets
+ #:layers (map (lambda (node)
+ (match node
+ (('layer . _)
+ (parse-tile-layer node tile-width tile-height tilesets))
+ (('objectgroup . _)
+ (parse-object-layer node (* height tile-height)))))
+ layers)
+ #:properties (map parse-property properties))))
+
+(define* (draw-tile-map* tile-map matrix region #:key
+ layers (time (elapsed-time))
+ (blend-mode blend:alpha) (tint white))
+ ;; Calculate the tiles that are visible so we don't waste time
+ ;; drawing unnecessary sprites.
+ (let* ((w (tile-map-width tile-map))
+ (h (tile-map-height tile-map))
+ (tw (tile-map-tile-width tile-map))
+ (th (tile-map-tile-height tile-map))
+ (rx (rect-x region))
+ (ry (rect-y region))
+ (rw (rect-width region))
+ (rh (rect-height region))
+ (x1 (max (inexact->exact (floor (/ rx tw))) 0))
+ (y1 (max (inexact->exact (floor (/ ry th))) 0))
+ (x2 (min (inexact->exact (ceiling (/ (+ rx rw) tw))) w))
+ (y2 (min (inexact->exact (ceiling (/ (+ ry rh) th))) h)))
+ (vector-for-each (lambda (i layer)
+ (when (and (tile-layer? layer)
+ (or (not layers)
+ (memv i layers)))
+ (draw-tile-layer layer matrix x1 y1 x2 y2 blend-mode tint time)))
+ (tile-map-layers tile-map))))
+
+(define %null-vec2 (vec2 0.0 0.0))
+(define %default-scale (vec2 1.0 1.0))
+(define *matrix* (make-null-matrix4))
+(define *position* (vec2 0.0 0.0))
+(define *region* (make-rect 0.0 0.0 0.0 0.0))
+
+(define* (draw-tile-map tile-map
+ #:key
+ layers
+ (camera %null-vec2)
+ (position %null-vec2)
+ (origin %null-vec2)
+ (scale %default-scale)
+ (rotation 0.0)
+ (blend-mode blend:alpha)
+ (tint white)
+ (time (elapsed-time)))
+ "Draw TILE-MAP. By default, all layers are drawn. The LAYERS
+argument may be used to specify a list of layers to draw, instead."
+ ;; Make the region as big as the current viewport.
+ (let ((vp (current-viewport)))
+ (set-rect-x! *region* (vec2-x camera))
+ (set-rect-y! *region* (vec2-y camera))
+ (set-rect-width! *region* (viewport-width vp))
+ (set-rect-height! *region* (viewport-height vp)))
+ ;; Translation must be adjusted by inverse of camera.
+ (vec2-copy! camera *position*)
+ (vec2-mult! *position* -1.0)
+ (vec2-add! *position* position)
+ (matrix4-2d-transform! *matrix*
+ #:origin origin
+ #:position *position*
+ #:rotation rotation
+ #:scale scale)
+ (matrix4-mult! *matrix* *matrix* (current-projection))
+ (draw-tile-map* tile-map *matrix* *region*
+ #:layers layers
+ #:blend-mode blend-mode
+ #:tint tint
+ #:time time))
diff --git a/chickadee/graphics/tiled.scm b/chickadee/graphics/tiled.scm
deleted file mode 100644
index 2692418..0000000
--- a/chickadee/graphics/tiled.scm
+++ /dev/null
@@ -1,500 +0,0 @@
-;;; Chickadee Game Toolkit
-;;; Copyright © 2018, 2020, 2021 David Thompson <davet@gnu.org>
-;;;
-;;; Chickadee is free software: you can redistribute it and/or modify
-;;; it under the terms of the GNU General Public License as published
-;;; by the Free Software Foundation, either version 3 of the License,
-;;; or (at your option) any later version.
-;;;
-;;; Chickadee is distributed in the hope that it will be useful, but
-;;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-;;; General Public License for more details.
-;;;
-;;; You should have received a copy of the GNU General Public License
-;;; along with this program. If not, see
-;;; <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-;;
-;; Tiled map format parser and renderer.
-;;
-;;; Code:
-
-(define-module (chickadee graphics tiled)
- #:use-module (chickadee math matrix)
- #:use-module (chickadee math rect)
- #:use-module (chickadee math vector)
- #:use-module (chickadee graphics color)
- #:use-module (chickadee graphics sprite)
- #:use-module (chickadee graphics texture)
- #:use-module (chickadee graphics viewport)
- #:use-module (chickadee utils)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-43)
- #:use-module (sxml simple)
- #:use-module (sxml xpath)
- #:export (tile-map?
- tile-map-orientation
- tile-map-width
- tile-map-height
- tile-map-tile-width
- tile-map-tile-height
- tile-map-tilesets
- tile-map-layers
- tile-map-properties
- tile-map-rect
- tile-map-layer-ref
- point->tile
-
- animation-frame?
- animation-frame-tile
- animation-frame-duration
-
- tile?
- tile-id
- tile-animation
- tile-properties
-
- tileset?
- tileset-name
- tileset-first-gid
- tileset-size
- tileset-tile-width
- tileset-tile-height
- tileset-atlas
- tileset-tiles
- tileset-properties
-
- map-tile?
- map-tile-ref
- map-tile-rect
-
- tile-layer?
- tile-layer-name
- tile-layer-width
- tile-layer-height
- tile-layer-tiles
- tile-layer-properties
-
- object-layer?
- object-layer-name
- object-layer-objects
- object-layer-properties
-
- polygon?
- polygon-points
-
- map-object?
- map-object-id
- map-object-name
- map-object-type
- map-object-shape
- map-object-properties
-
- load-tile-map
- draw-tile-map
- draw-tile-map*))
-
-(define-record-type <tile-map>
- (%make-tile-map orientation width height tile-width tile-height
- tilesets layers properties rect)
- tile-map?
- (orientation tile-map-orientation)
- (width tile-map-width)
- (height tile-map-height)
- (tile-width tile-map-tile-width)
- (tile-height tile-map-tile-height)
- (tilesets tile-map-tilesets)
- (layers tile-map-layers)
- (properties tile-map-properties)
- (rect tile-map-rect))
-
-(define-record-type <animation-frame>
- (%make-animation-frame tile duration)
- animation-frame?
- (tile animation-frame-tile)
- (duration animation-frame-duration))
-
-(define-record-type <tile>
- (%make-tile id texture batch animation properties)
- tile?
- (id tile-id)
- (texture tile-texture)
- (batch tile-batch)
- (animation tile-animation)
- (properties tile-properties))
-
-(define-record-type <tileset>
- (%make-tileset name first-gid size tile-width tile-height
- atlas tiles properties batch)
- tileset?
- (name tileset-name)
- (first-gid tileset-first-gid)
- (size tileset-size)
- (tile-width tileset-tile-width)
- (tile-height tileset-tile-height)
- (atlas tileset-atlas)
- (tiles tileset-tiles)
- (properties tileset-properties)
- (batch tileset-batch))
-
-(define-record-type <map-tile>
- (%make-map-tile tile rect)
- map-tile?
- (tile map-tile-ref)
- (rect map-tile-rect))
-
-(define-record-type <tile-layer>
- (%make-tile-layer name width height tiles properties)
- tile-layer?
- (name tile-layer-name)
- (width tile-layer-width)
- (height tile-layer-height)
- (tiles tile-layer-tiles)
- (properties tile-layer-properties))
-
-(define-record-type <object-layer>
- (%make-object-layer name objects properties)
- object-layer?
- (name object-layer-name)
- (objects object-layer-objects)
- (properties object-layer-properties))
-
-;; TODO: This should probably be a generic thing that we can use
-;; outside of tiled maps.
-(define-record-type <polygon>
- (make-polygon points)
- polygon?
- (points polygon-points))
-
-(define-record-type <map-object>
- (%make-map-object id name type shape properties)
- map-object?
- (id map-object-id)
- (name map-object-name)
- (type map-object-type)
- (shape map-object-shape)
- (properties map-object-properties))
-
-(define (tile-map-layer-ref tile-map name)
- "Return the layer named NAME."
- (define (layer-name layer)
- (if (tile-layer? layer)
- (tile-layer-name layer)
- (object-layer-name layer)))
- (let ((layers (tile-map-layers tile-map)))
- (let loop ((i 0))
- (cond
- ((= i (vector-length layers))
- #f)
- ((string=? name (layer-name (vector-ref layers i)))
- (vector-ref layers i))
- (else
- (loop (+ i 1)))))))
-
-(define (point->tile tile-map x y)
- "Translate the pixel coordinates (X, Y) into tile coordinates."
- (values (inexact->exact (floor (/ x (tile-map-tile-width tile-map))))
- (inexact->exact (floor (/ y (tile-map-tile-height tile-map))))))
-
-(define (load-tile-map file-name)
- "Load the Tiled TMX formatted map in FILE-NAME."
- (define map-directory
- (if (absolute-file-name? file-name)
- (dirname file-name)
- (string-append (getcwd) "/" (dirname file-name))))
- (define (scope file-name)
- (string-append map-directory "/" file-name))
- (define* (attr node name #:optional (parse identity))
- (let ((result ((sxpath `(@ ,name *text*)) node)))
- (if (null? result)
- #f
- (parse (car result)))))
- (define (parse-color-channel s start)
- (/ (string->number (substring s start (+ start 2)) 16) 255.0))
- (define (parse-property node)
- (let ((name (attr node 'name string->symbol))
- (type (or (attr node 'type string->symbol) 'string))
- (value (attr node 'value)))
- (cons name
- (match type
- ((or 'string 'file) value)
- ('bool (not (string=? value "false")))
- ((or 'int 'float) (string->number value))
- ('color
- (make-color (parse-color-channel value 3)
- (parse-color-channel value 5)
- (parse-color-channel value 7)
- (parse-color-channel value 1)))
- (_ (error "unsupported property type" type))))))
- (define (parse-image node)
- (let ((source (attr node 'source))
- (trans (attr node 'trans)))
- (load-image (scope source)
- #:transparent-color (and trans (string->color trans)))))
- (define (parse-frame node)
- (let ((tile-id (attr node 'tileid string->number))
- (duration (attr node 'duration string->number)))
- ;; TODO: lookup actual tile in tileset
- (%make-animation-frame tile-id duration)))
- (define (atlas-ref atlas id rows columns)
- ;; Tiled enumerates tiles from the top-left of the tileset image,
- ;; but here in OpenGL land the origin is in the bottom-left, so we
- ;; have to do some math invert the rows.
- (texture-atlas-ref atlas
- (+ (* (- rows (floor (/ id columns)) 1)
- columns)
- (modulo id columns))))
- (define (parse-tile node rows columns atlas batch)
- (let ((id (attr node 'id string->number))
- (animation (map parse-frame ((sxpath '(animation frame)) node)))
- (properties (map parse-property
- ((sxpath '(properties property)) node))))
- (%make-tile id (atlas-ref atlas id rows columns)
- batch animation properties)))
- (define (parse-tiles nodes size columns atlas batch)
- (let ((table (make-hash-table))
- (tiles (make-vector size))
- (rows (/ size columns)))
- (for-each (lambda (node)
- (let ((tile (parse-tile node rows columns atlas batch)))
- (hash-set! table (tile-id tile) tile)))
- nodes)
- (for-range ((i size))
- (let ((tile
- (or (hash-ref table i)
- (%make-tile i (atlas-ref atlas i rows columns)
- batch #f '()))))
- (vector-set! tiles i tile)))
- tiles))
- (define (first-gid node)
- (attr node 'firstgid string->number))
- (define (parse-internal-tileset node first-gid)
- (let* ((name (attr node 'name))
- (tile-width (attr node 'tilewidth string->number))
- (tile-height (attr node 'tileheight string->number))
- (margin (or (attr node 'margin string->number) 0))
- (spacing (or (attr node 'spacing string->number) 0))
- (columns (attr node 'columns string->number))
- (size (attr node 'tilecount string->number))
- (texture (parse-image ((sxpath '(image)) node)))
- (atlas (split-texture texture tile-width tile-height
- #:margin margin #:spacing spacing))
- (batch (make-sprite-batch texture))
- (tiles (parse-tiles ((sxpath '(tile)) node) size columns atlas batch))
- (properties (map parse-property
- ((sxpath '(properties property)) node))))
- (%make-tileset name first-gid size tile-width tile-height
- atlas tiles properties batch)))
- (define (parse-external-tileset node)
- (let* ((first-gid (attr node 'firstgid string->number))
- (source (scope (attr node 'source)))
- (tree (call-with-input-file source xml->sxml)))
- (parse-internal-tileset (car ((sxpath '(tileset)) tree)) first-gid)))
- (define (parse-tileset node)
- (if (attr node 'source)
- (parse-external-tileset node)
- (parse-internal-tileset node (first-gid node))))
- (define (tile-gid->map-tile raw-gid tilesets x y tile-width tile-height)
- ;; The top 3 bits of the tile gid are flags for various types of
- ;; flipping.
- ;;
- ;; TODO: Respect the flipping settings.
- (let* ((flipped-horizontally? (> (logand raw-gid #x80000000) 0))
- (flipped-vertically? (> (logand raw-gid #x40000000) 0))
- (flipped-diagonally? (> (logand raw-gid #x20000000) 0))
- ;; Remove the upper 3 bits to get the true tile id.
- (gid (logand raw-gid #x1FFFFFFF))
- (tileset (find (lambda (t)
- (and (>= gid (tileset-first-gid t))
- (< gid (+ (tileset-first-gid t)
- (tileset-size t)))))
- tilesets))
- (tw (tileset-tile-width tileset))
- (th (tileset-tile-height tileset)))
- (%make-map-tile (vector-ref (tileset-tiles tileset)
- (- gid (tileset-first-gid tileset)))
- (make-rect (* x tw) (* y th) tw th))))
- (define (tile-gids->map-tiles gids width height tilesets)
- (let ((tiles (make-vector (* width height))))
- (let y-loop ((y 0)
- (rows (reverse gids))) ; invert y
- (when (< y height)
- (match rows
- ((row . rest)
- (let x-loop ((x 0)
- (columns row))
- (when (< x width)
- (match columns
- ((gid . rest)
- (vector-set! tiles
- (+ (* width y) x)
- (if (zero? gid)
- #f
- (tile-gid->map-tile gid tilesets
- x y width height)))
- (x-loop (+ x 1) rest)))))
- (y-loop (+ y 1) rest)))))
- tiles))
- (define (parse-csv lines width height tilesets)
- (let ((gids (map (lambda (line)
- (filter-map (lambda (s)
- (and (not (string-null? s))
- (string->number s)))
- (string-split line #\,)))
- (take (drop (string-split lines #\newline) 1) height))))
- (tile-gids->map-tiles gids width height tilesets)))
- (define (parse-layer-data node width height tilesets)
- (let ((encoding (attr node 'encoding string->symbol))
- (data (car ((sxpath '(*text*)) node))))
- (match encoding
- ('csv (parse-csv data width height tilesets))
- (_ (error "unsupported tile layer encoding" encoding)))))
- (define (parse-tile-layer node tilesets)
- (let* ((name (attr node 'name))
- (width (attr node 'width string->number))
- (height (attr node 'height string->number))
- (tiles (parse-layer-data ((sxpath '(data)) node)
- width height tilesets))
- (properties (map parse-property
- ((sxpath '(properties property)) node))))
- (%make-tile-layer name width height tiles properties)))
- (define (parse-polygon node pixel-height)
- (make-polygon
- (list->vector
- (map (lambda (s)
- (match (string-split s #\,)
- ((x y)
- (vec2 (string->number x)
- (- pixel-height (string->number y))))))
- (string-split (attr node 'points) #\space)))))
- (define (parse-object node pixel-height)
- (let* ((id (attr node 'id string->number))
- (name (attr node 'name))
- (type (attr node 'type string->symbol))
- (x (attr node 'x string->number))
- (y (- pixel-height (attr node 'y string->number)))
- (width (attr node 'width string->number))
- (height (attr node 'height string->number))
- (shape (if (and width height)
- (make-rect x y width height)
- (parse-polygon (car ((sxpath '(polygon)) node))
- pixel-height)))
- (properties (map parse-property
- ((sxpath '(properties property)) node))))
- (%make-map-object id name type shape properties)))
- (define (parse-object-layer node pixel-height)
- (let ((name (attr node 'name))
- (objects (map (lambda (node)
- (parse-object node pixel-height))
- ((sxpath '(object)) node)))
- (properties (map parse-property
- ((sxpath '(properties property)) node))))
- (%make-object-layer name objects properties)))
- (let* ((tree (call-with-input-file file-name xml->sxml))
- (m ((sxpath '(map)) tree))
- (version (attr m 'version))
- (orientation (attr m 'orientation string->symbol))
- (width (attr m 'width string->number))
- (height (attr m 'height string->number))
- (tile-width (attr m 'tilewidth string->number))
- (tile-height (attr m 'tileheight string->number))
- (properties ((sxpath '(map properties property)) tree))
- (tilesets (map parse-tileset ((sxpath '(map tileset)) tree)))
- (layers ((node-or (sxpath '(map layer))
- (sxpath '(map objectgroup)))
- tree)))
- (%make-tile-map orientation width height tile-width tile-height
- tilesets
- (list->vector
- (map (lambda (node)
- (match node
- (('layer . _)
- (parse-tile-layer node tilesets))
- (('objectgroup . _)
- (parse-object-layer node (* height tile-height)))))
- layers))
- (map parse-property properties)
- (make-rect 0.0
- 0.0
- (* width tile-width)
- (* height tile-height)))))
-
-
-(define (draw-tile-layer layer matrix x1 y1 x2 y2)
- (let ((width (tile-layer-width layer))
- (height (tile-layer-height layer)))
- (for-range ((x x2 x1)
- (y y2 y1))
- (let ((tile (vector-ref (tile-layer-tiles layer)
- (+ (* y width) x))))
- (when tile
- (let ((tref (map-tile-ref tile)))
- (sprite-batch-add* (tile-batch tref)
- (map-tile-rect tile)
- matrix
- #:texture-region (tile-texture tref))))))))
-
-(define* (draw-tile-map* tile-map matrix region #:key layers)
- ;; Calculate the tiles that are visible so we don't waste time
- ;; drawing unnecessary sprites.
- (let* ((w (tile-map-width tile-map))
- (h (tile-map-height tile-map))
- (tw (tile-map-tile-width tile-map))
- (th (tile-map-tile-height tile-map))
- (rx (rect-x region))
- (ry (rect-y region))
- (rw (rect-width region))
- (rh (rect-height region))
- (x1 (max (inexact->exact (floor (/ rx tw))) 0))
- (y1 (max (inexact->exact (floor (/ ry th))) 0))
- (x2 (min (inexact->exact (ceiling (/ (+ rx rw) tw))) w))
- (y2 (min (inexact->exact (ceiling (/ (+ ry rh) th))) h)))
- (vector-for-each (lambda (i layer)
- (when (and (tile-layer? layer)
- (or (not layers)
- (memv i layers)))
- (for-each (lambda (tileset)
- (sprite-batch-clear! (tileset-batch tileset)))
- (tile-map-tilesets tile-map))
- (draw-tile-layer layer matrix x1 y1 x2 y2)
- (for-each (lambda (tileset)
- (draw-sprite-batch (tileset-batch tileset)))
- (tile-map-tilesets tile-map))))
- (tile-map-layers tile-map))))
-
-(define %null-vec2 (vec2 0.0 0.0))
-(define %default-scale (vec2 1.0 1.0))
-(define *matrix* (make-null-matrix4))
-(define *position* (vec2 0.0 0.0))
-(define *region* (make-rect 0.0 0.0 0.0 0.0))
-
-(define* (draw-tile-map tile-map
- #:key
- layers
- (camera %null-vec2)
- (position %null-vec2)
- (origin %null-vec2)
- (scale %default-scale)
- (rotation 0.0))
- "Draw TILE-MAP. By default, all layers are drawn. The LAYERS
-argument may be used to specify a list of layers to draw, instead."
- ;; Make the region as big as the current viewport.
- (let ((vp (current-viewport)))
- (set-rect-x! *region* (vec2-x camera))
- (set-rect-y! *region* (vec2-y camera))
- (set-rect-width! *region* (viewport-width vp))
- (set-rect-height! *region* (viewport-height vp)))
- ;; Translation must be adjusted by inverse of camera.
- (vec2-copy! camera *position*)
- (vec2-mult! *position* -1.0)
- (vec2-add! *position* position)
- (matrix4-2d-transform! *matrix*
- #:origin origin
- #:position *position*
- #:rotation rotation
- #:scale scale)
- (draw-tile-map* tile-map *matrix* *region* #:layers layers))
diff --git a/doc/api.texi b/doc/api.texi
index e99c89c..28a5553 100644
--- a/doc/api.texi
+++ b/doc/api.texi
@@ -2532,7 +2532,7 @@ A tile map is a scene created by composing lots of small sprites,
called ``tiles'', into a larger image. One program for editing such
maps is called @url{http://mapeditor.org,Tiled}. Chickadee has native
support for loading and rendering Tiled maps in the @code{(chickadee
-graphics tiled)} module.
+graphics tile-map)} module.
@deffn {Procedure} load-tile-map file-name
Load the Tiled formatted map in @var{file-name} and return a new tile
@@ -2581,7 +2581,8 @@ coordinates.
@end deffn
@deffn {Procedure} draw-tile-map tile-map [#:layers] [#:camera] @
- [#:origin] [#:position] [#:scale] [#:rotation]
+ [#:origin] [#:position] [#:scale] [#:rotation] [#:blend-mode] @
+ [#:tint] [#:time] [#:chunk-size]
Draw the layers of @var{tile-map} as viewed from @var{camera}, a 2D
vector offset. By default, all layers are drawn. To draw a subset of
@@ -2621,6 +2622,14 @@ Return the texture atlas for @var{tileset}.
Return the tiles in @var{tileset}.
@end deffn
+@deffn {Procedure} tileset-rows tileset
+Return the number of rows in @var{tileset}.
+@end deffn
+
+@deffn {Procedure} tileset-columns tileset
+Return the number of columns in @var{tileset}.
+@end deffn
+
@deffn {Procedure} tileset-properties tileset
Return the custom properties of @var{tileset}.
@end deffn
@@ -2641,6 +2650,18 @@ Return the animation for @var{tile}.
Return the custom properties of @var{tile}.
@end deffn
+@deffn {Procedure} animation? obj
+Return @code{#t} if @var{obj} is an animation.
+@end deffn
+
+@deffn {Procedure} animation-frames animation
+Return a vector of frames in @var{animation}.
+@end deffn
+
+@deffn {Procedure} animation-duration animation
+Return the duration of @var{animation}.
+@end deffn
+
@deffn {Procedure} animation-frame? obj
Return @code{#t} if @var{obj} is an animation frame.
@end deffn
diff --git a/examples/images/serene-village.png b/examples/images/serene-village.png
new file mode 100644
index 0000000..d94cb31
--- /dev/null
+++ b/examples/images/serene-village.png
Binary files differ
diff --git a/examples/images/tiles.png b/examples/images/tiles.png
deleted file mode 100644
index d8a3dfd..0000000
--- a/examples/images/tiles.png
+++ /dev/null
Binary files differ
diff --git a/examples/maps/example.tmx b/examples/maps/example.tmx
index 6c8bc3d..4a3a77a 100644
--- a/examples/maps/example.tmx
+++ b/examples/maps/example.tmx
@@ -1,96 +1,274 @@
<?xml version="1.0" encoding="UTF-8"?>
-<map version="1.0" tiledversion="1.1.0" orientation="orthogonal" renderorder="right-up" width="20" height="15" tilewidth="16" tileheight="16" infinite="0" nextobjectid="1">
- <tileset firstgid="1" name="tiles" tilewidth="16" tileheight="16" tilecount="1440" columns="40">
- <image source="../images/tiles.png" width="640" height="576"/>
- <terraintypes>
- <terrain name="grass" tile="0"/>
- <terrain name="dark-grass" tile="1"/>
- <terrain name="tree" tile="1"/>
- <terrain name="nothing" tile="1"/>
- </terraintypes>
- <tile id="0" terrain="0,0,0,0"/>
- <tile id="360" terrain="0,0,0,1"/>
- <tile id="361" terrain="0,0,1,0"/>
- <tile id="400" terrain="0,1,0,0"/>
- <tile id="401" terrain="1,0,0,0"/>
- <tile id="642" terrain="2,2,2,2"/>
- <tile id="643" terrain="3,2,2,2"/>
- <tile id="644" terrain="2,3,2,2"/>
- <tile id="645" terrain="3,3,3,2"/>
- <tile id="646" terrain="3,3,2,3"/>
- <tile id="648" terrain="3,3,3,3"/>
- <tile id="685" terrain="3,2,3,3"/>
- <tile id="686" terrain="2,3,3,3"/>
- <tile id="725" terrain="3,3,2,2"/>
- <tile id="1174" terrain="0,0,0,1"/>
- <tile id="1175" terrain="0,0,1,1"/>
- <tile id="1176" terrain="0,0,1,0"/>
- <tile id="1214" terrain="0,1,0,1"/>
- <tile id="1215" terrain="1,1,1,1"/>
- <tile id="1216" terrain="1,0,1,0"/>
- <tile id="1254" terrain="0,1,0,0"/>
- <tile id="1255" terrain="1,1,0,0"/>
- <tile id="1256" terrain="1,0,0,0"/>
- <tile id="1294" terrain="1,1,1,0"/>
- <tile id="1295" terrain="1,1,0,1"/>
- <tile id="1334" terrain="1,0,1,1"/>
- <tile id="1335" terrain="0,1,1,1"/>
- </tileset>
- <layer name="background" width="20" height="15">
+<map version="1.5" tiledversion="1.5.0" orientation="orthogonal" renderorder="right-down" width="50" height="50" tilewidth="32" tileheight="32" infinite="0" nextlayerid="6" nextobjectid="1">
+ <tileset firstgid="1" source="serene-village.tsx"/>
+ <layer id="1" name="Water" width="50" height="50">
<data encoding="csv">
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1175,1176,1176,1177,1,
-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1215,1216,1216,1217,1,
-1,1,1,1,1,1175,1176,1177,1,1,1,1,1,1,1,1215,1216,1216,1217,1,
-1,1,1,1175,1176,1336,1216,1335,1177,1,1,1,1,1,1,1255,1296,1216,1217,1,
-1,1,1,1215,1216,1216,1216,1216,1335,1177,1,1,1,1,1,1,1215,1216,1217,1,
-1,1,1,1215,1216,1216,1216,1216,1216,1335,1176,1176,1176,1176,1177,1,1255,1256,1257,1,
-1,1,1,1215,1216,1216,1216,1295,1296,1216,1216,1216,1216,1216,1217,1,1,1,1,1,
-1,1,1,1255,1296,1216,1216,1217,1255,1256,1296,1216,1295,1256,1257,1,1,1,1,1,
-1,1,1,1,1255,1256,1256,1257,1,367,1255,1256,1257,365,1,1,1,1,1,1,
-446,446,446,446,446,446,446,446,446,447,212,253,253,445,446,446,446,446,446,446,
-486,486,486,486,486,486,486,486,486,527,252,213,213,525,486,486,486,486,486,526,
-566,566,566,566,566,566,566,566,566,567,252,253,253,565,566,566,566,566,566,566,
-1,1,1,1,1,1,1,1,1,1,1175,1176,1177,1,1,1,1,1,1,1,
-1,1,1,1,1,1,1,1,1,1,1215,1216,1335,1177,1,1,1,1,1,1
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,0,0,0,0,0,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,0,0,0,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,0,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,0,0,0,0,0,0,0,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,
+210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210,210
</data>
</layer>
- <layer name="overlay" width="20" height="15">
+ <layer id="2" name="Land" width="50" height="50">
<data encoding="csv">
-685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,685,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,646,647,0,0,0,0,
-0,646,647,483,0,0,484,321,0,7,8,9,10,11,686,687,0,0,0,0,
-0,686,687,0,444,0,827,0,321,47,48,49,50,51,0,0,0,0,0,0,
-0,321,0,484,0,828,867,829,0,87,88,89,90,91,0,0,0,0,0,0,
-0,0,483,0,0,868,0,869,0,127,128,129,130,131,646,647,0,0,0,0,
-0,483,483,321,0,0,0,0,0,167,168,169,170,171,686,687,0,0,646,647,
-0,0,444,0,0,0,0,0,73,0,0,0,0,0,483,0,484,0,686,687,
-646,647,321,0,0,0,0,0,113,0,0,0,0,0,73,444,483,0,0,0,
-686,687,483,0,484,321,484,0,444,0,528,0,0,0,113,483,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,116,0,0,0,0,0,0,
-606,606,606,606,606,606,606,606,606,322,608,0,0,605,606,606,606,606,606,606,
-0,0,0,0,0,0,0,0,483,484,0,0,0,0,0,0,0,0,0,0
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,81,84,84,83,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,84,82,81,84,21,5,47,4,24,22,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,83,81,21,100,101,101,103,47,43,101,4,62,103,22,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,80,84,84,84,21,62,4,101,102,43,100,6,62,102,46,62,103,4,5,22,82,85,80,83,82,83,82,84,85,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,99,62,5,24,101,62,24,102,103,4,103,62,62,102,24,5,43,62,6,6,4,22,21,6,4,100,102,103,22,85,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,80,82,84,81,21,24,100,46,62,101,4,46,6,43,46,100,24,102,46,101,6,102,46,103,62,43,62,103,103,102,24,6,47,22,85,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,80,21,6,102,6,24,46,6,102,43,103,6,6,5,24,6,43,103,62,62,47,43,103,46,47,103,5,62,24,100,5,24,5,103,46,104,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,99,24,46,47,4,6,102,102,43,43,103,24,5,103,46,46,4,62,5,62,102,101,101,5,43,102,101,46,47,46,4,101,43,102,100,104,0,0,0,0,0,0,
+0,0,0,0,0,0,0,80,21,5,24,47,5,100,101,102,5,103,6,24,44,65,65,42,46,5,24,46,5,24,5,6,24,100,62,46,62,6,101,46,47,100,100,22,85,0,0,0,0,0,
+0,0,0,0,0,0,0,99,103,101,102,47,46,46,102,100,100,46,43,43,48,49,49,64,65,65,66,66,65,66,66,42,100,5,6,102,6,4,100,24,101,102,62,100,104,0,0,0,0,0,
+0,0,0,0,0,0,0,99,101,6,47,6,102,100,43,47,62,43,100,43,48,49,49,49,49,49,49,49,49,49,49,64,42,100,101,43,5,4,46,5,24,4,46,46,104,0,0,0,0,0,
+0,0,0,0,0,0,0,99,103,47,47,103,103,103,24,43,62,44,66,66,67,49,49,49,49,49,49,49,49,49,49,49,64,66,42,46,43,5,102,101,4,46,5,46,104,0,0,0,0,0,
+0,0,0,0,0,0,0,99,47,47,4,100,46,5,43,5,101,25,28,27,27,28,27,28,28,27,28,28,29,49,49,49,49,49,45,100,102,4,24,46,62,5,24,41,123,0,0,0,0,0,
+0,0,0,0,0,0,0,118,40,47,5,101,101,24,6,101,4,103,62,24,47,102,4,4,6,5,5,46,48,26,27,28,27,27,23,6,5,6,46,24,24,5,41,123,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,118,40,5,102,102,4,47,102,46,5,47,5,62,103,62,100,4,100,46,47,48,45,100,43,102,5,43,102,62,46,6,62,4,41,123,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,118,40,100,24,4,43,24,100,43,102,102,100,101,46,4,100,4,5,62,25,23,5,102,47,101,4,6,102,47,5,41,120,123,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,118,119,119,120,119,122,121,120,40,24,102,24,47,4,46,24,62,62,5,43,5,101,101,102,4,101,43,41,119,123,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,119,121,121,120,120,121,119,122,121,121,122,121,121,119,40,101,41,121,123,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,122,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,80,82,81,85,0,0,0,0,0,0,0,0,0,0,0,0,0,80,83,81,84,84,81,83,82,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,80,84,81,81,21,47,4,104,0,0,0,0,0,0,0,0,0,0,0,80,84,21,46,100,46,6,6,4,62,22,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,99,101,46,43,24,6,100,22,85,0,0,0,0,0,0,0,0,0,80,21,24,44,66,66,65,42,47,6,5,43,22,82,85,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,80,21,47,102,62,47,102,24,102,22,85,0,0,0,0,0,0,0,0,99,46,4,48,49,49,49,61,103,100,24,43,102,62,104,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,80,21,102,47,5,62,47,102,6,6,47,22,85,0,0,0,0,0,0,80,21,103,43,63,49,26,27,23,43,101,46,100,62,47,22,85,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,99,101,101,62,43,103,62,103,47,62,43,4,104,0,0,0,0,0,80,21,5,4,4,63,49,61,100,100,6,4,4,5,101,4,101,104,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,99,5,102,100,4,5,6,43,43,24,43,4,22,85,0,0,0,0,99,5,100,100,100,63,49,45,102,101,24,5,102,62,62,100,24,104,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,99,100,62,43,100,24,6,102,100,46,24,6,5,104,0,0,0,80,21,46,46,5,62,63,49,61,103,102,47,46,101,5,24,103,47,104,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,99,100,24,43,103,5,47,62,43,46,100,43,47,22,85,0,0,99,100,100,47,62,103,63,49,61,46,5,24,46,5,102,102,101,46,104,0,0,0,0,0,0,0,0,0,0,0,0,
+0,80,21,103,103,44,65,66,65,42,47,24,102,5,6,46,104,0,80,21,5,102,43,6,103,63,49,45,4,5,46,43,100,47,62,5,24,104,0,0,0,0,0,0,0,0,0,0,0,0,
+0,99,101,24,44,67,49,49,49,64,66,65,66,65,65,42,104,0,99,44,66,65,66,65,66,67,49,45,24,4,24,4,46,103,102,101,46,104,0,0,0,0,0,0,0,0,0,0,0,0,
+0,99,46,101,63,49,49,49,49,49,49,49,49,49,49,45,104,0,99,63,49,49,49,49,49,49,49,45,46,100,103,101,62,6,101,102,101,104,0,0,0,0,0,0,0,0,0,0,0,0,
+80,21,46,4,48,49,49,49,49,49,26,28,28,28,28,23,104,0,99,25,28,28,28,28,27,28,27,23,100,62,6,103,6,24,24,24,41,123,0,0,0,0,0,0,0,0,0,0,0,0,
+99,100,102,24,25,29,49,49,49,49,45,62,43,103,62,100,104,0,99,4,101,103,100,5,101,43,46,43,100,4,43,103,43,103,103,5,104,0,0,0,0,0,0,0,0,0,0,0,0,0,
+99,43,5,46,103,25,28,28,28,28,23,102,102,43,46,41,123,0,118,121,120,122,119,121,40,6,100,101,24,24,46,4,24,46,24,103,104,0,0,0,0,0,0,0,0,0,0,0,0,0,
+118,121,40,4,101,5,46,4,100,103,24,47,4,6,41,123,0,0,0,0,0,0,0,0,118,40,101,62,100,102,47,24,46,46,41,119,123,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,118,122,122,120,40,100,6,47,62,100,46,47,104,0,0,0,0,0,0,0,0,0,0,118,120,122,122,122,122,121,120,120,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,118,121,119,119,122,122,120,122,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
- <layer name="overoverlay" width="20" height="15">
+ <layer id="5" name="Decoration Under" width="50" height="50">
<data encoding="csv">
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,450,451,0,0,0,0,0,0,0,0,0,0,0,
-0,0,0,0,0,0,0,490,491,0,0,0,0,0,45,46,0,0,0,0,
-0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,86,0,0,0,0
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,290,291,0,0,0,291,290,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,309,0,0,0,0,0,309,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,309,0,0,0,0,0,309,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,328,329,312,0,329,329,328,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,290,291,0,0,0,291,290,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,309,0,0,0,0,0,311,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,290,291,0,0,0,291,291,292,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,309,0,0,0,0,0,311,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,309,0,0,0,0,0,0,311,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,328,329,0,0,329,329,330,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,309,0,0,0,0,0,0,311,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,328,329,329,0,329,329,329,330,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="3" name="Buildings" width="50" height="50">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,410,411,412,413,414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,429,430,431,432,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,552,553,554,0,0,0,0,0,0,448,449,450,451,452,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,571,572,573,0,0,0,0,0,0,467,468,469,470,471,0,0,0,0,0,710,711,712,713,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,590,591,592,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,729,730,731,732,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,609,610,611,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,748,749,750,751,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,767,768,769,770,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,107,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,628,629,630,631,632,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,647,648,649,650,651,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,785,786,787,788,789,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,666,667,668,669,670,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,804,805,806,807,808,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,685,686,687,688,689,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,823,824,825,826,827,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,842,843,844,845,846,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,71,72,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,90,91,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,109,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <layer id="4" name="Decoration Over" width="50" height="50">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,269,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,269,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,368,369,0,368,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,233,0,0,0,234,0,0,0,234,0,0,0,0,0,0,0,97,0,0,0,0,0,0,236,0,0,0,0,236,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,234,348,0,348,234,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,0,0,0,0,236,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,270,0,0,0,0,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,350,0,366,367,236,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,233,96,289,98,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,115,116,117,233,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,117,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,250,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,232,0,0,0,0,0,0,250,0,0,0,646,719,646,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,665,0,665,0,0,0,0,0,0,0,269,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,274,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,352,353,354,355,356,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,371,372,373,374,375,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,390,391,392,393,394,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,236,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,368,369,0,368,369,0,0,0,0,0,0,0,0,0,0,0,0,269,0,0,0,0,236,0,0,0,0,0,0,347,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,331,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,305,306,0,0,0,236,0,366,367,0,366,367,347,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,275,0,0,0,0,0,0,0,382,324,325,0,0,0,236,0,0,0,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,115,97,97,117,0,269,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,250,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,269,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,250,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,250,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
</map>
diff --git a/examples/maps/serene-village.tsx b/examples/maps/serene-village.tsx
new file mode 100644
index 0000000..aec16da
--- /dev/null
+++ b/examples/maps/serene-village.tsx
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tileset version="1.5" tiledversion="1.5.0" name="Serene Village" tilewidth="32" tileheight="32" tilecount="855" columns="19">
+ <image source="../images/serene-village.png" width="608" height="1440"/>
+ <tile id="209">
+ <animation>
+ <frame tileid="209" duration="100"/>
+ <frame tileid="210" duration="100"/>
+ <frame tileid="211" duration="100"/>
+ <frame tileid="212" duration="100"/>
+ <frame tileid="213" duration="100"/>
+ <frame tileid="214" duration="100"/>
+ <frame tileid="215" duration="100"/>
+ <frame tileid="216" duration="100"/>
+ <frame tileid="217" duration="100"/>
+ <frame tileid="218" duration="100"/>
+ <frame tileid="219" duration="100"/>
+ <frame tileid="220" duration="100"/>
+ <frame tileid="221" duration="100"/>
+ <frame tileid="222" duration="100"/>
+ </animation>
+ </tile>
+ <tile id="718">
+ <animation>
+ <frame tileid="718" duration="100"/>
+ <frame tileid="719" duration="100"/>
+ <frame tileid="720" duration="100"/>
+ <frame tileid="721" duration="100"/>
+ </animation>
+ </tile>
+ <wangsets>
+ <wangset name="Unnamed Set" type="corner" tile="-1">
+ <wangcolor name="Grass" color="#ff0000" tile="-1" probability="1"/>
+ <wangcolor name="Water" color="#00ff00" tile="-1" probability="1"/>
+ <wangcolor name="Dirt" color="#0000ff" tile="-1" probability="1"/>
+ <wangtile tileid="3" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="4" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="5" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="20" wangid="0,1,0,1,0,1,0,0"/>
+ <wangtile tileid="21" wangid="0,0,0,1,0,1,0,1"/>
+ <wangtile tileid="22" wangid="0,1,0,1,0,1,0,3"/>
+ <wangtile tileid="23" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="24" wangid="0,3,0,1,0,1,0,1"/>
+ <wangtile tileid="25" wangid="0,3,0,1,0,3,0,3"/>
+ <wangtile tileid="26" wangid="0,3,0,1,0,1,0,3"/>
+ <wangtile tileid="27" wangid="0,3,0,1,0,1,0,3"/>
+ <wangtile tileid="28" wangid="0,3,0,3,0,1,0,3"/>
+ <wangtile tileid="39" wangid="0,1,0,1,0,0,0,1"/>
+ <wangtile tileid="40" wangid="0,1,0,0,0,1,0,1"/>
+ <wangtile tileid="41" wangid="0,1,0,1,0,3,0,1"/>
+ <wangtile tileid="42" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="43" wangid="0,1,0,3,0,1,0,1"/>
+ <wangtile tileid="44" wangid="0,1,0,1,0,3,0,3"/>
+ <wangtile tileid="45" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="46" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="47" wangid="0,3,0,3,0,1,0,1"/>
+ <wangtile tileid="48" wangid="0,3,0,3,0,3,0,3"/>
+ <wangtile tileid="60" wangid="0,1,0,1,0,3,0,3"/>
+ <wangtile tileid="61" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="62" wangid="0,3,0,3,0,1,0,1"/>
+ <wangtile tileid="63" wangid="0,1,0,3,0,3,0,3"/>
+ <wangtile tileid="64" wangid="0,1,0,3,0,3,0,1"/>
+ <wangtile tileid="65" wangid="0,1,0,3,0,3,0,1"/>
+ <wangtile tileid="66" wangid="0,3,0,3,0,3,0,1"/>
+ <wangtile tileid="79" wangid="0,0,0,1,0,0,0,0"/>
+ <wangtile tileid="80" wangid="0,0,0,1,0,1,0,0"/>
+ <wangtile tileid="81" wangid="0,0,0,1,0,1,0,0"/>
+ <wangtile tileid="82" wangid="0,0,0,1,0,1,0,0"/>
+ <wangtile tileid="83" wangid="0,0,0,1,0,1,0,0"/>
+ <wangtile tileid="84" wangid="0,0,0,0,0,1,0,0"/>
+ <wangtile tileid="98" wangid="0,1,0,1,0,0,0,0"/>
+ <wangtile tileid="99" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="100" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="101" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="102" wangid="0,1,0,1,0,1,0,1"/>
+ <wangtile tileid="103" wangid="0,0,0,0,0,1,0,1"/>
+ <wangtile tileid="117" wangid="0,1,0,0,0,0,0,0"/>
+ <wangtile tileid="118" wangid="0,1,0,0,0,0,0,1"/>
+ <wangtile tileid="119" wangid="0,1,0,0,0,0,0,1"/>
+ <wangtile tileid="120" wangid="0,1,0,0,0,0,0,1"/>
+ <wangtile tileid="121" wangid="0,1,0,0,0,0,0,1"/>
+ <wangtile tileid="122" wangid="0,0,0,0,0,0,0,1"/>
+ </wangset>
+ </wangsets>
+</tileset>
diff --git a/examples/tiled.scm b/examples/tile-map.scm
index c528f74..52d6b94 100644
--- a/examples/tiled.scm
+++ b/examples/tile-map.scm
@@ -3,11 +3,13 @@
(chickadee math rect)
(chickadee graphics color)
(chickadee graphics font)
- (chickadee graphics tiled)
+ (chickadee graphics tile-map)
(ice-9 format)
(ice-9 match)
(srfi srfi-11))
+(define window-width 960)
+(define window-height 540)
(define map #f)
(define prev-camera (vec2 0.0 0.0))
(define camera (vec2 0.0 0.0))
@@ -20,12 +22,12 @@
(define start-time 0.0)
(define avg-frame-time 16)
(define stats-text (stats-message))
-(define stats-text-pos (vec2 4.0 464.0))
+(define stats-text-pos (vec2 4.0 (- window-height 16.0)))
(define last-update start-time)
(define scroll-speed 6.0)
(define (load)
- (set! map (load-tile-map "maps/example.tmx")))
+ (set! map (load-tile-map "maps/example3.tmx")))
(define (draw alpha)
;; Linearly interpolate between the current camera position and the
@@ -34,11 +36,13 @@
;; loop does not render in lock-step with updates.
(let ((beta (- 1.0 alpha)))
(set-vec2-x! render-camera
- (+ (* (vec2-x camera) alpha)
- (* (vec2-x prev-camera) beta)))
+ (round
+ (+ (* (vec2-x camera) alpha)
+ (* (vec2-x prev-camera) beta))))
(set-vec2-y! render-camera
- (+ (* (vec2-y camera) alpha)
- (* (vec2-y prev-camera) beta))))
+ (round
+ (+ (* (vec2-y camera) alpha)
+ (* (vec2-y prev-camera) beta)))))
(draw-tile-map map #:camera render-camera)
(draw-text text text-position #:color black)
(draw-text stats-text stats-text-pos #:color black)
@@ -47,20 +51,36 @@
(+ (* (- current-time start-time) 0.1)
(* avg-frame-time 0.9)))
(set! start-time current-time)
- (when (>= (- current-time last-update) 1000)
+ (when (>= (- current-time last-update) 1.0)
(set! stats-text (stats-message))
(set! last-update current-time))))
+(define (refresh-tile-coords x y)
+ (call-with-values (lambda ()
+ (point->tile map
+ (+ x (vec2-x camera))
+ (+ y (vec2-y camera))))
+ (lambda (tx ty)
+ (set! text (format #f "~d, ~d" tx ty)))))
+
(define (update dt)
(vec2-copy! camera prev-camera)
- (set-vec2-x! camera
- (+ (vec2-x camera)
- (if (key-pressed? 'right) scroll-speed 0.0)
- (if (key-pressed? 'left) (- scroll-speed) 0.0)))
- (set-vec2-y! camera
- (+ (vec2-y camera)
- (if (key-pressed? 'up) scroll-speed 0.0)
- (if (key-pressed? 'down) (- scroll-speed) 0.0))))
+ (set-vec2! camera
+ (min (max (+ (vec2-x camera)
+ (if (key-pressed? 'right) scroll-speed 0.0)
+ (if (key-pressed? 'left) (- scroll-speed) 0.0))
+ 0.0)
+ (- (* (tile-map-width map) (tile-map-tile-width map))
+ window-width))
+ (min (max (+ (vec2-y camera)
+ (if (key-pressed? 'up) scroll-speed 0.0)
+ (if (key-pressed? 'down) (- scroll-speed) 0.0))
+ 0.0)
+ (- (* (tile-map-height map) (tile-map-tile-height map))
+ window-height)))
+ (when (or (key-pressed? 'left) (key-pressed? 'right)
+ (key-pressed? 'down) (key-pressed? 'up))
+ (refresh-tile-coords (mouse-x) (mouse-y))))
(define (key-press key modifiers repeat?)
(match key
@@ -68,11 +88,10 @@
(_ #t)))
(define (mouse-move x y x-rel y-rel buttons)
- (let-values (((tx ty) (point->tile map (- x (vec2-x camera)) (- y (vec2-y camera)))))
- (set! text (format #f "~d, ~d" tx ty))))
+ (refresh-tile-coords x y))
-(run-game #:window-width 640
- #:window-height 480
+(run-game #:window-width window-width
+ #:window-height window-height
#:window-title "tile map demo"
#:load load
#:draw draw