(local GraphicsEditView (require :editor.gfxedit)) (local style (require :core.style)) (local util (require :lib.util)) (local lume (require :lib.lume)) (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 : dimensions} (util.require :game.tiles)) (local actions (require :editor.actions)) (local MapEditView (GraphicsEditView:extend)) (local sprite-scale 3) (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))) (fn MapEditView.scaled-dimensions [self ?ilayer] (let [dim (lume.clone (self:dimensions))] (each [_ key (ipairs [:tilew :tileh :xstagger :ystagger])] (when (. dim key) (tset dim key (* sprite-scale (. dim key))))) dim)) (fn MapEditView.mapw [self ?ilayer] (. (self:dimensions ?ilayer) :mapw)) (fn MapEditView.maph [self ?ilayer] (. (self:dimensions ?ilayer) :maph)) (fn MapEditView.tilew [self ?ilayer] (. (self:scaled-dimensions ?ilayer) :tilew)) (fn MapEditView.tileh [self ?ilayer] (. (self:scaled-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 MapEditView.imap-from-xy [self mx my] (+ mx -1 (* (self:mapw) (- (self:maph) 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 (+ (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] (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)) (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.levels [self] (when (= files.game.levels nil) (set files.game.levels [])) files.game.levels) (fn MapEditView.draw-map-selector [self x y] (renderer.draw_text style.font "Map" x (+ y (/ style.padding.y 2)) style.text) (let [options {} level-count (length (self:levels)) _ (do (for [i 1 level-count] (tset options i i)) (table.insert options :New)) (ilevel yNext) (dropdown self :map-selector self.ilevel options (+ x (* 50 SCALE)) y (* 100 SCALE))] (when (not= ilevel self.ilevel) (set self.ilevel (if (= ilevel :New) (+ level-count 1) ilevel)) (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) (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 ?ilayer] ; stripcache leaks but honestly who cares (local tilestrip []) (var stripid "") (for [mx 1 (self: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 (self:layer-type ?ilayer))) (tset self.stripcache stripid sprite)) (love.graphics.draw sprite x y 0 self.sprite-scale self.sprite-scale)) (fn MapEditView.mapsize [self ilayer] (let [{: mapw : maph : tilew : tileh : xstagger : ystagger} (self:scaled-dimensions ilayer) intileh (or ystagger tileh)] [(+ (or xstagger 0) (* mapw tilew)) (+ tileh (* (- maph 1) intileh))])) (fn MapEditView.draw-map-editor [self x y ilayer] (love.graphics.setColor 1 1 1 1) (local button-state self.imstate.left) (local {: mapw : maph : tilew : tileh : xstagger : ystagger} (self:scaled-dimensions ilayer)) (local intileh (or ystagger tileh)) (local [mappw mapph] (self:mapsize ilayer)) (activate self :map x y mappw mapph) (var iobject-over nil) (for [my 1 maph] (local tiley (+ y (* (- my 1) (or ystagger tileh)))) (local intiley (+ tiley (- tileh intileh))) (local xoff (if (and xstagger (= (% my 2) 0)) xstagger 0)) (self:draw-tilestrip (+ x xoff) tiley my ilayer) (for [mx 1 mapw] (local tilex (+ x (* (- mx 1) tilew) xoff)) (local itile (self:itile-from-xy mx my)) (local iobject (self:iobject-from-xy mx my)) (when (= self.itile nil) (each [_ player (ipairs (or files.game.players [:player]))] (match (. self.level player) {:x mx :y my} (renderer.draw_text style.font player tilex intiley 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 intiley tilew intileh) (love.graphics.setColor 1 1 1)) (when (mouse-inside tilex intiley tilew intileh) (when (not= iobject nil) (set iobject-over iobject)) (local labely (math.floor (+ intiley (- (or ystagger 0)) (/ (- (if ystagger (* ystagger 2) tileh) (style.font:get_height)) 2)))) (renderer.draw_text style.font (string.format "%x" (encode-yx {:x mx :y my})) (+ tilex 20) labely style.text) (love.graphics.setColor 1 1 1)) (when (and self.itile (active? self :map) (mouse-inside tilex intiley tilew intileh) (not= itile self.itile)) (self:set-tile mx my self.itile)) (when (and (= self.itile nil) (active? self :map) (mouse-inside tilex intiley tilew intileh)) (match button-state :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)))) mapph) (fn condition-label [flag] (if flag {:label flag : flag} {:label ""})) (fn condition-options [] (let [options [(condition-label nil)]] (each [_ flag (ipairs (or files.game.flags []))] (table.insert options (condition-label flag))) options)) (fn MapEditView.draw-object-code-editor [self object x y] (var y y) (var istep-to-delete nil) (when (not object.steps) (set object.steps [])) (each [istep step (ipairs object.steps)] (when (textbutton self "X" (+ x (* 280 SCALE)) y) (set istep-to-delete istep)) (set step.condition (. (dropdown self [:code-condition istep] (condition-label step.condition) (condition-options) (+ x (* 100 SCALE) style.padding.x) y (* 100 SCALE)) :flag)) (set (step.action y) (dropdown self [:code-action istep] (or step.action (. actions.actionlist 1)) actions.actionlist x y (* 100 SCALE))) (set y (actions.edit step self x y (* 300 SCALE) istep)) (set y (+ y style.padding.y))) (when istep-to-delete (table.remove object.steps istep-to-delete)) (let [(do-new y) (textbutton self "+ New Step" x (+ y style.padding.y))] (when do-new (table.insert object.steps {})) y)) (fn advanced? [object] (or object.advanced (and (= object.advanced nil) (not= object.func "") (not= object.func nil)))) (fn MapEditView.draw-object-advanced-editor [self object x y] (let [(func y) (textfield self "Word" object.func x y (* 100 SCALE) (* 200 SCALE)) (name y) (textfield self "Name" object.name x (+ y style.padding.y) (* 100 SCALE) (* 200 SCALE)) (linkword y) (textfield self "Link word" object.linkword x (+ y style.padding.y) (* 100 SCALE) (* 200 SCALE)) (do-unlink y) (if object.link (textbutton self "Unlink" x (+ y style.padding.y)) (values false y)) (linkentity y) (if object.link (values object.linkentity y) (textfield self "Link entity" object.linkentity x (+ y style.padding.y) (* 100 SCALE) (* 200 SCALE)))] (lume.extend object {: func : name : linkword : linkentity}) (when do-unlink (set object.link nil)) y)) (fn MapEditView.draw-object-editor [self x y] (let [object (self:object) y (if (advanced? object) (self:draw-object-advanced-editor object x y) (self:draw-object-code-editor object x y)) new-flag-name (textbox self :new-flag-name self.new-flag-name x (+ y style.padding.y) (* 200 SCALE)) (mk-new-flag y) (textbutton self "+ New Flag" (+ x (* 200 SCALE) style.padding.x) (+ y style.padding.y)) do-delete (textbutton self "Delete" x (+ y (* style.padding.y 2))) (do-advanced y) (textbutton self (if (advanced? object) "Simple" "Advanced") (+ x (* 150 SCALE)) (+ y (* style.padding.y 2)))] (set self.new-flag-name new-flag-name) (when mk-new-flag (when (= files.game.flags nil) (set files.game.flags [])) (table.insert files.game.flags new-flag-name) (set self.new-flag-name "")) (when do-delete (move-object self.level.objects (+ self.iobject 1) self.iobject) (set self.iobject nil)) (when do-advanced (set object.advanced (not (advanced? object)))) y)) (fn MapEditView.load-level [self] (set self.stripcache {}) (when (= (. (self:levels) self.ilevel) nil) (tset (self:levels) self.ilevel {:map (self:empty-map) :objects []})) (set self.level (. (self:levels) self.ilevel)) (set self.iobject nil)) (fn MapEditView.reload [self] (MapEditView.super.reload self) (self:load-level)) (fn MapEditView.draw [self] (var x (+ self.position.x style.padding.x (- self.scroll.x))) (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:scaled-dimensions)) (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)) (set y (+ y (self:draw-map-editor x y) style.padding.y)) (set y (+ y (self:draw-tile-selector x y (if editor-on-side (* tilew mapw) (- self.size.x (* style.padding.x 2)))))) (set (self.level.tickword y) (textfield self "Tick word" self.level.tickword x (+ y style.padding.y) (* 100 SCALE) (* 200 SCALE))) (set (self.level.moveword y) (textfield self "Move word" self.level.moveword x (+ y style.padding.y) (* 100 SCALE) (* 200 SCALE))) (set (self.level.loadword y) (textfield self "Load word" self.level.loadword x (+ y style.padding.y) (* 100 SCALE) (* 200 SCALE))) (let [(checked y-new) (checkbox self "Edit objects" (= self.itile nil) x (+ y style.padding.y)) _ (when checked (set self.itile nil) (set self.playerpos nil))] (set y y-new) (each [_ player (ipairs (or files.game.players [:player]))] (let [(checked y-new) (checkbox self (.. "Position " player) (and (= self.itile nil) (= self.playerpos player)) x (+ y style.padding.y))] (when checked (set self.itile nil) (set self.playerpos player)) (set y y-new)))) (each [_ levelflag (ipairs (or files.game.levelflags []))] (let [(checked y-new) (checkbox self levelflag (. self.level levelflag) x (+ y style.padding.y))] (when checked (tset self.level levelflag (not (. self.level levelflag)))) (set y y-new))) (when self.iobject (set y (math.max y (if editor-on-side (self:draw-object-editor (+ x (* tilew mapw) style.padding.x) ytop) (self:draw-object-editor x (+ y style.padding.y)))))) (set self.scrollheight (+ y (- self.position.y) self.scroll.y style.padding.y))) (fn MapEditView.get_name [self] (.. "Map " self.ilevel)) MapEditView