(local GraphicsEditView (require :editor.gfxedit)) (local style (require :core.style)) (local util (require :lib.util)) (local lume (require :lib.lume)) (local {: mouse-inside : activate : active? : checkbox : textfield : textbutton} (util.require :editor.imstate)) (local {: tilestrip-to-sprite} (util.require :editor.tiledraw)) (local {: encode-yx : encode-itile : decode-itile} (util.require :game.tiles)) (local MapEditView (GraphicsEditView:extend)) (local sprite-scale 3) (local mapw 20) (local maph 12) (local tilew (* sprite-scale 14)) (local tileh (* sprite-scale 16)) (fn MapEditView.new [self filename] (MapEditView.super.new self) (set self.sprite-scale sprite-scale) (set self.stripcache {}) (set self.mapfilename filename) (self:reload)) ; map is stored bottom-to-top (fn imap-from-xy [mx my] (+ mx -1 (* mapw (- maph my)))) (fn update-map [map mx my itile] (local imap (imap-from-xy mx my)) (local enctile (encode-itile itile)) (.. (map:sub 1 imap) (string.char enctile) (map:sub (+ imap 2)))) (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))) (decode-itile enctile)) (fn MapEditView.set-tile [self mx my itile] (set self.level.map (update-map self.level.map mx my itile))) (fn MapEditView.iobject-from-xy [self mx my ?iobj] (local iobj (or ?iobj 1)) (local obj (. self.level.objects iobj)) (when obj (if (and (= obj.x mx) (= obj.y my)) iobj (self:iobject-from-xy mx my (+ iobj 1))))) (fn MapEditView.object [self] (. self.level.objects self.iobject)) (fn move-object [objects iobjectsrc iobjectdst] (each [_ object (pairs objects)] (when (= object.link iobjectsrc) (set object.link iobjectdst))) (tset objects iobjectdst (. objects iobjectsrc)) (tset objects iobjectsrc nil) (when (. objects (+ iobjectsrc 1)) (move-object objects (+ iobjectsrc 1) iobjectsrc))) (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 xStart (coord x objectSrc.x tilew)) (local yStart (coord y objectSrc.y tileh)) (when (or toMouse? objectDest) (local xEnd (if toMouse? (love.mouse.getX) (coord x objectDest.x tilew))) (local yEnd (if toMouse? (love.mouse.getY) (coord y objectDest.y tileh))) (love.graphics.setColor (table.unpack color)) (love.graphics.line xStart yStart xEnd yEnd) (love.graphics.circle :line xEnd yEnd (/ tilew 5)) (love.graphics.setColor 1 1 1))) (fn MapEditView.draw-tilestrip [self x y my] ; stripcache leaks but honestly who cares (local tilestrip []) (var stripid "") (for [mx 1 mapw] (local itile (self:itile-from-xy mx my)) (local tile (. self.tilecache.tiles itile :gfx)) (table.insert tilestrip tile) (set stripid (.. stripid (string.char itile)))) (var sprite (. self.stripcache stripid)) (when (= sprite nil) (set sprite (tilestrip-to-sprite tilestrip)) (tset self.stripcache stripid sprite)) (love.graphics.draw sprite x y 0 self.sprite-scale self.sprite-scale)) (fn MapEditView.draw-map-editor [self x y] (activate self :map x y (* tilew mapw) (* tileh maph)) (var iobject-over nil) (for [my 1 maph] (local tiley (+ y (* (- my 1) tileh))) (self:draw-tilestrip x tiley my) (for [mx 1 mapw] (local tilex (+ x (* (- mx 1) tilew))) (local itile (self:itile-from-xy mx my)) (local iobject (self:iobject-from-xy mx my)) (when (= self.itile nil) (let [player :player] (match (. self.level player) {:x mx :y my} (renderer.draw_text style.font player tilex tiley style.text))) (love.graphics.setColor 1 1 1)) (when (and (not= iobject nil) (= self.itile nil)) (love.graphics.setColor 1 0 (if (and (= self.itile nil) (= iobject self.iobject)) 1 0)) (love.graphics.setLineWidth 3) (love.graphics.rectangle :line tilex tiley tilew tileh) (love.graphics.setColor 1 1 1)) (when (mouse-inside tilex tiley tilew tileh) (when (not= iobject nil) (set iobject-over iobject)) (renderer.draw_text style.font (string.format "%x" (encode-yx {:x mx :y my})) tilex (+ tiley 15) style.text) (love.graphics.setColor 1 1 1)) (when (and self.itile (active? self :map) (mouse-inside tilex tiley tilew tileh) (not= itile self.itile)) (self:set-tile mx my self.itile)) (when (and (= self.itile nil) (active? self :map) (mouse-inside tilex tiley tilew tileh)) (match self.imstate.left :pressed (set self.iobject-linking iobject) :released (if (and (not= iobject nil) (= self.iobject-linking iobject)) (set self.iobject iobject) (not= self.iobject-linking nil) (tset (self:linking-obj) :link iobject) (not= self.playerpos nil) (do (tset self.level self.playerpos {:x mx :y my}) (set self.playerpos nil)) (= iobject nil) (let [tile (self.tilecache:tile itile)] (table.insert self.level.objects {:x mx :y my :func (or tile.word "")}) (set self.iobject (length self.level.objects)))))))) (when (= self.itile nil) (for [iobject 1 (length self.level.objects)] (self:draw-link-line x y iobject [0 0 1 0.3])) (when (not= iobject-over nil) (self:draw-link-line x y iobject-over [0 0.5 1] false)) (when (not= self.iobject-linking nil) (if (= self.imstate.left :released) (set self.iobject-linking nil) (self:draw-link-line x y self.iobject-linking [0 1 0] true))))) (fn MapEditView.draw-object-editor [self x y] (var y y) (local object (self:object)) (set (object.func y) (textfield self "Word" object.func x y 100 200)) (set (object.name y) (textfield self "Name" object.name x (+ y 5) 100 200)) (set (object.linkword y) (textfield self "Link word" object.linkword x (+ y 5) 100 200)) (if object.link (match (textbutton self "Unlink" x (+ y 5)) (unlink yNext) (do (when unlink (set object.link nil)) (set y yNext))) (set (object.linkentity y) (textfield self "Link entity" object.linkentity x (+ y 5) 100 200))) (when (textbutton self "Delete" x (+ y 40)) (move-object self.level.objects (+ self.iobject 1) self.iobject) (set self.iobject nil))) (fn MapEditView.reload [self] (MapEditView.super.reload self) (local (loaded level) (pcall #(util.readjson self.mapfilename))) (set self.level (match (and loaded (type level)) false {:map (string.rep "\0" (* mapw maph)) :objects []} :string {:map (level:fromhex) :objects []} :table (doto level (tset :map (level.map:fromhex)))))) (fn MapEditView.save [self] (util.writejson self.mapfilename (doto (lume.clone self.level) (tset :map (self.level.map:tohex))))) (fn MapEditView.draw [self] (var x (+ self.position.x 10)) (var y (+ self.position.y 10)) (self:draw_background style.background) (love.graphics.setColor 1 1 1 1) (self:draw-map-editor x y) (when self.iobject (self:draw-object-editor (+ x (* tilew mapw) 10) y)) (set y (+ y (* tileh maph) 10)) (set self.level.tickword (textfield self "Tick word" self.level.tickword x y 100 200)) (set y (+ y 30)) (set self.level.moveword (textfield self "Move word" self.level.moveword x y 100 200)) (set y (+ y 30)) (set self.level.loadword (textfield self "Load word" self.level.loadword x y 100 200)) (set y (+ y 30)) (when (checkbox self "Edit objects" (= self.itile nil) x y) (set self.itile nil) (set self.playerpos nil)) (set y (+ y 30)) (let [player :player] (when (checkbox self (.. "Position " player) (and (= self.itile nil) (= self.playerpos player)) x y) (set self.itile nil) (set self.playerpos player)) (set y (+ y 30))) (set y (+ y 30)) (self:draw-tile-selector x y (- self.size.x 20))) (fn MapEditView.get_name [self] (.. "Map: " self.mapfilename)) MapEditView