Multiple tile-style support, layered maps

This commit is contained in:
Jeremy Penner 2021-11-14 14:55:41 -05:00
parent fe00a91064
commit 18f62e89b9
9 changed files with 137 additions and 52 deletions

View file

@ -12,14 +12,19 @@
(fn GraphicsEditView.new [self]
(GraphicsEditView.super.new self)
(set self.tilecache (files.cache (self:resource-key)))
(set self.itile 1)
(self:set-style (self:initial-style))
(set self.scrollheight math.huge)
(set self.scrollable true)
(attach-imstate self))
(fn GraphicsEditView.get_scrollable_size [self] self.scrollheight)
(fn GraphicsEditView.resource-key [self] :tiles)
(fn GraphicsEditView.tilesize [self] (values 16 16))
(fn GraphicsEditView.initial-style [self] :tiles)
(fn GraphicsEditView.tilesize [self]
(let [style (tiles.style self.style)]
(values (or style.editw style.tilew) (or style.edith style.tileh))))
(fn GraphicsEditView.set-style [self key]
(set self.style key)
(set self.tilecache (files.cache key))
(set self.itile 1))
(fn GraphicsEditView.reload [self] (files.reload))
(fn GraphicsEditView.save [self] (files.save))

View file

@ -5,48 +5,56 @@
(local files (require :game.files))
(local {: mouse-inside : activate : active? : checkbox : textfield : textbutton : textbox : dropdown} (util.require :editor.imstate))
(local {: tilestrip-to-sprite} (util.require :editor.tiledraw))
(local {: encode-yx : encode-itile : decode-itile} (util.require :game.tiles))
(local {: encode-yx : encode-itile : decode-itile : dimensions} (util.require :game.tiles))
(local actions (require :editor.actions))
(local MapEditView (GraphicsEditView:extend))
(local sprite-scale 3)
(local platforms {
:ii {:mapw 20 :maph 12 :tilew 14 :tileh 16}
:iigs {:mapw 20 :maph 12 :tilew 16 :tileh 16}
})
(fn platform [?key] (let [p (dimensions)] (if ?key (. p ?key) p)))
(fn MapEditView.layer-type [self ?ilayer] (or (?. (platform :layers) (or ?ilayer self.ilayer)) :tiles))
(fn MapEditView.dimensions [self ?ilayer] (or (platform (self:layer-type)) (platform)))
(local platform (. platforms (files.platform)))
(local {: mapw : maph} platform)
(local tilew (* sprite-scale platform.tilew))
(local tileh (* sprite-scale platform.tileh))
(fn MapEditView.mapw [self ?ilayer] (. (self:dimensions ?ilayer) :mapw))
(fn MapEditView.maph [self ?ilayer] (. (self:dimensions ?ilayer) :maph))
(fn MapEditView.tilew [self ?ilayer] (* sprite-scale (. (self:dimensions ?ilayer) :tilew)))
(fn MapEditView.tileh [self ?ilayer] (* sprite-scale (. (self:dimensions ?ilayer) :tileh)))
(fn MapEditView.empty-map [self] (string.rep "\0" (* (self:mapw) (self:maph))))
(fn MapEditView.new [self]
(MapEditView.super.new self)
(set self.sprite-scale sprite-scale)
(set self.stripcache {})
(set self.ilevel 1)
(self:set-ilayer 1)
(self:reload))
; map is stored bottom-to-top
(fn imap-from-xy [mx my]
(+ mx -1 (* mapw (- maph my))))
(fn MapEditView.imap-from-xy [self mx my]
(+ mx -1 (* (self:mapw) (- (self:maph) my))))
(fn update-map [map mx my itile]
(local imap (imap-from-xy mx my))
(fn MapEditView.update-map [self map mx my itile]
(local imap (self:imap-from-xy mx my))
(local enctile (encode-itile itile))
(..
(map:sub 1 imap)
(string.char enctile)
(map:sub (+ imap 2))))
(fn MapEditView.map [self]
(if (platform :layers) (or (?. self.level.layers self.ilayer) (self:empty-map))
self.level.map))
(fn MapEditView.itile-from-xy [self mx my]
(local imap (+ (imap-from-xy mx my) 1))
(local enctile (string.byte (self.level.map:sub imap imap)))
(local imap (+ (self:imap-from-xy mx my) 1))
(local enctile (string.byte (string.sub (self:map) imap imap)))
(decode-itile enctile))
(fn MapEditView.set-tile [self mx my itile]
(set self.level.map (update-map self.level.map mx my itile)))
(let [updated-map (self:update-map (self:map) mx my itile)]
(if (platform :layers) (util.nested-tset self.level [:layers self.ilayer] updated-map)
(set self.level.map updated-map))))
(fn MapEditView.iobject-from-xy [self mx my ?iobj]
(local iobj (or ?iobj 1))
@ -83,11 +91,25 @@
(self:load-level))
(- yNext y)))
(fn MapEditView.set-ilayer [self ilayer]
(set self.ilayer ilayer)
(self:set-style (self:layer-type)))
(fn MapEditView.draw-layer-selector [self x y]
(renderer.draw_text style.font "Layer" x (+ y (/ style.padding.y 2)) style.text)
(let [mkopt (fn [ilayer] {: ilayer :label (.. ilayer " (" (self:layer-type ilayer) ")")})
options (icollect [ilayer (ipairs (platform :layers))] (mkopt ilayer))
(selection yNext) (dropdown self :layer-selector (mkopt self.ilayer) options (+ x (* 50 SCALE)) y (* 100 SCALE))]
(when (not= self.ilayer selection.ilayer)
(self:set-ilayer selection.ilayer))
(- yNext y)))
(fn MapEditView.linking-obj [self] (. self.level.objects self.iobject-linking))
(fn MapEditView.draw-link-line [self x y iobjectSrc color toMouse?]
(local objectSrc (. self.level.objects iobjectSrc))
(local objectDest (. self.level.objects objectSrc.link))
(local coord (fn [c m d] (+ c (* (- m 1) d) (/ d 2))))
(local [tilew tileh] [(self:tilew) (self:tileh)])
(local xStart (coord x objectSrc.x tilew))
(local yStart (coord y objectSrc.y tileh))
(when (or toMouse? objectDest)
@ -102,7 +124,7 @@
; stripcache leaks but honestly who cares
(local tilestrip [])
(var stripid "")
(for [mx 1 mapw]
(for [mx 1 (self:mapw)]
(local itile (self:itile-from-xy mx my))
(local tile (?. self.tilecache.tiles itile :gfx))
(table.insert tilestrip tile)
@ -116,6 +138,7 @@
(fn MapEditView.draw-map-editor [self x y]
(love.graphics.setColor 1 1 1 1)
(local button-state self.imstate.left)
(local [mapw maph tilew tileh] [(self:mapw) (self:maph) (self:tilew) (self:tileh)])
(activate self :map x y (* tilew mapw) (* tileh maph))
(var iobject-over nil)
(for [my 1 maph]
@ -234,7 +257,7 @@
(fn MapEditView.load-level [self]
(set self.stripcache {})
(when (= (. (self:levels) self.ilevel) nil)
(tset (self:levels) self.ilevel {:map (string.rep "\0" (* mapw maph)) :objects []}))
(tset (self:levels) self.ilevel {:map (self:empty-map) :objects []}))
(set self.level (. (self:levels) self.ilevel))
(set self.iobject nil))
@ -247,9 +270,12 @@
(var y (+ self.position.y style.padding.y (- self.scroll.y)))
(self:draw_background style.background)
(self:draw_scrollbar)
(local [mapw maph tilew tileh] [(self:mapw) (self:maph) (self:tilew) (self:tileh)])
(local ytop y)
(local editor-on-side (> self.size.x (+ (* tilew mapw) (* 300 SCALE))))
(when (platform :layers) (self:draw-layer-selector (+ x (* 200 SCALE)) y))
(set y (+ y (self:draw-map-selector x y) style.padding.y))
(self:draw-map-editor x y)
(set y (+ y (* tileh maph) style.padding.y))
(set y (+ y (self:draw-tile-selector x y (if editor-on-side (* tilew mapw)

View file

@ -1,21 +1,29 @@
(local {: putpixel : make-canvas} (require :editor.tiledraw))
(local tiles (require :game.tiles))
; converted from http://pixeljoint.com/forum/forum_posts.asp?TID=12795 (db16)
; maybe check out https://lospec.com/palette-list ?
(local pal [[1 0 1] [4 2 3] [3 3 6] [4 4 4] [8 4 3] [3 6 2] [13 4 4] [7 7 6]
[5 7 12] [13 7 2] [8 9 10] [6 10 2] [13 10 9] [6 12 12] [13 13 5] [13 14 13]])
(fn gs-to-rgb [color] (icollect [_ v (ipairs color)] (* v 0x11)))
(fn gs-to-rgb [color] (icollect [_ v (ipairs (or color [0 0 0]))] (* v 0x11)))
(fn tile-to-sprite [tile]
(if tile (make-canvas 16 16 (fn [canvas]
(fn spritegen-for-size [w h]
(fn [tile]
(when tile (make-canvas w h (fn [canvas]
(love.graphics.clear 0 0 0 0)
(for [y 0 15]
(for [x 0 15]
(let [ibyte (+ (* y 16) x 1)
(for [y 0 (- h 1)]
(for [x 0 (- w 1)]
(let [ibyte (+ (* y w) x 1)
byte (string.byte (tile:sub ibyte ibyte))
mask (bit.band (bit.rshift byte 4) 0xf)
color (bit.band byte 0xf)
rgb (if (= mask 0) (gs-to-rgb (. pal (+ color 1))) [255 0 255])]
(when (= mask 0) (putpixel x y rgb)))))))))
(when (= mask 0) (putpixel x y rgb))))))))))
{: tile-to-sprite : pal : gs-to-rgb}
(local tile-to-sprite (spritegen-for-size 16 16))
(fn spritegen-for-style [name]
(let [{: tilew : tileh} (tiles.style name)]
(spritegen-for-size tilew tileh)))
{: tile-to-sprite : spritegen-for-size : spritegen-for-style : pal : gs-to-rgb}

View file

@ -31,6 +31,14 @@
#(each [isprite sprite (ipairs sprites)]
(love.graphics.draw sprite (* (sprite:getWidth) (- isprite 1)) 0))))))
(files.default-platform-method TileDraw :editor.tiledraw :spritegen-for-style
(fn [style]
(match style
:font TileDraw.char-to-sprite
:brushes TileDraw.char-to-sprite
:portraits TileDraw.portrait-to-sprite
_ TileDraw.tile-to-sprite)))
(fn TileDraw.TileCache [tiles ?spritegen]
{: tiles
:spritegen (or ?spritegen TileDraw.tile-to-sprite)

View file

@ -1,12 +1,16 @@
(local {: pal : gs-to-rgb} (require :editor.tiledraw.iigs))
(local lume (require :lib.lume))
{:map-bitxy (fn [self x y] (values (+ (* y 16) x) 0 0xff))
{:map-bitxy (fn [self x y w h] (values (+ (* y w) x) 0 0xff))
:pixel-color (fn [self b] (match b 0xf0 (values [128 128 128] [64 64 64])
_ (gs-to-rgb (. pal (+ b 1)))))
:draw-bits #(if (= $1.icolor 17) 0xf0 (- $1.icolor 1))
:palette #(lume.concat (icollect [_ color (ipairs pal)] (gs-to-rgb color)) [[255 0 255]])
:pixel-storage-divisor #1
:preview-locations #[[8 0] [0 5] [16 5] [8 10]]
:blank-tile-byte #"\xf0"
:preview-locations (fn [self]
(match self.style
:iso [[12 0] [0 6] [24 6] [12 12]]
_ [[0 0] [12 0] [0 12] [12 12]]))
}

View file

@ -3,14 +3,13 @@
(local tiles (require :game.tiles))
(local files (require :game.files))
(local util (require :lib.util))
(local {: mouse-inside : activate : active? : checkbox : textfield : button} (util.require :editor.imstate))
(local {: mouse-inside : activate : active? : checkbox : textfield : button : dropdown} (util.require :editor.imstate))
(local TileView (GraphicsEditView:extend))
(set TileView.pixel-size 24)
(local pixel-size TileView.pixel-size)
(fn TileView.tilesize [self] (values 16 16))
(fn TileView.tilekeys [self]
(if files.game.tilesets (icollect [_ key (pairs files.game.tilesets)] key)
[:gfx]))
@ -37,10 +36,12 @@
(files.default-platform-method TileView :editor.tileedit :preview-locations
(fn [self] (let [(w h) (self:tilesize)] [[0 0] [w 0] [0 h] [w h]])))
(files.default-platform-method TileView :editor.tileedit :blank-tile-byte #"\0")
(fn TileView.tile [self]
(local (w h) (self:tilesize))
(or (-?> self.tilecache.tiles (. self.itile) (. (or self.tilekey :gfx)))
(string.rep "\0" (/ (* w h) (self:pixel-storage-divisor)))))
(string.rep (self:blank-tile-byte) (/ (* w h) (self:pixel-storage-divisor)))))
(fn TileView.draw-pixel [self x y colorbg ?colorfg]
(renderer.draw_rect x y pixel-size pixel-size colorbg)
@ -53,7 +54,7 @@
(local editor-h (* (+ pixel-size 1) h))
(activate self :tile x y editor-w editor-h)
(for [bitx 0 (- w 1)] (for [bity 0 (- h 1)]
(local (ibyte ibit mask) (self:map-bitxy bitx bity))
(local (ibyte ibit mask) (self:map-bitxy bitx bity w h))
(local b (get-bits tile ibyte ibit mask))
(local (px py) (values (+ x (* bitx (+ pixel-size 1))) (+ y (* bity (+ pixel-size 1)))))
(local (colorbg colorfg) (self:pixel-color b ibyte ibit))
@ -109,6 +110,13 @@
(fn TileView.update-tile [self newtile]
(self.tilecache:update-tile self.itile newtile self.tilekey))
(fn TileView.draw-style-selector [self x y]
(renderer.draw_text style.font "Style" x (+ y (/ style.padding.y 2)) style.text)
(let [(selection yNext) (dropdown self :layer-selector self.style (tiles.tile-styles) (+ x (* 50 SCALE)) y (* 100 SCALE)) ]
(when (not= self.style selection)
(self:set-style selection))
(- yNext y)))
(fn TileView.draw [self]
(self:draw_background style.background)
(self:draw_scrollbar)
@ -119,12 +127,14 @@
(self:draw-tile-preview (+ x editor-w pixel-size) (+ preview-y style.padding.y))
(var selector-y (+ y editor-h pixel-size))
(set selector-y (+ selector-y (self:draw-tile-palette x selector-y (- self.size.x 20))))
(when (> (length (tiles.tile-styles)) 1)
(set selector-y (+ selector-y (self:draw-style-selector x selector-y) style.padding.y)))
(each [_ key (ipairs (self:tilekeys))]
(local selector-h (self:draw-tile-selector x selector-y (- self.size.x 20) key))
(set selector-y (+ selector-y selector-h pixel-size)))
(set self.scrollheight (- selector-y y)))
(fn TileView.resource-key [self] :tiles)
(fn TileView.initial-style [self] :tiles)
(fn TileView.get_name [self] "Tile Editor")
TileView

View file

@ -29,13 +29,17 @@
(fn deserialize [key value root]
(match key
(where (or :tiles :portraits :font :brushes)) (tile-deserialize value root)
:levels (do (set value.map (value.map:fromhex)) value)
:levels (do (set value.map (value.map:fromhex))
(set value.layers (icollect [_ layer (ipairs (or value.layers []))] (layer:fromhex)))
value)
_ value))
(fn serialize [key value root]
(match key
(where (or :tiles :portraits :font :brushes)) (tile-serialize value root)
:levels (do (set value.map (value.map:tohex)) value)
:levels (do (set value.map (value.map:tohex))
(set value.layers (icollect [_ layer (ipairs (or value.layers []))] (layer:tohex)))
value)
_ value))
(fn clone [v]
@ -65,11 +69,8 @@
(fn new-cache [game key]
(let [tiledraw (require :editor.tiledraw)
spritegen (match key
:font tiledraw.char-to-sprite
:brushes tiledraw.char-to-sprite
:portraits tiledraw.portrait-to-sprite
_ tiledraw.tile-to-sprite)
tiles (require :game.tiles)
spritegen (tiledraw.spritegen-for-style key)
gfx (. game key)]
(tiledraw.TileCache gfx spritegen)))

View file

@ -2,6 +2,29 @@
(local lume (require :lib.lume))
(local files (require :game.files))
(local platforms {
:ii {:mapw 20 :maph 12 :tilew 14 :tileh 16 :editw 16
:font {:tilew 7 :editw 8 :tileh 8}
:brushes {:tilew 7 :editw 8 :tileh 8}
:portraits {:tilew 28 :editw 32 :tileh 32}}
:iigs {:mapw 26 :maph 16 :tilew 12 :tileh 12
:layers [:tiles :iso :iso]
:yoffsets [0 6 0]
:iso {:mapw 13 :maph 16 :tilew 24 :tileh 32 :stagger 12}
:font {:tilew 8 :tileh 8}
:brushes {:tilew 8 :tileh 8}
:portraits {:tilew 32 :tileh 32}}
})
(fn dimensions [] (. platforms (files.platform)))
(fn style [name] (or (. (dimensions) name) (dimensions)))
(fn tile-styles [include-details]
(let [dim (dimensions)
styles {:tiles dim}]
(each [_ layer (ipairs (or dim.layers []))]
(when (not= layer :tiles) (tset styles layer (. dim layer))))
(if include-details styles (lume.keys styles))))
(fn flags [] (or files.game.tileflags [:walkable]))
(fn flag-to-bit []
(collect [iflag flag (ipairs (flags))] (values flag (bit.lshift 1 (- iflag 1)))))
@ -55,5 +78,5 @@
(find-itile tiles label (+ itile 1))))
{: appendtiles : appendgfx : append-portraitwords : flags : flag-to-bit : find-itile
: encode-yx : encode-itile : decode-itile}
: encode-yx : encode-itile : decode-itile : dimensions : tile-styles : style}

File diff suppressed because one or more lines are too long