diff --git a/.gitignore b/.gitignore index 9b8deb4..95499cb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ .clj-kondo/ .lsp/ + + +build/ +.internal diff --git a/defold_game/.editor_settings b/defold_game/.editor_settings new file mode 100644 index 0000000..7948eab --- /dev/null +++ b/defold_game/.editor_settings @@ -0,0 +1,86 @@ +{ + :code { + :breakpoints [ + ] + } + :workflow { + :open-tabs [ + [ + [ + "/main/main.collection" + :scene + ] + [ + "/main/player/player.script" + :code + ] + ] + ] + :recent-files [ + [ + "/main/player.tilesource" + :text + ] + [ + "/main/player.sprite" + :text + ] + [ + "/main/main.script" + :code + ] + [ + "/main/main.collection" + :text + ] + [ + "/main/player.sprite" + :scene + ] + [ + "/main/main.atlas" + :scene + ] + [ + "/main/player.tilesource" + :scene + ] + [ + "/main/player.script" + :code + ] + [ + "/main/zoom.render_script" + :code + ] + [ + "/builtins/render/default.render_script" + :code + ] + [ + "/builtins/render/default.render" + :cljfx-form-view + ] + [ + "/input/game.input_binding" + :cljfx-form-view + ] + [ + "/game.project" + :cljfx-form-view + ] + [ + "/main/zoom.script" + :code + ] + [ + "/main/main.collection" + :scene + ] + [ + "/main/player/player.script" + :code + ] + ] + } +} \ No newline at end of file diff --git a/defold_game/README.md b/defold_game/README.md new file mode 100644 index 0000000..7a684e1 --- /dev/null +++ b/defold_game/README.md @@ -0,0 +1,19 @@ +# Welcome to Defold + +This project was created from the "desktop" project template. This means that the settings in ["game.project"](defold://open?path=/game.project) have been changed to be suitable for a desktop game: + +- The screen size is set to 1280x720 +- Projection is set to Fixed Fit +- macOS and Windows icons are set +- Mouse clicks are bound to action "touch" +- A simple script in a game object is set up to receive and react to input + +[Build and run](defold://project.build) to see it in action. You can of course alter these settings to fit your needs. + +Check out [the documentation pages](https://defold.com/learn) for examples, tutorials, manuals and API docs. + +If you run into trouble, help is available in [our forum](https://forum.defold.com). + +Happy Defolding! + +--- diff --git a/defold_game/assets/app_icons/appicon.icns b/defold_game/assets/app_icons/appicon.icns new file mode 100644 index 0000000..f5a79a4 Binary files /dev/null and b/defold_game/assets/app_icons/appicon.icns differ diff --git a/defold_game/assets/app_icons/appicon.ico b/defold_game/assets/app_icons/appicon.ico new file mode 100644 index 0000000..e6dd0b0 Binary files /dev/null and b/defold_game/assets/app_icons/appicon.ico differ diff --git a/defold_game/assets/images/background.png b/defold_game/assets/images/background.png new file mode 100644 index 0000000..4692a7f Binary files /dev/null and b/defold_game/assets/images/background.png differ diff --git a/defold_game/assets/images/logo.png b/defold_game/assets/images/logo.png new file mode 100644 index 0000000..1facc50 Binary files /dev/null and b/defold_game/assets/images/logo.png differ diff --git a/defold_game/assets/images/player.png b/defold_game/assets/images/player.png new file mode 100644 index 0000000..ca62b72 Binary files /dev/null and b/defold_game/assets/images/player.png differ diff --git a/defold_game/game.project b/defold_game/game.project new file mode 100644 index 0000000..3cc0ee1 --- /dev/null +++ b/defold_game/game.project @@ -0,0 +1,35 @@ +[project] +title = Desktop Game + +[bootstrap] +main_collection = /main/main.collectionc + +[input] +game_binding = /input/game.input_bindingc +use_accelerometer = 0 + +[display] +width = 1280 +height = 720 +high_dpi = 1 + +[script] +shared_state = 1 + +[windows] +app_icon = /assets/app_icons/appicon.ico + +[osx] +app_icon = /assets/app_icons/appicon.icns + +[android] +input_method = HiddenInputField + +[physics] +gravity_y = -1000.0 +scale = 0.01 +velocity_threshold = 100.0 + +[html5] +scale_mode = stretch + diff --git a/defold_game/input/game.input_binding b/defold_game/input/game.input_binding new file mode 100644 index 0000000..438c772 --- /dev/null +++ b/defold_game/input/game.input_binding @@ -0,0 +1,20 @@ +key_trigger { + input: KEY_A + action: "a_key" +} +key_trigger { + input: KEY_Q + action: "q_key" +} +key_trigger { + input: KEY_D + action: "d_key" +} +key_trigger { + input: KEY_E + action: "e_key" +} +mouse_trigger { + input: MOUSE_BUTTON_LEFT + action: "touch" +} diff --git a/defold_game/main/main.atlas b/defold_game/main/main.atlas new file mode 100644 index 0000000..5faa635 --- /dev/null +++ b/defold_game/main/main.atlas @@ -0,0 +1,15 @@ +images { + image: "/assets/images/background.png" +} +images { + image: "/assets/images/logo.png" +} +images { + image: "/builtins/graphics/particle_blob.png" +} +images { + image: "/assets/images/player.png" + pivot_x: 0.536806 + pivot_y: 0.447809 +} +extrude_borders: 2 diff --git a/defold_game/main/main.collection b/defold_game/main/main.collection new file mode 100644 index 0000000..a3c3b0b --- /dev/null +++ b/defold_game/main/main.collection @@ -0,0 +1,42 @@ +name: "main" +scale_along_z: 0 +embedded_instances { + id: "camera" + data: "embedded_components {\n" + " id: \"camera\"\n" + " type: \"camera\"\n" + " data: \"aspect_ratio: 1.0\\n" + "fov: 0.7854\\n" + "near_z: -1.0\\n" + "far_z: 1.0\\n" + "orthographic_projection: 1\\n" + "orthographic_zoom: 2.0\\n" + "\"\n" + "}\n" + "" + position { + x: 640.0 + y: 360.0 + } +} +embedded_instances { + id: "player" + children: "body" + data: "components {\n" + " id: \"player_controller\"\n" + " component: \"/main/player/player.script\"\n" + "}\n" + "" + position { + x: 649.0 + y: 503.0 + } +} +embedded_instances { + id: "body" + data: "components {\n" + " id: \"body\"\n" + " component: \"/main/player/player.sprite\"\n" + "}\n" + "" +} diff --git a/defold_game/main/main.script b/defold_game/main/main.script new file mode 100644 index 0000000..1fde8d3 --- /dev/null +++ b/defold_game/main/main.script @@ -0,0 +1,10 @@ +-- function init(self) +-- msg.post(".", "acquire_input_focus") +-- end +-- +-- function on_input(self, action_id, action) +-- if action_id == hash("touch") and action.pressed then +-- print("Touch!") +-- end +-- end +-- \ No newline at end of file diff --git a/defold_game/main/player/player.script b/defold_game/main/player/player.script new file mode 100644 index 0000000..cd7b341 --- /dev/null +++ b/defold_game/main/player/player.script @@ -0,0 +1,86 @@ +local ACCELERATION = 100 +local DECELERATION = 200 +local MAX_SPEED = 400 +local WHEEL_BASE = 80 + +local UP = vmath.vector3(0, 1, 0) + +local DIRECTIONS = { "n", "ne", "e", "se", "s", "sw", "w", "nw" } + +local function get_direction(rot) + local forward = vmath.rotate(rot, UP) + local angle = math.deg(math.atan2(forward.x, forward.y)) + if angle < 0 then angle = angle + 360 end + local index = math.floor((angle + 22.5) / 45) % 8 + 1 + return DIRECTIONS[index] +end + +function init(self) + msg.post(".", "acquire_input_focus") + + self.left_speed = 0 + self.right_speed = 0 + self.current_anim = nil + self.left_fwd = false + self.left_rev = false + self.right_fwd = false + self.right_rev = false +end + +local function update_speed(speed, drive, dt) + if drive ~= 0 then + local target = drive * MAX_SPEED + local step = ACCELERATION * dt + if speed < target then + return math.min(speed + step, target) + else + return math.max(speed - step, target) + end + else + local step = DECELERATION * dt + if speed > 0 then + return math.max(speed - step, 0) + else + return math.min(speed + step, 0) + end + end +end + +function update(self, dt) + local left_drive = (self.left_fwd and 1 or 0) - (self.left_rev and 1 or 0) + local right_drive = (self.right_fwd and 1 or 0) - (self.right_rev and 1 or 0) + + self.left_speed = update_speed(self.left_speed, left_drive, dt) + self.right_speed = update_speed(self.right_speed, right_drive, dt) + + -- differential drive: speed difference creates rotation, average creates forward motion + local linear_speed = (self.left_speed + self.right_speed) / 2 + local angular_vel = (self.right_speed - self.left_speed) / WHEEL_BASE + + -- pprint({rot_changes = angular_vel * dt}) + local rot = go.get_rotation() + rot = rot * vmath.quat_rotation_z(angular_vel * dt) + go.set_rotation(rot) + + go.set_rotation(vmath.conj(rot), "body") + + local p = go.get_position() + p = p + vmath.rotate(rot, UP * linear_speed * dt) + go.set_position(p) + + -- switch animation when facing direction changes + local dir = get_direction(rot) + if dir ~= self.current_anim then + self.current_anim = dir + msg.post("body#body", "play_animation", { id = hash(dir) }) + end +end + +function on_input(self, action_id, action) + local held = action.pressed or not action.released + if action_id == hash("a_key") then self.left_fwd = held + elseif action_id == hash("q_key") then self.left_rev = held + elseif action_id == hash("d_key") then self.right_fwd = held + elseif action_id == hash("e_key") then self.right_rev = held + end +end diff --git a/defold_game/main/player/player.sprite b/defold_game/main/player/player.sprite new file mode 100644 index 0000000..327d49c --- /dev/null +++ b/defold_game/main/player/player.sprite @@ -0,0 +1,6 @@ +default_animation: "n" +material: "/builtins/materials/sprite.material" +textures { + sampler: "texture_sampler" + texture: "/main/player/player.tilesource" +} diff --git a/defold_game/main/player/player.tilesource b/defold_game/main/player/player.tilesource new file mode 100644 index 0000000..d16afdf --- /dev/null +++ b/defold_game/main/player/player.tilesource @@ -0,0 +1,52 @@ +image: "/assets/images/player.png" +tile_width: 25 +tile_height: 25 +collision_groups: "player" +animations { + id: "e" + start_tile: 4 + end_tile: 4 + playback: PLAYBACK_NONE +} +animations { + id: "n" + start_tile: 1 + end_tile: 1 + playback: PLAYBACK_NONE +} +animations { + id: "ne" + start_tile: 3 + end_tile: 3 + playback: PLAYBACK_NONE +} +animations { + id: "nw" + start_tile: 8 + end_tile: 8 + playback: PLAYBACK_NONE +} +animations { + id: "s" + start_tile: 2 + end_tile: 2 + playback: PLAYBACK_NONE +} +animations { + id: "se" + start_tile: 5 + end_tile: 5 + playback: PLAYBACK_NONE +} +animations { + id: "sw" + start_tile: 6 + end_tile: 6 + playback: PLAYBACK_NONE +} +animations { + id: "w" + start_tile: 7 + end_tile: 7 + playback: PLAYBACK_NONE +} diff --git a/game/src/entities/player.fnl b/game/src/entities/player.fnl index e6386a1..925fd70 100644 --- a/game/src/entities/player.fnl +++ b/game/src/entities/player.fnl @@ -31,7 +31,8 @@ (local height 15) (local player { :x 50 :y 50 :speed 80 :battery 100 :rot 0 - :vision-pts [] + :vision-pts [] ; the points that make a polygon around the player's cone of vision + :vision-hitboxes [] ; the hitboxes that are visible to the player's cone of vision :hitbox [50 50 width height] :ever-moved false ; useful for inital update }) @@ -74,6 +75,7 @@ (let [pts (self:cal-vision-points) (ox oy) (self:origin-pt) poly-pts [ox oy] + visable-hitboxes [] ] (each [_ pt (ipairs pts)] (let [ @@ -81,12 +83,14 @@ first-item (. items 1)] (if first-item (do + (table.insert visable-hitboxes (. first-item :item)) (table.insert poly-pts (. first-item :x1)) (table.insert poly-pts (. first-item :y1))) (do (table.insert poly-pts (. pt 1)) (table.insert poly-pts (. pt 2)))))) (set self.vision-pts poly-pts) + (set self.vision-hitboxes visable-hitboxes) )) diff --git a/game/src/levels/dev.fnl b/game/src/levels/dev.fnl index 2890ba9..757c3b9 100644 --- a/game/src/levels/dev.fnl +++ b/game/src/levels/dev.fnl @@ -61,7 +61,7 @@ camera walls fog-of-war - radar + ; radar (hud {: screen}) ] })] diff --git a/game/src/systems/camera.fnl b/game/src/systems/camera.fnl index 74e92af..de895f8 100644 --- a/game/src/systems/camera.fnl +++ b/game/src/systems/camera.fnl @@ -5,7 +5,7 @@ (local camera {:x 0 :y 0}) -(fn camera.draw10 [self] +(fn camera.draw25 [self] (love.graphics.origin) (love.graphics.translate (* -1 self.x) (* -1 self.y))) diff --git a/game/src/systems/fog_of_war.fnl b/game/src/systems/fog_of_war.fnl index d702587..848a318 100644 --- a/game/src/systems/fog_of_war.fnl +++ b/game/src/systems/fog_of_war.fnl @@ -7,15 +7,22 @@ (local fow { :player-vision-pts [] - :player-pos [0 0] + :player-vision-hitboxes [] + :player-pt [0 0] + :player-origin-pt [0 0] :font nil - :canvas nil }) + :canvas nil + :main-canvas nil + }) (lambda fow.load [self] (set self.canvas (love.graphics.newCanvas 1000 1000)) (beholder.observe "PLAYER.MOVED" (lambda [player] - (set self.player-pos [player.x player.y]) - (set self.player-vision-pts player.vision-pts))) + (let [(ox oy) (player:origin-pt)] + (set self.player-origin-pt [ox oy])) + (set self.player-pt [player.x player.y]) + (set self.player-vision-hitboxes player.vision-hitboxes) + (set self.player-vision-pts player.vision-pts))) (let [previousCanvas (love.graphics.getCanvas)] (love.graphics.setCanvas self.canvas) @@ -24,26 +31,35 @@ (love.graphics.setCanvas previousCanvas) )) -(lambda fow.draw9 [self] - (let [ - previousCanvas (love.graphics.getCanvas) - previousBlendMode (love.graphics.getBlendMode) - [x y] self.player-pos] - (love.graphics.setCanvas self.canvas) - (love.graphics.clear) - (color.set-color :dark-purple) - (love.graphics.rectangle "fill" 0 0 1000 1000) +(lambda fow.draw10 [self] + (set self.main-canvas (love.graphics.getCanvas)) + (love.graphics.setCanvas self.canvas) + (love.graphics.clear) + (color.set-color :dark-purple) + (love.graphics.rectangle "fill" 0 0 1000 1000)) + + +(lambda fow.draw15 [self] + (let [previousBlendMode (love.graphics.getBlendMode)] (love.graphics.setBlendMode "replace") (love.graphics.setColor 0 0 0 0) ; transparent (love.graphics.push) - (love.graphics.translate (- x 3) (- y 7)) + (love.graphics.translate (unpack self.player-pt)) + (love.graphics.translate -3 -7) (love.graphics.polygon "fill" 3 11 3 17 7 21 17 21 21 17 21 11 17 7 7 7) (love.graphics.pop) ; draw the vision cone - (when (> (length self.player-vision-pts) 2) - (love.graphics.polygon "fill" self.player-vision-pts)) - (love.graphics.setBlendMode previousBlendMode) - (love.graphics.setCanvas previousCanvas))) + (when (> (length self.player-vision-pts) 2) + (love.graphics.polygon "fill" self.player-vision-pts)) + ; draw the vision hitboxes + (each [_ hitbox (pairs self.player-vision-hitboxes)] + (love.graphics.rectangle "fill" hitbox.x hitbox.y hitbox.w hitbox.h)) + (love.graphics.setBlendMode previousBlendMode)) + + (color.set-color :white) + (each [_ hitbox (pairs self.player-vision-hitboxes)] + (love.graphics.rectangle "line" hitbox.x hitbox.y hitbox.w hitbox.h)) + (love.graphics.setCanvas self.main-canvas)) (lambda fow.draw80 [self] (color.reset-color) diff --git a/game/src/systems/radar.fnl b/game/src/systems/radar.fnl index bca255f..416718b 100644 --- a/game/src/systems/radar.fnl +++ b/game/src/systems/radar.fnl @@ -8,6 +8,9 @@ { :x 0 :y 0 :rot 0 + :debug-pt [0 0] + :boxes {} + :size 150 :speed 4 }) (lambda radar.load [self] @@ -16,18 +19,46 @@ (set self.x x) (set self.y y))))) +(lambda vision-bump-filter [other] + (= other.behavior "block")) + + (lambda radar.update [self dt] - (set self.rot (+ self.rot (* (/ dt self.speed))))) + (set self.rot (+ self.rot (* (/ dt self.speed)))) + ; if we are over the limit of radar boxes the oldest out + (let [ + ox self.x + oy self.y + x ox + y (- oy self.size) + (tip-x tip-y) (utils.rotate-pt x y ox oy (+ self.rot utils.deg-90)) + items (self.pool.data.bump-world:querySegment ox oy tip-x tip-y vision-bump-filter)] + (when (> (length items) 0) + (each [_ item (ipairs items)] + (set item.lase-seen dt) + (tset self.boxes item item))) + + (set self.debug-pt [tip-x tip-y]))) + +(lambda radar.draw11 [self] + "Draw radar boxes" + (color.set-color :light-pink) + (each [_ box (pairs self.boxes)] + (love.graphics.rectangle "line" box.x box.y box.w box.h) + (love.graphics.line box.x box.y (+ box.x box.w) (+ box.y box.h)) + (love.graphics.line (+ box.x box.w) box.y box.x (+ box.y box.h)))) (lambda radar.draw81 [self] (color.set-color :dark-pink) + (love.graphics.setPointSize 4) + (love.graphics.points (unpack self.debug-pt)) + (love.graphics.line self.x self.y (. self.debug-pt 1) (. self.debug-pt 2)) (love.graphics.push) (love.graphics.translate self.x self.y) (love.graphics.rotate self.rot) - (love.graphics.setPointSize 2) (love.graphics.points 0 0) - (love.graphics.circle "line" 0 0 150) - (love.graphics.line 0 0 0 150) + (love.graphics.circle "line" 0 0 self.size) + (love.graphics.line 0 0 0 self.size) (love.graphics.pop)) radar diff --git a/game/src/systems/walls.fnl b/game/src/systems/walls.fnl index e6a007b..071a19f 100644 --- a/game/src/systems/walls.fnl +++ b/game/src/systems/walls.fnl @@ -30,6 +30,9 @@ (love.graphics.line x1 y1 x1 (+ y1 1000)) (love.graphics.print (.. (string.format "%d" x1) "," (string.format "%d" y1)) (+ x1 3) (+ y1 3))))))) +; (lambda gen-wall-collider-box [x y width height] +; ) + (fn walls.load [self] (set self.batch (love.graphics.newSpriteBatch assets.walls-sprite 2500)) ;; load quads @@ -37,8 +40,8 @@ (for [i 0 19 1] (table.insert self.quads (love.graphics.newQuad (* i 25) 0 25 25 w h)))) ;; fill batch - (each [_ row (pairs (. levels :levels self.pool.data.level-key :tiles))] - (each [_ tile (pairs row)] + (each [row-key row (pairs (. levels :levels self.pool.data.level-key :tiles))] + (each [tile-key tile (pairs row)] (let [ x tile.x y tile.y @@ -47,12 +50,12 @@ (if (and (> id 0) (< id 21)) ;; 1-20 are wall tiles (do (self.batch:add (. self.quads id) (if tile.h-flip (+ x 25) x) y 0 (if tile.h-flip -1 1) 1) - (each [_ collider (pairs colliders)] + (each [collider-key collider (pairs colliders)] (let [ mirrored-collider (if tile.h-flip (mirror-collider collider) collider) collider-x (+ x mirrored-collider.x) collider-y (+ y mirrored-collider.y)] - (self.pool.data.bump-world:add {: x : y :name :wall :behavior :block} collider-x collider-y mirrored-collider.width mirrored-collider.height) + (self.pool.data.bump-world:add {:id (.. row-key tile-key collider-key) : x : y :name :wall :behavior :block :w mirrored-collider.width :h mirrored-collider.height} collider-x collider-y mirrored-collider.width mirrored-collider.height) (table.insert self.collider-debug-boxes {:x collider-x :y collider-y :width mirrored-collider.width :height mirrored-collider.height})))))))) @@ -65,7 +68,7 @@ ) -(fn walls.draw49 [self] +(fn walls.draw40 [self] (color.reset-color) (love.graphics.draw self.batch))