summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob L. Kreuze <jakob@memeware.net>2018-10-23 20:03:24 -0400
committerJakob L. Kreuze <jakob@memeware.net>2018-10-23 20:03:24 -0400
commitd2a4535049a9feccb611e6a7b80c872075de0b96 (patch)
tree57ffe4c6c6c56bcb245a30b03570648b357a2555
parent0ecfdef4d6d9de2b26208349e5f6e5ca631110dc (diff)
Breaking everything with major (but necessary to address bugs in last commit) refactoring
-rw-r--r--art/swanky.pngbin5192 -> 2466 bytes
-rw-r--r--camera.fnl23
-rw-r--r--draw.fnl33
-rw-r--r--game.fnl370
-rw-r--r--map.fnl158
-rw-r--r--player.fnl76
-rw-r--r--world.fnl237
7 files changed, 491 insertions, 406 deletions
diff --git a/art/swanky.png b/art/swanky.png
index c864873..3795968 100644
--- a/art/swanky.png
+++ b/art/swanky.png
Binary files differ
diff --git a/camera.fnl b/camera.fnl
new file mode 100644
index 0000000..ba8a435
--- /dev/null
+++ b/camera.fnl
@@ -0,0 +1,23 @@
+;;;; Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
+;;;;
+;;;; This file is part of slime-the-world.
+;;;;
+;;;; slime-the-world is free software: you can redistribute it and/or modify it
+;;;; under the terms of the GNU General Public License as published by the Free
+;;;; Software Foundation, either version 3 of the License, or (at your option)
+;;;; any later version.
+;;;;
+;;;; slime-the-world is distributed in the hope that it will be useful, but
+;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+;;;; more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License along
+;;;; with slime-the-world. If not, see <http://www.gnu.org/licenses/>.
+
+;; Returns a new camera object initially focused at the given world coordinates,
+;; (`x', `y').
+(fn new-camera [x y]
+ {:x-pos x :y-pos y})
+
+{:new new-camera}
diff --git a/draw.fnl b/draw.fnl
new file mode 100644
index 0000000..11b7594
--- /dev/null
+++ b/draw.fnl
@@ -0,0 +1,33 @@
+;;;; Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
+;;;;
+;;;; This file is part of slime-the-world.
+;;;;
+;;;; slime-the-world is free software: you can redistribute it and/or modify it
+;;;; under the terms of the GNU General Public License as published by the Free
+;;;; Software Foundation, either version 3 of the License, or (at your option)
+;;;; any later version.
+;;;;
+;;;; slime-the-world is distributed in the hope that it will be useful, but
+;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+;;;; more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License along
+;;;; with slime-the-world. If not, see <http://www.gnu.org/licenses/>.
+
+;; Draws from the a given tile sheet to the screen coordinates, (`x', `y'),
+;; accounting for padding and actual tile size.
+(fn tile [x y sheet frame-x frame-y]
+ (let [x-offset (+ (. sheet :padding-x)
+ (* frame-x (+ (. sheet :width) (. sheet :padding-x))))
+ y-offset (+ (. sheet :padding-y)
+ (* frame-y (+ (. sheet :height) (. sheet :padding-y))))]
+ (let [quad (love.graphics.newQuad x-offset
+ y-offset
+ (. sheet :width)
+ (. sheet :height)
+ (: (. sheet :img) :getWidth)
+ (: (. sheet :img) :getHeight))]
+ (love.graphics.draw (. sheet :img) quad x y))))
+
+{:tile tile}
diff --git a/game.fnl b/game.fnl
index fb440f7..c450ef3 100644
--- a/game.fnl
+++ b/game.fnl
@@ -15,287 +15,161 @@
;;;; You should have received a copy of the GNU General Public License along
;;;; with slime-the-world. If not, see <http://www.gnu.org/licenses/>.
-(local bump (require :lib.bump))
-(local lume (require :lib.lume))
+;; (local camera (require :camera))
+(local player (require :player))
+(local world (require :world))
-(local map (require :map))
+;; (local camera (camera.new 0 0))
+;; (local player (player.new 128 128))
+;; (local world (map.load "sandbox"))
+;; (local collision-map (make-collision-map player (. world :tiles)))
-(local player-sheet
- {:img (love.graphics.newImage "art/swanky.png")
- :orientation-offset 128
- :width 24 :height 22
- :padding-x 8 :padding-y 10})
-(local player-animations
- {:idle {:offset 0 :frames 1}
- :walk {:offset 1 :frames 4}})
+;; (var fade-in-alpha 1)
-;; Returns a new player object, placed at the world coordinates, (`x', `y').
-(fn make-player [x y]
- {:width (. player-sheet :width)
- :height (. player-sheet :height)
+;; (fn draw [message]
+;; (love.graphics.clear 1 1 1)
+;; (draw-tiles (. world :tiles) (. camera :x-pos) (. camera :y-pos))
+;; (let [x (- (. player :x-pos) (. camera :x-pos))
+;; y (- (. player :y-pos) (. camera :y-pos))
+;; animation-offset (. player-animations (. player :animation) :offset)]
+;; (draw-sprite player-sheet
+;; animation-offset
+;; (. player :frame)
+;; (. player :orientation)
+;; x
+;; y))
+;; (love.graphics.print (string.format "%d/%d" (. world :surfaces-slimed)
+;; (. world :surfaces-total))
+;; 0 0)
- :goal-x-vel 128
- :goal-y-vel 128
+;; (when (< 0 fade-in-alpha)
+;; (love.graphics.setColor 0 0 0 fade-in-alpha)
+;; (love.graphics.rectangle "fill" 0 0 screen-width screen-height)
+;; (set fade-in-alpha (- fade-in-alpha 0.01))))
- :x-pos x
- :y-pos y
- :x-vel 0
- :y-vel 0
+;; (fn update [dt set-mode]
+;; (local gravity 128)
+;; (local camera-lock-goal-x (math.floor (- (/ screen-width 2)
+;; (/ (. player-sheet :width) 2))))
+;; (local camera-lock-goal-y (math.floor (- (/ screen-height 2)
+;; (/ (. player-sheet :height) 2))))
+;; ;; Update animations.
+;; (tset player :frame-timer (- (. player :frame-timer) 1))
+;; (when (= 0 (. player :frame-timer))
+;; (tset player :frame-timer (. player :frame-delay))
+;; (tset player :frame (+ 1 (. player :frame)))
+;; (when (<= (. player-animations (. player :animation) :frames) (. player :frame))
+;; (tset player :frame 0)))
- :orientation :right
- :animation :idle
- :frame 0
- :frame-delay 4
- :frame-timer 4
+;; ;; Update camera.
+;; (tset camera :x-pos (lume.lerp (. camera :x-pos)
+;; (- (. player :x-pos) camera-lock-goal-x) (* 4 dt)))
+;; (tset camera :y-pos (lume.lerp (. camera :y-pos)
+;; (- (. player :y-pos) camera-lock-goal-x) (* 4 dt)))
- :grounded false
+;; ;; Lock camera so that it doesn't go out of bounds.
+;; (when (> 0 (. camera :x-pos))
+;; (tset camera :x-pos 0))
- :action {:jump false
- :right false
- :left false}})
+;; (when (> 0 (. camera :y-pos))
+;; (tset camera :y-pos 0))
-(fn player-turn-right [player]
- (tset player :orientation :right)
- (tset player :action :right true)
- (tset player :action :left false)
- (tset player :x-vel (/ (. player :x-vel) 2)))
+;; (let [max-x (- (* (. map :tiles :width) (. world :width)) screen-width)]
+;; (when (>= (. camera :x-pos) max-x)
+;; (tset camera :x-pos max-x)))
-(fn player-turn-left [player]
- (tset player :orientation :left)
- (tset player :action :left true)
- (tset player :action :right false)
- (tset player :x-vel (/ (. player :x-vel) 2)))
+;; (let [max-y (- (* (. map :tiles :height) (. world :height)) screen-height)]
+;; (when (>= (. camera :y-pos) max-y)
+;; (tset camera :y-pos max-y)))
-;; Returns a new camera object initially focused at the given world coordinates,
-;; (`x', `y').
-(fn make-camera [x y]
- {:x-pos x
- :y-pos y})
+;; ;; Update the the player's velocities to reflect controls.
+;; (let [goal-x-vel (if (. player :action :right) (. player :goal-x-vel)
+;; (. player :action :left) (- (. player :goal-x-vel))
+;; 0)]
+;; (tset player :x-vel (lume.lerp (. player :x-vel) goal-x-vel dt)))
-;; Returns a new collision map containing `player' and everything in `map'.
-(fn make-collision-map [player tiles]
- (let [world (bump.newWorld 32)
- width (# (. tiles 1))
- height (# tiles)]
- (: world :add player (. player :x-pos) (. player :y-pos) (. player :width) (. player :height))
- (for [x 0 (- width 1)]
- (for [y 0 (- height 1)]
- (let [tile (. tiles (+ 1 y) (+ 1 x))]
- (when (~= :empty (. tile :type))
- (: world :add tile (* x (. map :tiles :width)) (* y (. map :tiles :height)) (. map :tiles :width) (. map :tiles :height))))))
- world))
+;; (when (and (. player :action :jump) (. player :grounded))
+;; (tset player :y-vel (- (. player :y-vel) (. player :goal-y-vel))))
-(local camera (make-camera 0 0))
-(local player (make-player 128 128))
-(local world (map.load "sandbox"))
-(local collision-map (make-collision-map player (. world :tiles)))
+;; ;; Update the y-velocity to reflect gravity.
+;; (if (and (. player :grounded) (not (. player :action :jump)))
+;; (tset player :y-vel 0)
+;; (tset player :y-vel (+ (. player :y-vel) (* gravity dt))))
-;; Draws the slime effect for a given orientation at the screen coordinates,
-;; (`x', `y').
-(fn draw-slime-decal [tile orientation x y]
- (let [decal-offset (. map :slime :offsets orientation)]
- (let [quad (love.graphics.newQuad (+ (. map :slime :padding-x)
- (* (+ (. map :slime :width)
- (. map :slime :padding-x))
- decal-offset))
- (. map :slime :padding-y)
- (. map :slime :width)
- (. map :slime :height)
- (: (. map :slime :img) :getWidth)
- (: (. map :slime :img) :getHeight))]
- (love.graphics.draw (. map :slime :img) quad x y))))
+;; ;; Perform collision detection and update the player's position accordingly.
+;; (let [goal-x (+ (. player :x-pos) (* dt (. player :x-vel)))
+;; goal-y (+ (. player :y-pos) (* dt (. player :y-vel)))]
+;; (let [(x y collisions length) (: collision-map :move player goal-x goal-y)]
+;; (tset player :x-pos x)
+;; (tset player :y-pos y)
-(fn draw-slime [tile x y]
- (each [orientation slimed (pairs (. tile :slimed))]
- (when slimed
- (draw-slime-decal tile orientation x y))))
+;; (tset player :grounded false)
+;; (each [_ collision (ipairs collisions)]
+;; (let [tile (. collision :other)]
+;; ;; Touching top.
+;; (when (> 0 (. collision :normal :y))
+;; (tset player :grounded true)
+;; (when (not (. tile :slimed :top))
+;; (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
+;; (tset tile :slimed :top true)))
-;; Draws a single tile at the screen coordinates, (`x', `y').
-(fn draw-tile [tile x y]
- (let [tile-offset (. map :tiles :offsets (. tile :type))]
- (let [quad (love.graphics.newQuad (+ (. map :tiles :padding-x)
- (* (+ (. map :tiles :width) (. map :tiles :padding-x)) tile-offset))
- (. map :tiles :padding-y)
- (. map :tiles :width)
- (. map :tiles :height)
- (: (. map :tiles :img) :getWidth)
- (: (. map :tiles :img) :getHeight))]
- (love.graphics.draw (. map :tiles :img) quad x y))))
+;; ;; Touching bottom.
+;; (when (< 0 (. collision :normal :y))
+;; (when (not (. tile :slimed :bottom))
+;; (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
+;; (tset tile :slimed :bottom true)))
-;; Drawing routine for rendering the tiles visible from a given camera offset.
-(fn draw-tiles [tiles camera-x camera-y]
- (let [how-many-x (+ 1 (/ screen-width (. map :tiles :width)))
- how-many-y (+ 1 (/ screen-height (. map :tiles :height)))
- start-x (math.floor (/ camera-x (. map :tiles :width)))
- start-y (math.floor (/ camera-y (. map :tiles :height)))
- width (# (. tiles 1))
- height (# tiles)]
- (for [x-offset 0 how-many-x]
- (for [y-offset 0 how-many-y]
- (when (and (and (>= (+ start-x x-offset) 0) (< (+ start-x x-offset) width))
- (and (>= (+ start-y y-offset) 0) (< (+ start-y y-offset) height)))
- (let [tile (. tiles (+ start-y y-offset 1) (+ start-x x-offset 1))
- x (- (* x-offset (. map :tiles :width)) (% camera-x (. map :tiles :width)))
- y (- (* y-offset (. map :tiles :height)) (% camera-y (. map :tiles :height)))]
- (when (~= :empty (. tile :type))
- (draw-tile tile x y)
- (draw-slime tile x y))))))))
+;; ;; Touching left.
+;; (when (> 0 (. collision :normal :x))
+;; (when (not (. tile :slimed :left))
+;; (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
+;; (tset tile :slimed :left true)))
-;; Draws from a given sprite sheet, `sheet' to the screen coordinates (`x', `y')
-;; with the given animation parameters.
-(fn draw-sprite [sheet animation-offset frame orientation x y]
- (let [x-offset (+ (. sheet :padding-x)
- (* frame (+ (. sheet :width) (. sheet :padding-x)))
- (if (= orientation :right) 0 (. sheet :orientation-offset)))
- y-offset (+ (. sheet :padding-y)
- (* animation-offset (+ (. sheet :height) (. sheet :padding-y))))]
- (let [quad (love.graphics.newQuad x-offset
- y-offset
- (. sheet :width)
- (. sheet :height)
- (: (. sheet :img) :getWidth)
- (: (. sheet :img) :getHeight))]
- (love.graphics.draw (. sheet :img) quad x y))))
+;; ;; Touching right.
+;; (when (< 0 (. collision :normal :x))
+;; (when (not (. tile :slimed :right))
+;; (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
+;; (tset tile :slimed :right true))))))))
-(var fade-in-alpha 1)
-(fn draw [message]
- (love.graphics.clear 1 1 1)
- (draw-tiles (. world :tiles) (. camera :x-pos) (. camera :y-pos))
- (let [x (- (. player :x-pos) (. camera :x-pos))
- y (- (. player :y-pos) (. camera :y-pos))
- animation-offset (. player-animations (. player :animation) :offset)]
- (draw-sprite player-sheet
- animation-offset
- (. player :frame)
- (. player :orientation)
- x
- y))
- (love.graphics.print (string.format "%d/%d" (. world :surfaces-slimed)
- (. world :surfaces-total))
- 0 0)
-
- (when (< 0 fade-in-alpha)
- (love.graphics.setColor 0 0 0 fade-in-alpha)
- (love.graphics.rectangle "fill" 0 0 screen-width screen-height)
- (set fade-in-alpha (- fade-in-alpha 0.01))))
-
-;; Ideally, all the components of 'update will be refactored out like this.
-(fn update-player-animation-state [player]
- (if (and (or (. player :action :right) (. player :action :left))
- (> 1 (math.abs (. player :y-vel))))
- (tset player :animation :walk)
- (tset player :animation :idle)))
-
-(fn update [dt set-mode]
- (local gravity 128)
- (local camera-lock-goal-x (math.floor (- (/ screen-width 2)
- (/ (. player-sheet :width) 2))))
- (local camera-lock-goal-y (math.floor (- (/ screen-height 2)
- (/ (. player-sheet :height) 2))))
-
- ;; Update animations.
- (update-player-animation-state player)
-
- (tset player :frame-timer (- (. player :frame-timer) 1))
- (when (= 0 (. player :frame-timer))
- (tset player :frame-timer (. player :frame-delay))
- (tset player :frame (+ 1 (. player :frame)))
- (when (<= (. player-animations (. player :animation) :frames) (. player :frame))
- (tset player :frame 0)))
-
- ;; Update camera.
- (tset camera :x-pos (lume.lerp (. camera :x-pos)
- (- (. player :x-pos) camera-lock-goal-x) (* 4 dt)))
- (tset camera :y-pos (lume.lerp (. camera :y-pos)
- (- (. player :y-pos) camera-lock-goal-x) (* 4 dt)))
-
- ;; Lock camera so that it doesn't go out of bounds.
- (when (> 0 (. camera :x-pos))
- (tset camera :x-pos 0))
-
- (when (> 0 (. camera :y-pos))
- (tset camera :y-pos 0))
+;; (fn keypressed [key set-mode]
+;; (when (= key "right")
+;; (player-turn-right player))
- (let [max-x (- (* (. map :tiles :width) (. world :width)) screen-width)]
- (when (>= (. camera :x-pos) max-x)
- (tset camera :x-pos max-x)))
+;; (when (= key "left")
+;; (player-turn-left player))
- (let [max-y (- (* (. map :tiles :height) (. world :height)) screen-height)]
- (when (>= (. camera :y-pos) max-y)
- (tset camera :y-pos max-y)))
+;; (when (= key "z")
+;; (tset player :action :jump true)))
- ;; Update the the player's velocities to reflect controls.
- (let [goal-x-vel (if (. player :action :right) (. player :goal-x-vel)
- (. player :action :left) (- (. player :goal-x-vel))
- 0)]
- (tset player :x-vel (lume.lerp (. player :x-vel) goal-x-vel dt)))
+;; (fn keyreleased [key set-mode]
+;; (when (= key "right")
+;; (tset player :action :right false))
- (when (and (. player :action :jump) (. player :grounded))
- (tset player :y-vel (- (. player :y-vel) (. player :goal-y-vel))))
+;; (when (= key "left")
+;; (tset player :action :left false))
- ;; Update the y-velocity to reflect gravity.
- (if (and (. player :grounded) (not (. player :action :jump)))
- (tset player :y-vel 0)
- (tset player :y-vel (+ (. player :y-vel) (* gravity dt))))
+;; (when (= key "z")
+;; (tset player :action :jump false)))
- ;; Perform collision detection and update the player's position accordingly.
- (let [goal-x (+ (. player :x-pos) (* dt (. player :x-vel)))
- goal-y (+ (. player :y-pos) (* dt (. player :y-vel)))]
- (let [(x y collisions length) (: collision-map :move player goal-x goal-y)]
- (tset player :x-pos x)
- (tset player :y-pos y)
+(local current-player (player.new))
+(local current-world (world.new "sandbox" current-player))
+(: current-player :position-at 128 128)
- (tset player :grounded false)
- (each [_ collision (ipairs collisions)]
- (let [tile (. collision :other)]
- ;; Touching top.
- (when (> 0 (. collision :normal :y))
- (tset player :grounded true)
- (when (not (. tile :slimed :top))
- (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
- (tset tile :slimed :top true)))
+(var camera-x 0)
+(var camera-y 0)
- ;; Touching bottom.
- (when (< 0 (. collision :normal :y))
- (when (not (. tile :slimed :bottom))
- (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
- (tset tile :slimed :bottom true)))
-
- ;; Touching left.
- (when (> 0 (. collision :normal :x))
- (when (not (. tile :slimed :left))
- (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
- (tset tile :slimed :left true)))
-
- ;; Touching right.
- (when (< 0 (. collision :normal :x))
- (when (not (. tile :slimed :right))
- (tset world :surfaces-slimed (+ 1 (. world :surfaces-slimed)))
- (tset tile :slimed :right true))))))))
-
-
-(fn keypressed [key set-mode]
- (when (= key "right")
- (player-turn-right player))
-
- (when (= key "left")
- (player-turn-left player))
-
- (when (= key "z")
- (tset player :action :jump true)))
+(fn draw [message]
+ (: current-world :draw camera-x camera-y screen-width screen-height)
+ (: current-player :draw 128 128))
-(fn keyreleased [key set-mode]
- (when (= key "right")
- (tset player :action :right false))
+(fn update [dt set-mode])
- (when (= key "left")
- (tset player :action :left false))
+(fn keypressed [key set-mode])
- (when (= key "z")
- (tset player :action :jump false)))
+(fn keyreleased [key set-mode])
{:draw draw
:update update
diff --git a/map.fnl b/map.fnl
deleted file mode 100644
index efaf6bb..0000000
--- a/map.fnl
+++ /dev/null
@@ -1,158 +0,0 @@
-;;;; Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
-;;;;
-;;;; This file is part of slime-the-world.
-;;;;
-;;;; slime-the-world is free software: you can redistribute it and/or modify it
-;;;; under the terms of the GNU General Public License as published by the Free
-;;;; Software Foundation, either version 3 of the License, or (at your option)
-;;;; any later version.
-;;;;
-;;;; slime-the-world is distributed in the hope that it will be useful, but
-;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-;;;; more details.
-;;;;
-;;;; You should have received a copy of the GNU General Public License along
-;;;; with slime-the-world. If not, see <http://www.gnu.org/licenses/>.
-
-(local tile-sheet
- {:img (love.graphics.newImage "art/tiles.png")
- :width 32 :height 32
- :padding-x 8 :padding-y 8
- :offsets {:bricks 0}})
-
-(local tile-values
- {:empty [1 0 0 0]
- :bricks [0 0 0 1]})
-
-(local slime
- {:img (love.graphics.newImage "art/slime.png")
- :width 32 :height 32
- :padding-x 8 :padding-y 8
- :offsets {:top 0 :bottom 1 :left 2 :right 3}})
-
-(fn tile-at [tiles x y]
- (if (and (>= x 0)
- (< x (. tiles :width))
- (>= y 0)
- (< y (. tiles :height)))
- (. tiles :tiles (+ 1 y) (+ 1 x))
- nil))
-
-(fn find-first [tile-type tiles]
- (var res nil)
- (each [_ row (ipairs (. tiles :tiles))]
- (each [_ tile (ipairs row)]
- (when (= tile-type (. tile :type))
- (set res tile))))
- res)
-
-(fn tile-checked [tile checked]
- (var res false)
- (each [_ other (ipairs checked)]
- (when (and (= (. tile :x-pos) (. other :x-pos))
- (= (. tile :y-pos) (. other :y-pos)))
- (set res true)))
- res)
-
-(fn count-surfaces-recur [tiles tile checked]
- (var surfaces 0)
-
- (when (not (tile-checked tile checked))
- (table.insert checked tile)
-
- (let [x (/ (. tile :x-pos) (. tile-sheet :width))
- y (/ (. tile :y-pos) (. tile-sheet :height))
- width (. tiles :width)
- height (. tiles :height)]
- (when (>= x 0)
- (let [tile (tile-at tiles (- x 1) y)]
- (if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur tiles tile checked)))
- (set surfaces (+ 1 surfaces)))))
-
- (when (< x width)
- (let [tile (tile-at tiles (+ x 1) y)]
- (if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur tiles tile checked)))
- (set surfaces (+ 1 surfaces)))))
-
- (when (>= y 0)
- (let [tile (tile-at tiles x (- y 1))]
- (if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur tiles tile checked)))
- (set surfaces (+ 1 surfaces)))))
-
- (when (< y height)
- (let [tile (tile-at tiles x (+ y 1))]
- (if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur tiles tile checked)))
- (set surfaces (+ 1 surfaces)))))))
-
- surfaces)
-
-(fn count-surfaces [tiles]
- (let [seed (find-first :empty tiles)]
- (when seed
- (count-surfaces-recur tiles seed []))))
-
-;; Creates a new tile object of the given `type-type' positioned at world
-;; coordinates (`x', `y').
-(fn make-tile [tile-type x y]
- {:x-pos x
- :y-pos y
- :type tile-type
- :slimed {:top false
- :bottom false
- :left false
- :right false}})
-
-;; Returns the tile object for a given pixel in `image'.
-(fn get-tile [image x y]
- (var res (make-tile :empty (* x (. tile-sheet :width)) (* y (. tile-sheet :width))))
- (let [(r g b a) (: image :getPixel x y)]
- (each [tile-type colors (pairs tile-values)]
- (when (and (= (. colors 1) r)
- (= (. colors 2) g)
- (= (. colors 3) b)
- (= (. colors 4) a))
- (tset res :type tile-type))))
- res)
-
-;; Loads the tile layout of the level from the given ImageData, returning a
-;; rectangular two-dimensional array.
-(fn load-tiles [image]
- (var tiles [])
- (let [width (: image :getWidth)
- height (: image :getHeight)]
- (for [y 0 (- height 1)]
- (var row [])
- (for [x 0 (- width 1)]
- (table.insert row (get-tile image x y)))
- (table.insert tiles row))
- (values tiles width height)))
-
-;; Returns the metadata and objects stored in the metadata file at the given
-;; path.
-(fn load-meta [path]
- (fennel.dofile path))
-
-;; Loads the map of the given name. Will error out if either 'maps/${name}.png'
-;; or 'maps/${name}.fnl' do not exist.
-(fn load [name]
- (let [tiles-path (.. "maps/" name ".png")
- meta-path (.. "maps/" name ".fnl")]
- (let [(tiles width height) (load-tiles (love.image.newImageData tiles-path))
- tiles-temp {:tiles tiles :width width :height height}]
- {:tiles tiles
- :width width
- :height height
-
- :surfaces-slimed 0
- :surfaces-total (count-surfaces tiles-temp)
-
- :meta (load-meta meta-path)})))
-
-{:tiles tile-sheet
- :slime slime
- :load load}
diff --git a/player.fnl b/player.fnl
new file mode 100644
index 0000000..250b70c
--- /dev/null
+++ b/player.fnl
@@ -0,0 +1,76 @@
+;;;; Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
+;;;;
+;;;; This file is part of slime-the-world.
+;;;;
+;;;; slime-the-world is free software: you can redistribute it and/or modify it
+;;;; under the terms of the GNU General Public License as published by the Free
+;;;; Software Foundation, either version 3 of the License, or (at your option)
+;;;; any later version.
+;;;;
+;;;; slime-the-world is distributed in the hope that it will be useful, but
+;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+;;;; more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License along
+;;;; with slime-the-world. If not, see <http://www.gnu.org/licenses/>.
+
+(local draw (require :draw))
+
+(fn move-right [player]
+ (tset player :orientation :right)
+ (tset player :action :right true)
+ (tset player :action :left false)
+ (tset player :x-vel (/ (. player :x-vel) 2)))
+
+(fn move-left [player]
+ (tset player :orientation :left)
+ (tset player :action :left true)
+ (tset player :action :right false)
+ (tset player :x-vel (/ (. player :x-vel) 2)))
+
+(fn position-at [player x y]
+ (tset player :x-pos x)
+ (tset player :y-pos y))
+
+(local player-sheet
+ {:img (love.graphics.newImage "art/swanky.png")
+ :orientation-offset 128
+ :width 24 :height 22
+ :padding-x 8 :padding-y 10})
+
+(fn draw-player [player x y]
+ (draw.tile x y player-sheet 0 0))
+
+;; Returns a new player object.
+(fn new-player []
+ {:width (. player-sheet :width)
+ :height (. player-sheet :height)
+
+ :goal-x-vel 128
+ :goal-y-vel 128
+
+ :x-pos 0
+ :y-pos 0
+
+ :x-vel 0
+ :y-vel 0
+
+ :orientation :right
+ ;; :frame 0
+ ;; :frame-delay 4
+ ;; :frame-timer 4
+
+ :grounded false
+
+ :action {:jump false
+ :right false
+ :left false}
+
+ :position-at position-at
+ :move-right move-right
+ :move-left move-left
+
+ :draw draw-player})
+
+{:new new-player}
diff --git a/world.fnl b/world.fnl
new file mode 100644
index 0000000..a31e5c0
--- /dev/null
+++ b/world.fnl
@@ -0,0 +1,237 @@
+;;;; Copyright (C) 2018 Jakob L. Kreuze, All Rights Reserved.
+;;;;
+;;;; This file is part of slime-the-world.
+;;;;
+;;;; slime-the-world is free software: you can redistribute it and/or modify it
+;;;; under the terms of the GNU General Public License as published by the Free
+;;;; Software Foundation, either version 3 of the License, or (at your option)
+;;;; any later version.
+;;;;
+;;;; slime-the-world is distributed in the hope that it will be useful, but
+;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+;;;; more details.
+;;;;
+;;;; You should have received a copy of the GNU General Public License along
+;;;; with slime-the-world. If not, see <http://www.gnu.org/licenses/>.
+
+(local bump (require :lib.bump))
+(local lume (require :lib.lume))
+
+(local draw (require :draw))
+
+;; Returns the tile at the given grid coordinates.
+(fn tile-at [map x y]
+ (if (and (>= x 0)
+ (< x (. map :width))
+ (>= y 0)
+ (< y (. map :height)))
+ (. map :tiles (+ 1 y) (+ 1 x))
+ nil))
+
+(local tile-sheet
+ {:img (love.graphics.newImage "art/tiles.png")
+ :width 32 :height 32
+ :padding-x 8 :padding-y 8
+ :offsets {:bricks 0}})
+
+(local slime-sheet
+ {:img (love.graphics.newImage "art/slime.png")
+ :width 32 :height 32
+ :padding-x 8 :padding-y 8
+ :offsets {:top 0 :bottom 1 :left 2 :right 3}})
+
+;; Draws the slime decal for a given orientation at the screen coordinates,
+;; (`x', `y').
+(fn draw-slime-decal [orientation x y]
+ (let [tile-offset (. slime-sheet :offsets orientation)]
+ (draw.tile x y slime-sheet tile-offset 0)))
+
+
+;; Draws all slimed surfaces for a tile at screen coordinates, (`x', `y').
+(fn draw-slime [tile x y]
+ (each [orientation slimed (pairs (. tile :slimed))]
+ (when slimed
+ (draw-slime-decal tile orientation x y))))
+
+;; Draws a single tile at the screen coordinates, (`x', `y').
+(fn draw-tile [tile x y]
+ (let [tile-offset (. tile-sheet :offsets (. tile :type))]
+ (draw.tile x y tile-sheet tile-offset 0)
+ (draw-slime tile x y)))
+
+;; ;; Drawing routine for rendering the tiles visible from a given camera offset.
+(fn draw-map [map camera-x camera-y screen-width screen-height]
+ (let [tile-width (. tile-sheet :width)
+ tile-height (. tile-sheet :height)
+ how-many-x (+ 1 (/ screen-width tile-width))
+ how-many-y (+ 1 (/ screen-height tile-height))
+ start-x (math.floor (/ camera-x tile-width))
+ start-y (math.floor (/ camera-y tile-height))
+ width (. map :width)
+ height (. map :height)]
+ (for [x-offset 0 how-many-x]
+ (for [y-offset 0 how-many-y]
+ (when (and (and (>= (+ start-x x-offset) 0) (< (+ start-x x-offset) width))
+ (and (>= (+ start-y y-offset) 0) (< (+ start-y y-offset) height)))
+ (let [tile (tile-at map (+ start-x x-offset) (+ start-y y-offset))
+ screen-x (- (* x-offset tile-width) (% camera-x tile-width))
+ screen-y (- (* y-offset tile-height) (% camera-y tile-height))]
+ (when (~= :empty (. tile :type))
+ (draw-tile tile screen-x screen-y))))))))
+
+;; Returns whether or not `tile' exists in `checked'.
+(fn tile-checked [tile checked]
+ (var res false)
+ (each [_ other (ipairs checked)]
+ (when (and (= (. tile :x-pos) (. other :x-pos))
+ (= (. tile :y-pos) (. other :y-pos)))
+ (set res true)))
+ res)
+
+;; Modified implementation of <https://en.wikipedia.org/wiki/Flood_fill>.
+(fn count-surfaces-recur [map tile checked]
+ (var surfaces 0)
+
+ (when (not (tile-checked tile checked))
+ (table.insert checked tile)
+
+ (let [x (/ (. tile :x-pos) (. tile-sheet :width))
+ y (/ (. tile :y-pos) (. tile-sheet :height))
+ width (. map :width)
+ height (. map :height)]
+ (when (>= x 0)
+ (let [tile (tile-at map (- x 1) y)]
+ (if (= :empty (. tile :type))
+ (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ 1 surfaces)))))
+
+ (when (< x width)
+ (let [tile (tile-at map (+ x 1) y)]
+ (if (= :empty (. tile :type))
+ (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ 1 surfaces)))))
+
+ (when (>= y 0)
+ (let [tile (tile-at map x (- y 1))]
+ (if (= :empty (. tile :type))
+ (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ 1 surfaces)))))
+
+ (when (< y height)
+ (let [tile (tile-at map x (+ y 1))]
+ (if (= :empty (. tile :type))
+ (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ 1 surfaces)))))))
+
+ surfaces)
+
+;; Returns some tile in `map' of type `tile-type', or nil if no such tile is
+;; present.
+(fn find-any [tile-type map]
+ (var res nil)
+ (let [width (. map :width)
+ height (. map :height)]
+ (for [x 0 (- width 1)]
+ (for [y 0 (- height 1)]
+ (let [tile (tile-at map x y)]
+ (when (= tile-type (. tile :type))
+ (set res tile))))))
+ res)
+
+;; Returns the number of slime-able surfaces in the given grid of tiles.
+(fn count-surfaces [map]
+ (let [seed (find-any :empty map)]
+ (when seed
+ (count-surfaces-recur map seed []))))
+
+(fn add-player-to-collision-map [collision-map player]
+ (let [x (. player :x-pos)
+ y (. player :y-pos)
+ width (. player :width)
+ height (. player :height)]
+ (: collision-map :add player x y width height)))
+
+(fn add-tile-to-collision-map [collision-map tile]
+ (let [x (. tile :x-pos)
+ y (. tile :y-pos)
+ width (. tile :width)
+ height (. tile :height)]
+ (: collision-map :add tile (* x width) (* y height) width height)))
+
+;; Returns a new collision map containing `player' and everything in `map'.
+(fn new-collision-map [map player]
+ (let [collision-map (bump.newWorld 32)
+ width (. map :width)
+ height (. map :height)]
+ (add-player-to-collision-map collision-map player)
+ (for [x 0 (- width 1)]
+ (for [y 0 (- height 1)]
+ (let [tile (tile-at map x y)]
+ (when (~= :empty (. tile :type))
+ (add-tile-to-collision-map collision-map tile)))))
+ collision-map))
+
+;; Creates a new tile object of the given `type-type' positioned at world
+;; coordinates (`x', `y').
+(fn new-tile [tile-type x y]
+ {:width (. tile-sheet :width)
+ :height (. tile-sheet :height)
+
+ :x-pos x
+ :y-pos y
+
+ :type tile-type
+
+ :slimed {:top false
+ :bottom false
+ :left false
+ :right false}})
+
+;; Returns the tile object for a given pixel in `image'.
+(fn get-tile [image x y]
+ (local tile-values
+ {:empty [1 0 0 0]
+ :bricks [0 0 0 1]})
+ (var res (new-tile :empty (* x (. tile-sheet :width)) (* y (. tile-sheet :height))))
+ (let [(r g b a) (: image :getPixel x y)]
+ (each [tile-type colors (pairs tile-values)]
+ (when (and (= (. colors 1) r)
+ (= (. colors 2) g)
+ (= (. colors 3) b)
+ (= (. colors 4) a))
+ (tset res :type tile-type))))
+ res)
+
+;; Loads the tile layout of the level from the given ImageData, returning a
+;; rectangular two-dimensional array.
+(fn load-tiles [image]
+ (var tiles [])
+ (let [width (: image :getWidth)
+ height (: image :getHeight)]
+ (for [y 0 (- height 1)]
+ (var row [])
+ (for [x 0 (- width 1)]
+ (table.insert row (get-tile image x y)))
+ (table.insert tiles row))
+ {:tiles tiles :width width :height height}))
+
+;; Returns the metadata and objects stored in the metadata file at the given
+;; path.
+(fn load-meta [path]
+ (fennel.dofile path))
+
+;; Loads the map of the given name into a new world containing `player'. Will
+;; error out if either 'maps/${name}.png' or 'maps/${name}.fnl' do not exist.
+(fn new-world [name player]
+ (let [tiles-path (.. "maps/" name ".png")
+ meta-path (.. "maps/" name ".fnl")
+ res (load-tiles (love.image.newImageData tiles-path))
+ res (lume.extend res {:surfaces-slimed 0 :meta (load-meta meta-path)})
+ res (lume.extend res {:surfaces-total (count-surfaces res)})
+ res (lume.extend res {:collision-map (new-collision-map res player)})
+ res (lume.extend res {:draw draw-map})]
+ res))
+
+{:tile-at tile-at
+ :new new-world}