(local util (require :lib.util)) (local dim (require :game.dim)) (local {: direct : draw : update} (util.require :game.entity)) (local {: edge : edge-crosses : vec*} (util.require :game.helpers)) (local {: defmethod} (util.require :lib.multimethod)) (local map (require :game.tilemap)) (local bomberman (util.hot-table ...)) (set bomberman.keymap {:up :w :down :s :left :a :right :d :bomb :x}) (defmethod draw :bomberman (fn [{:pos [x y]}] (love.graphics.setColor 0.2 0.2 0.2) (love.graphics.circle :fill x y dim.halftile))) (fn tile-at [x y rules] (-?> [x y] (map.world-to-tile) (rules.tile-at))) (fn bomberman.collides? [[x y] rules] (let [tile (tile-at x y rules)] (and tile (or tile.bomb tile.wall)))) (fn bomberman.tile-edge [c dc] (let [tc (math.floor (/ c dim.tilesize)) cfloor (* tc dim.tilesize)] (if (= dc 0) c (> dc 0) (- cfloor dim.halftile) (+ cfloor dim.tilesize dim.halftile)))) ; Collision model for Bomberman works like this (in order): ; * if the moving edge is NOT crossing over a tile boundary, allow the movement ; * if there is no solid tile in in front of Bomberman, allow the movement ; * if there are two solid tiles in front of Bomberman, push Bomberman back onto ; the tile boundary ; * if there is one solid and one empty tile in front of Bomberman, push Bomberman ; back onto the tile boundary, and push Bomberman towards the empty tile in the ; other axis ; Movement only happens along one axis; diagonal movement is not allowed. (fn horizontal [c copp] [c copp]) (fn vertical [c copp] [copp c]) (fn bomberman.axis-movement [make-xy c copp dc rules] (let [(edge-prev edge-next) (edge c dc dim.halftile) crosses (edge-crosses edge-prev edge-next dim.tilesize) neg-edge (- copp dim.halftile) neg-collides (bomberman.collides? (make-xy edge-next neg-edge) rules) pos-edge (+ copp dim.halftile -0.01) pos-collides (bomberman.collides? (make-xy edge-next pos-edge) rules) dc-edge (- (bomberman.tile-edge edge-next dc) c) dcopp (math.abs dc) [cn coppn] (if (not crosses) [dc 0] (and (not neg-collides) (not pos-collides)) [dc 0] (and neg-collides pos-collides) [dc-edge 0] neg-collides [dc-edge (math.min dcopp (- (bomberman.tile-edge (+ pos-edge dim.tilesize) 1) copp))] pos-collides [dc-edge (math.max (- dcopp) (- (bomberman.tile-edge (- neg-edge dim.tilesize) -1) copp))])] (make-xy cn coppn))) (defmethod update :bomberman (fn [entity dt rules] (set entity.vel (direct bomberman.keymap (* dim.tilesize 3))) (let [[x y] entity.pos [dx dy] (vec* entity.vel dt) [dx dy] (if (not= dx 0) (bomberman.axis-movement horizontal x y dx rules) (bomberman.axis-movement vertical y x dy rules))] (set entity.pos [(+ x dx) (+ y dy)])) (when (love.keyboard.isDown bomberman.keymap.bomb) (rules.place-bomb (map.world-to-tile entity.pos))))) (fn bomberman.new [pos] {: pos :vel [0 0] :entity :bomberman}) bomberman.hot