aboutsummaryrefslogtreecommitdiff
path: root/tilemap.scm
diff options
context:
space:
mode:
Diffstat (limited to 'tilemap.scm')
-rw-r--r--tilemap.scm235
1 files changed, 235 insertions, 0 deletions
diff --git a/tilemap.scm b/tilemap.scm
new file mode 100644
index 0000000..6e4dc95
--- /dev/null
+++ b/tilemap.scm
@@ -0,0 +1,235 @@
+(module tilemap
+ *
+ (import scheme
+ (chicken io)
+ (chicken base)
+ (chicken string)
+ (chicken format)
+ (chicken process-context)
+ (chicken pathname)
+ (chicken pretty-print)
+ (srfi 1)
+ matchable
+ expat
+ defstruct
+ (prefix sdl2-image "img:")
+ (prefix sdl2 "sdl2:"))
+
+(+ 1 1)
+
+ (defstruct tileset
+ tilewidth
+ tileheight
+ spacing
+ tilecount
+ columns
+ image-source
+ image)
+
+ (+ 1 1)
+
+ (defstruct layer
+ name
+ width
+ height
+ map)
+
+ (defstruct object
+ name
+ type
+ x
+ y
+ width
+ height
+ properties)
+
+ (defstruct tilemap
+ width
+ height
+ tilewidth
+ tileheight
+ tileset-source
+ tileset
+ layers
+ objects)
+
+ (defstruct tile
+ id
+ rect)
+
+ (define (maybe-do action)
+ (lambda (value)
+ (if (eq? value #f)
+ #f
+ (action value))))
+
+ (define (attempt action)
+ (lambda (value)
+ (or (action value) value)))
+
+ (define maybe-string->number (maybe-do (attempt string->number)))
+
+ (define (string-alist->alist string-alist)
+ (map (lambda (pair) (cons (string->symbol (car pair))
+ (maybe-string->number (cdr pair))))
+ string-alist))
+
+ (define (parse-tileset string-tileset)
+ (let ((parser (expat:make-parser))
+ (tags '())
+ (tileset (make-tileset 0 0 0 0 0 "")))
+ (expat:set-start-handler! parser
+ (lambda (tag attrs)
+ (let ((symbol-attrs (string-alist->alist attrs)))
+ (match tag
+ ("tileset" (set! tileset (alist->tileset symbol-attrs)))
+ ("image" (tileset-image-source-set! tileset (alist-ref 'source symbol-attrs)))
+ (_ #f)))
+ (set! tags (cons tag tags))))
+ (expat:set-end-handler! parser (lambda (tag)
+ (set! tags (cdr tags))))
+ (expat:set-character-data-handler! parser (lambda (line) #f))
+ (expat:parse parser string-tileset)
+ tileset))
+
+ (define (load-tileset file-name)
+ (call-with-input-file file-name
+ (lambda (port)
+ (let* ((tileset (parse-tileset (read-string #f port)))
+ (image-source (tileset-image-source tileset))
+ (base-path (pathname-directory file-name))
+ (img-to-load (if (absolute-pathname? image-source)
+ image-source
+ (pathname-replace-directory
+ image-source
+ (if (pathname-directory image-source)
+ (format "~a/~a" base-path (pathname-directory image-source))
+ base-path)))))
+ (tileset-image-set! tileset (img:load img-to-load))
+ tileset))))
+
+ (define (parse-tilemap string-tilemap)
+ (let ((parser (expat:make-parser))
+ (tags '())
+ (tilemap (make-tilemap width: 0 height: 0 tilewidth: 0 tileheight: 0
+ tileset-source: "" tileset: #f
+ layers: '() objects: '()))
+ (layer '())
+ (object '()))
+ (expat:set-start-handler!
+ parser
+ (lambda (tag attrs)
+ (let ((symbol-attrs (string-alist->alist attrs)))
+ (match tag
+ ("map"
+ (tilemap-width-set! tilemap (alist-ref 'width symbol-attrs))
+ (tilemap-height-set! tilemap (alist-ref 'height symbol-attrs))
+ (tilemap-tilewidth-set! tilemap (alist-ref 'tilewidth symbol-attrs))
+ (tilemap-tileheight-set! tilemap (alist-ref 'tileheight symbol-attrs)))
+ ("tileset"
+ (tilemap-tileset-source-set! tilemap (alist-ref 'source symbol-attrs)))
+ ("layer"
+ (set! layer (alist->layer attrs)))
+ ("object"
+ (set! object (alist->object symbol-attrs)))
+ ("property"
+ (object-properties-set!
+ object
+ (cons (cons (alist-ref 'name symbol-attrs) (alist-ref 'value symbol-attrs))
+ (or (object-properties object) '()))))
+ (_ #f))
+ (set! tags (cons tag tags)))))
+ (expat:set-end-handler!
+ parser
+ (lambda (tag)
+ (match tag
+ ("layer" (begin
+ (tilemap-layers-set! tilemap
+ (cons layer (tilemap-layers tilemap)))
+ (set! layer '())))
+ ("object" (tilemap-objects-set! tilemap (cons object (tilemap-objects tilemap))))
+ (_ #f))
+ (set! tags (cdr tags))))
+ (expat:set-character-data-handler!
+ parser
+ (lambda (line)
+ (when (string=? (car tags) "data")
+ (let ((txt (string-chomp line)))
+ (when (not (string=? txt ""))
+ (layer-map-set! layer (append
+ (or (layer-map layer) '())
+ (list (map string->number
+ (string-split txt ","))))))))))
+ (expat:parse parser string-tilemap)
+ tilemap))
+
+ (define (tileset-rows tileset)
+ "Return the number of rows in the tileset"
+ (inexact->exact (ceiling (/ (tileset-tilecount tileset) (tileset-columns tileset)))))
+
+ (define (tileset-tile tileset tile-id)
+ ;; Use the tileset's columns setting and the tileheight/tilewidth to
+ ;; find the tile's x,y location and create a rect
+ (let* ((tile-num (- tile-id 1)) ; tile-id starts at 1!
+ (tile-width (tileset-tilewidth tileset))
+ (tile-height (tileset-tileheight tileset))
+ (tile-x (modulo tile-num (tileset-columns tileset)))
+ (tile-y (inexact->exact (floor (/ tile-num (tileset-columns tileset)))))
+ (x (+ (* tile-x tile-width) tile-x))
+ (y (+ (* tile-y tile-height) tile-y)))
+ (make-tile
+ id: tile-id
+ rect: (sdl2:make-rect x y tile-width tile-height))))
+
+ (define (load-tilemap file-name)
+ (call-with-input-file file-name
+ (lambda (port)
+ (let* ((tilemap (parse-tilemap (read-string #f port)))
+ (base-path (pathname-directory file-name))
+ (tileset-source (tilemap-tileset-source tilemap)))
+ (tilemap-tileset-set! tilemap (load-tileset
+ (if (absolute-pathname? tileset-source)
+ tileset-source
+ (pathname-replace-directory
+ tileset-source
+ (if (pathname-directory tileset-source)
+ (format "~a/~a" base-path (pathname-directory tileset-source))
+ base-path))
+ )))
+ tilemap))))
+
+ (when #f
+
+ (let ((txt "<?xml version='1.0' encoding='UTF-8'?>
+<tileset version='1.10' tiledversion='1.11.2' name='monochrome_transparent' tilewidth='16' tileheight='16' spacing='1' tilecount='1078' columns='49'>
+ <image source='monochrome-transparent.png' width='832' height='373'/>
+</tileset>
+"))
+ (tileset-image (parse-tileset txt)))
+
+ (let ((txt "<?xml version='1.0' encoding='UTF-8'?>
+<map version='1.10' tiledversion='1.11.0' orientation='orthogonal' renderorder='right-down' width='40' height='30' tilewidth='16' tileheight='16' infinite='0' nextlayerid='8' nextobjectid='5'>
+ <tileset firstgid='1' source='monochrome_transparent.tsx'/>
+ <layer id='3' name='ground' width='40' height='30'>
+ <data encoding='csv'>
+0,0,0,0,168,169,0,0,0,0,
+844,0,0,0,0,0,845,546,546,546,
+</data>
+ </layer>
+ <objectgroup id='7' name='entities'>
+ <object id='2' name='player' type='Player' gid='29' x='182' y='350.5' width='16' height='16'/>
+ <object id='3' name='hint' type='Text' x='98.5' y='432.5' width='197' height='78'>
+ <properties>
+ <property name='text' value='hit enter to start a macro'/>
+ </properties>
+ </object>
+ <object id='4' name='goal' type='Goal' x='560.935' y='288.641' width='16' height='16'/>
+ </objectgroup>
+</map>
+"))
+ (tilemap-tileset (parse-tilemap txt)))
+
+ (tileset-image (tilemap-tileset (load-tilemap "assets/level-0.tmx")))
+ )
+
+ ) ;; End tilemap module