summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob L. Kreuze <jakob@memeware.net>2018-10-24 15:44:36 -0400
committerJakob L. Kreuze <jakob@memeware.net>2018-10-24 15:44:36 -0400
commitc64189ebe56e8132282335fcb9cf5e48e5c3961e (patch)
treef3246d419db035d559a36622a70def583456fe1e
parentd2a4535049a9feccb611e6a7b80c872075de0b96 (diff)
Reimplementing features from two commits back + fixes for significant graphical bugs
-rw-r--r--camera.fnl45
-rw-r--r--game.fnl33
-rw-r--r--player.fnl61
-rw-r--r--world.fnl143
4 files changed, 225 insertions, 57 deletions
diff --git a/camera.fnl b/camera.fnl
index ba8a435..b46142d 100644
--- a/camera.fnl
+++ b/camera.fnl
@@ -15,9 +15,46 @@
;;;; 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})
+(local lume (require :lib.lume))
+
+(local tile-sheet (. (require :world) :tile-sheet))
+
+;; TODO: Document this
+(fn focus-on-object [camera object dt]
+ (let [last-x (. camera :x-pos)
+ last-y (. camera :y-pos)
+ max-x (. camera :max-x)
+ max-y (. camera :max-y)
+ object-x (. object :x-pos)
+ object-y (. object :y-pos)
+ width (. object :width)
+ height (. object :height)
+ screen-width (. camera :screen-width)
+ screen-height (. camera :screen-height)
+ x-offset (math.floor (- (/ screen-width 2) (/ width 2)))
+ y-offset (math.floor (- (/ screen-height 2) (/ height 2)))
+ x (lume.lerp last-x (- object-x x-offset) (* 4 dt))
+ x (lume.clamp x 0 max-x)
+ y (lume.lerp last-y (- object-y y-offset) (* 4 dt))
+ y (lume.clamp y 0 max-y)]
+ (values x y)))
+
+;; Returns a new camera object, aware of the bounds of `world' and the camera
+;; lock parameters entailed by the given screen dimensions.
+(fn new-camera [world screen-width screen-height]
+ (let [tile-width (. tile-sheet :width)
+ tile-height (. tile-sheet :height)
+ max-x (- (* tile-width (. world :width)) screen-width)
+ max-y (- (* tile-height (. world :height)) screen-height)]
+ {:x-pos 0
+ :y-pos 0
+
+ :screen-width screen-width
+ :screen-height screen-height
+
+ :max-x max-x
+ :max-y max-y
+
+ :focus-on-object focus-on-object}))
{:new new-camera}
diff --git a/game.fnl b/game.fnl
index c450ef3..d95e3db 100644
--- a/game.fnl
+++ b/game.fnl
@@ -15,7 +15,7 @@
;;;; 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 camera (require :camera))
+(local camera (require :camera))
(local player (require :player))
(local world (require :world))
@@ -50,7 +50,7 @@
;; (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)
@@ -156,20 +156,31 @@
(local current-player (player.new))
(local current-world (world.new "sandbox" current-player))
-(: current-player :position-at 128 128)
-
-(var camera-x 0)
-(var camera-y 0)
+(local current-camera (camera.new current-world screen-width screen-height))
+(: current-world :position-player-at 128 128)
(fn draw [message]
- (: current-world :draw camera-x camera-y screen-width screen-height)
- (: current-player :draw 128 128))
+ (let [camera-x (. current-camera :x-pos)
+ camera-y (. current-camera :y-pos)]
+ (: current-world :draw camera-x camera-y screen-width screen-height)))
-(fn update [dt set-mode])
+(fn update [dt set-mode]
+ (: current-world :update dt)
+ (let [(camera-x camera-y) (: current-camera :focus-on-object current-player dt)]
+ (tset current-camera :x-pos camera-x)
+ (tset current-camera :y-pos camera-y)))
-(fn keypressed [key set-mode])
+(fn keypressed [key set-mode]
+ (let [method (if (= key "right") :move-right
+ (= key "left") :move-left)]
+ (when method
+ (: current-player method))))
-(fn keyreleased [key set-mode])
+(fn keyreleased [key set-mode]
+ (let [method (if (= key "right") :stop-right
+ (= key "left") :stop-left)]
+ (when method
+ (: current-player method))))
{:draw draw
:update update
diff --git a/player.fnl b/player.fnl
index 250b70c..b26c310 100644
--- a/player.fnl
+++ b/player.fnl
@@ -15,23 +15,65 @@
;;;; 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 lume (require :lib.lume))
+
(local draw (require :draw))
+(local gravity 128)
+
+;; Returns a pair, (x, y) that represents the player's next position, assuming
+;; that it did /not/ collide with anything. The role of collision detection, and
+;; further, setting the player's position, is on the world.
+(fn next-position [player dt]
+ (let [x (+ (. player :x-pos) (* dt (. player :x-vel)))
+ y (+ (. player :y-pos) (* dt (. player :y-vel)))]
+ (values x y)))
+
+(fn next-x-vel [player dt]
+ (let [last-x-vel (. player :x-vel)
+ moving-right (. player :action :right)
+ moving-left (. player :action :left)
+ abs-x-vel (. player :goal-x-vel)
+ goal-x-vel (if moving-right abs-x-vel moving-left (- abs-x-vel) 0)]
+ (lume.lerp last-x-vel goal-x-vel dt)))
+
+(fn next-y-vel [player dt]
+ (let [last-y-vel (. player :y-vel)
+ jumping (. player :action :jump)
+ grounded (. player :grounded)
+ jumping-y-vel (- last-y-vel (. player :goal-y-vel))]
+ (if (and jumping grounded) jumping-y-vel
+ grounded 0
+ :else (+ (. player :y-vel) (* gravity dt)))))
+
+(fn impact-bottom [player]
+ (tset player :grounded true))
+
+(fn impact-top [player])
+
+(fn impact-left [player])
+
+(fn impact-right [player])
+
(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 stop-right [player]
+ (tset player :action :right 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))
+(fn stop-left [player]
+ (tset player :action :left false)
+ (tset player :x-vel (/ (. player :x-vel) 2)))
(local player-sheet
{:img (love.graphics.newImage "art/swanky.png")
@@ -40,7 +82,8 @@
:padding-x 8 :padding-y 10})
(fn draw-player [player x y]
- (draw.tile x y player-sheet 0 0))
+ (let [x-offset (if (= (. player :orientation) :left) 4 0)]
+ (draw.tile x y player-sheet x-offset 0)))
;; Returns a new player object.
(fn new-player []
@@ -67,9 +110,17 @@
:right false
:left false}
- :position-at position-at
+ :next-position next-position
+ :next-x-vel next-x-vel
+ :next-y-vel next-y-vel
+ :impact-bottom impact-bottom
+ :impact-top impact-top
+ :impact-left impact-left
+ :impact-right impact-right
:move-right move-right
+ :stop-right stop-right
:move-left move-left
+ :stop-left stop-left
:draw draw-player})
diff --git a/world.fnl b/world.fnl
index a31e5c0..661bcff 100644
--- a/world.fnl
+++ b/world.fnl
@@ -20,13 +20,22 @@
(local draw (require :draw))
+;; Updates the position of the player contained in `world' to the world
+;; coordinates, (`x', `y').
+(fn position-player-at [world x y]
+ (let [player (. world :player)
+ collision-map (. world :collision-map)]
+ (tset player :x-pos x)
+ (tset player :y-pos y)
+ (: collision-map :update player x y)))
+
;; Returns the tile at the given grid coordinates.
-(fn tile-at [map x y]
+(fn tile-at [world x y]
(if (and (>= x 0)
- (< x (. map :width))
+ (< x (. world :width))
(>= y 0)
- (< y (. map :height)))
- (. map :tiles (+ 1 y) (+ 1 x))
+ (< y (. world :height)))
+ (. world :tiles (+ 1 y) (+ 1 x))
nil))
(local tile-sheet
@@ -47,12 +56,11 @@
(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))))
+ (draw-slime-decal orientation x y))))
;; Draws a single tile at the screen coordinates, (`x', `y').
(fn draw-tile [tile x y]
@@ -61,25 +69,43 @@
(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]
+(fn draw-map [world 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)]
+ width (. world :width)
+ height (. world :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))
+ (let [tile (tile-at world (+ 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))]
+ screen-x (lume.round screen-x)
+ screen-y (- (* y-offset tile-height) (% camera-y tile-height))
+ screen-y (lume.round screen-y)]
(when (~= :empty (. tile :type))
(draw-tile tile screen-x screen-y))))))))
+(fn draw-world [world camera-x camera-y screen-width screen-height]
+ (love.graphics.clear 0.1 0.1 0.1)
+ (draw-map world camera-x camera-y screen-width screen-height)
+ (let [player (. world :player)
+ player-x (. player :x-pos)
+ player-y (. player :y-pos)
+ x (- player-x camera-x)
+ y (- player-y camera-y)]
+ (: player :draw x y))
+ ;; TODO: Refactor this into multiple functions
+ (love.graphics.print (string.format "%d/%d" (. world :surfaces-slimed)
+ (. world :surfaces-total))
+ 0 0))
+
+
+
;; Returns whether or not `tile' exists in `checked'.
(fn tile-checked [tile checked]
(var res false)
@@ -90,7 +116,7 @@
res)
;; Modified implementation of <https://en.wikipedia.org/wiki/Flood_fill>.
-(fn count-surfaces-recur [map tile checked]
+(fn count-surfaces-recur [world tile checked]
(var surfaces 0)
(when (not (tile-checked tile checked))
@@ -98,52 +124,52 @@
(let [x (/ (. tile :x-pos) (. tile-sheet :width))
y (/ (. tile :y-pos) (. tile-sheet :height))
- width (. map :width)
- height (. map :height)]
+ width (. world :width)
+ height (. world :height)]
(when (>= x 0)
- (let [tile (tile-at map (- x 1) y)]
+ (let [tile (tile-at world (- x 1) y)]
(if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ surfaces (count-surfaces-recur world tile checked)))
(set surfaces (+ 1 surfaces)))))
(when (< x width)
- (let [tile (tile-at map (+ x 1) y)]
+ (let [tile (tile-at world (+ x 1) y)]
(if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ surfaces (count-surfaces-recur world tile checked)))
(set surfaces (+ 1 surfaces)))))
(when (>= y 0)
- (let [tile (tile-at map x (- y 1))]
+ (let [tile (tile-at world x (- y 1))]
(if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ surfaces (count-surfaces-recur world tile checked)))
(set surfaces (+ 1 surfaces)))))
(when (< y height)
- (let [tile (tile-at map x (+ y 1))]
+ (let [tile (tile-at world x (+ y 1))]
(if (= :empty (. tile :type))
- (set surfaces (+ surfaces (count-surfaces-recur map tile checked)))
+ (set surfaces (+ surfaces (count-surfaces-recur world tile checked)))
(set surfaces (+ 1 surfaces)))))))
surfaces)
-;; Returns some tile in `map' of type `tile-type', or nil if no such tile is
+;; Returns some tile in `world' of type `tile-type', or nil if no such tile is
;; present.
-(fn find-any [tile-type map]
+(fn find-any [tile-type world]
(var res nil)
- (let [width (. map :width)
- height (. map :height)]
+ (let [width (. world :width)
+ height (. world :height)]
(for [x 0 (- width 1)]
(for [y 0 (- height 1)]
- (let [tile (tile-at map x y)]
+ (let [tile (tile-at world 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)]
+(fn count-surfaces [world]
+ (let [seed (find-any :empty world)]
(when seed
- (count-surfaces-recur map seed []))))
+ (count-surfaces-recur world seed []))))
(fn add-player-to-collision-map [collision-map player]
(let [x (. player :x-pos)
@@ -157,17 +183,17 @@
y (. tile :y-pos)
width (. tile :width)
height (. tile :height)]
- (: collision-map :add tile (* x width) (* y height) width height)))
+ (: collision-map :add tile x y width height)))
;; Returns a new collision map containing `player' and everything in `map'.
-(fn new-collision-map [map player]
+(fn new-collision-map [world player]
(let [collision-map (bump.newWorld 32)
- width (. map :width)
- height (. map :height)]
+ width (. world :width)
+ height (. world :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)]
+ (let [tile (tile-at world x y)]
(when (~= :empty (. tile :type))
(add-tile-to-collision-map collision-map tile)))))
collision-map))
@@ -221,17 +247,60 @@
(fn load-meta [path]
(fennel.dofile path))
+(fn slime-tile [world tile orientation]
+ (when (not (. tile :slimed orientation))
+ (let [surfaces-slimed (. world :surfaces-slimed)]
+ (tset world :surfaces-slimed (+ 1 surfaces-slimed))
+ (tset tile :slimed orientation true))))
+
+(fn update-world [world dt]
+ (let [player (. world :player)
+ collision-map (. world :collision-map)
+ (goal-x goal-y) (: player :next-position dt)
+ (x y collisions _) (: collision-map :move player goal-x goal-y)]
+ (tset player :x-pos x)
+ (tset player :y-pos y)
+
+ (each [_ collision (ipairs collisions)]
+ ;; Touching top of surface.
+ (when (> 0 (. collision :normal :y))
+ (: player :impact-bottom)
+ (slime-tile world (. collision :other) :top))
+
+ ;; Touching bottom of surface.
+ (when (< 0 (. collision :normal :y))
+ (: player :impact-top)
+ (slime-tile world (. collision :other) :bottom))
+
+ ;; Touching left of surface.
+ (when (> 0 (. collision :normal :x))
+ (: player :impact-right)
+ (slime-tile world (. collision :other) :left))
+
+ ;; Touching right of surface.
+ (when (< 0 (. collision :normal :x))
+ (: player :impact-left)
+ (slime-tile world (. collision :other) :right)))
+
+ (tset player :x-vel (: player :next-x-vel dt))
+ (tset player :y-vel (: player :next-y-vel dt))
+ (tset player :grounded false)))
+
;; 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 {:player player})
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 (lume.extend res {:position-player-at position-player-at})
+ res (lume.extend res {:draw draw-world})
+ res (lume.extend res {:update update-world})]
res))
-{:tile-at tile-at
+{:tile-sheet tile-sheet
+ :tile-at tile-at
:new new-world}