diff options
| author | Jakob L. Kreuze <jakob@memeware.net> | 2018-10-23 20:03:24 -0400 |
|---|---|---|
| committer | Jakob L. Kreuze <jakob@memeware.net> | 2018-10-23 20:03:24 -0400 |
| commit | d2a4535049a9feccb611e6a7b80c872075de0b96 (patch) | |
| tree | 57ffe4c6c6c56bcb245a30b03570648b357a2555 | |
| parent | 0ecfdef4d6d9de2b26208349e5f6e5ca631110dc (diff) | |
Breaking everything with major (but necessary to address bugs in last commit) refactoring
| -rw-r--r-- | art/swanky.png | bin | 5192 -> 2466 bytes | |||
| -rw-r--r-- | camera.fnl | 23 | ||||
| -rw-r--r-- | draw.fnl | 33 | ||||
| -rw-r--r-- | game.fnl | 370 | ||||
| -rw-r--r-- | map.fnl | 158 | ||||
| -rw-r--r-- | player.fnl | 76 | ||||
| -rw-r--r-- | world.fnl | 237 |
7 files changed, 491 insertions, 406 deletions
diff --git a/art/swanky.png b/art/swanky.png Binary files differindex c864873..3795968 100644 --- a/art/swanky.png +++ b/art/swanky.png 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} @@ -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} |