diff --git a/gleam.toml b/gleam.toml index feb1fcf..f3e2b93 100644 --- a/gleam.toml +++ b/gleam.toml @@ -15,7 +15,7 @@ target = "javascript" [dependencies] gleam_stdlib = ">= 0.36.0 and < 2.0.0" -p5js_gleam = ">= 2.1.0 and < 3.0.0" +p5js_gleam = ">= 2.1.1 and < 3.0.0" prng = ">= 3.0.2 and < 4.0.0" gleam_community_maths = ">= 1.1.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 8f5dfec..9897fc7 100644 --- a/manifest.toml +++ b/manifest.toml @@ -16,7 +16,7 @@ packages = [ { name = "gleam_json", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "8B197DD5D578EA6AC2C0D4BDC634C71A5BCA8E7DB5F47091C263ECB411A60DF3" }, { name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" }, { name = "glint", version = "0.18.1", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "5FB54D7732B4105E4AF4D89A7EE6D5E8CF33DA13A3575D0C6ECE470B97958454" }, - { name = "p5js_gleam", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "p5js_gleam", source = "hex", outer_checksum = "239AB03FB21F8CA929AE8E0C246AAF37D316AC1E6431470F17033C9483AF336D" }, + { name = "p5js_gleam", version = "2.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "p5js_gleam", source = "hex", outer_checksum = "4EEC8FCD162486FCAA2AFEC9E8B7A8843AC9935551CF2B2A604A0F070AB6B00F" }, { name = "prng", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_bitwise", "gleam_stdlib"], otp_app = "prng", source = "hex", outer_checksum = "C61B103F9AF5031ADAA35187CCE7130845EF5088D88FD084E5995D4FBEC9D745" }, { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, @@ -30,6 +30,6 @@ packages = [ esgleam = { version = ">= 0.6.0 and < 1.0.0" } gleam_community_maths = { version = ">= 1.1.0 and < 2.0.0" } gleam_stdlib = { version = ">= 0.36.0 and < 2.0.0" } -p5js_gleam = { version = ">= 2.1.0 and < 3.0.0"} +p5js_gleam = { version = ">= 2.1.1 and < 3.0.0"} prng = { version = ">= 3.0.2 and < 4.0.0" } startest = { version = ">= 0.2.0 and < 1.0.0" } diff --git a/src/bullet_heck_gleam.gleam b/src/bullet_heck_gleam.gleam index aa15d67..b904d10 100644 --- a/src/bullet_heck_gleam.gleam +++ b/src/bullet_heck_gleam.gleam @@ -16,6 +16,7 @@ type WorldState { player: player.Player, bullets: List(bullet.Bullet), ) + GameOver(fell_in_pitch: Bool) // enemies: List(Enemy), } @@ -31,102 +32,181 @@ fn setup(p: P5) -> WorldState { } fn draw(p: P5, state: WorldState) { - p5.background(p, "#000000") - dungeon.draw(p, state.dungeon) - player.draw(p, state.player) - list.each(state.bullets, bullet.draw(p, _)) + case state { + GameRunning(dungeon, player, bullets) -> { + p5.background(p, "#000000") + dungeon.draw(p, dungeon) + player.draw(p, player) + list.each(bullets, bullet.draw(p, _)) + p + } + GameOver(_) -> { + p5.background(p, "#000000") + } + } } fn on_key_pressed(key: String, _: Int, state: WorldState) -> WorldState { - case key { - " " -> GameRunning(..state, player: player.jump(state.player)) - "w" -> - GameRunning(..state, player: player.accelerate_y(state.player, False)) - "s" -> GameRunning(..state, player: player.accelerate_y(state.player, True)) - "a" -> - GameRunning(..state, player: player.accelerate_x(state.player, False)) - "d" -> GameRunning(..state, player: player.accelerate_x(state.player, True)) - _ -> state + case key, state { + " ", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.jump(player), + ) + "w", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.accelerate_y(player, False), + ) + "s", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.accelerate_y(player, True), + ) + "a", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.accelerate_x(player, False), + ) + "d", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.accelerate_x(player, True), + ) + _, _ -> state } } fn on_key_released(key: String, _: Int, state: WorldState) -> WorldState { - case key { - "w" | "s" -> GameRunning(..state, player: player.stop_y(state.player)) - "a" | "d" -> GameRunning(..state, player: player.stop_x(state.player)) - _ -> state + case key, state { + "w", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.stop_y(player), + ) + "s", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.stop_y(player), + ) + "a", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.stop_x(player), + ) + "d", GameRunning(dungeon, player, bullets) -> + GameRunning( + dungeon: dungeon, + bullets: bullets, + player: player.stop_x(player), + ) + _, _ -> state } } fn on_mouse_clicked(x: Float, y: Float, state: WorldState) -> WorldState { - let GameRunning(_dungeon, player, bullets) = state - use <- bool.guard(!player.can_player_fire(player), state) - - let firing_direction = - vector.vector_2d(vector.subtract(vector.Vector(x, y, 0.0), player.position)) - let player.Player(position: p, ..) = player - GameRunning( - ..state, - bullets: [ - bullet.spawn_bullet(vector.Vector(p.x, p.y, 0.0), firing_direction, True), - ..bullets - ], - player: player.Player(..player, last_fire_time: utils.now_in_milliseconds()), - ) -} + case state { + GameRunning(dungeon, player, bullets) -> { + use <- bool.guard(!player.can_player_fire(player), state) -fn on_tick(state: WorldState) -> WorldState { - let GameRunning(dungeon, player, bullets) = state - // Attempt to move player - let old_position = player.position - let moved = player.move(player) - let player = case dungeon.can_move(dungeon, old_position, moved.position) { - True -> moved - // If they can't move then just apply gravity - False -> - player.Player( - ..player, - position: vector.Vector( - old_position.x, - old_position.y, - old_position.z +. player.velocity.z, + let firing_direction = + vector.vector_2d(vector.subtract( + vector.Vector(x, y, 0.0), + player.position, + )) + let player.Player(position: p, ..) = player + GameRunning( + dungeon: dungeon, + bullets: [ + bullet.spawn_bullet( + vector.Vector(p.x, p.y, 0.0), + firing_direction, + True, + ), + ..bullets + ], + player: player.Player( + ..player, + last_fire_time: utils.now_in_milliseconds(), ), ) + } + _ -> state } +} - let player = player.update_velocity(player) - let player = player.apply_gravity(player) - - let bullets = - bullets - |> list.filter(bullet.is_still_alive) +fn on_tick(state: WorldState) -> WorldState { + case state { + GameRunning(dungeon, player, bullets) -> { + // Attempt to move player + let old_position = player.position + let moved = player.move(player) + let player = case + dungeon.can_move(dungeon, old_position, moved.position) + { + True -> moved + // If they can't move then just apply gravity + False -> + player.Player( + ..player, + position: vector.Vector( + old_position.x, + old_position.y, + old_position.z +. player.velocity.z, + ), + ) + } - let #(bullets, player) = - list.fold(bullets, #([], player), fn(acc, b) { - // If the bullet hits a wall then remove it use <- bool.guard( - !dungeon.can_move( - dungeon, - b.position, - bullet.advance_bullet(b).position, - ), - acc, + player.position.z <. 0.0 + && dungeon.is_over_pit(dungeon, player.position), + GameOver(False), ) - // Check if the bullet collides with something it can hit - let #(bullets, player) = acc - let player = case - !b.belongs_to_player - && bullet.collides_with(b, player.position, player.player_size) - { - True -> player.apply_damage(player, bullet.enemy_damage) - False -> player - } + let player = player.update_velocity(player) + let player = player.apply_gravity(player) + + let bullets = + bullets + |> list.filter(bullet.is_still_alive) - #([bullet.advance_bullet(b), ..bullets], player) - }) + let #(bullets, player) = + list.fold(bullets, #([], player), fn(acc, b) { + // If the bullet hits a wall then remove it + use <- bool.guard( + !dungeon.can_move( + dungeon, + b.position, + bullet.advance_bullet(b).position, + ), + acc, + ) - GameRunning(..state, player: player, bullets: bullets) + // Check if the bullet collides with something it can hit + let #(bullets, player) = acc + let player = case + !b.belongs_to_player + && bullet.collides_with(b, player.position, player.player_size) + { + True -> player.apply_damage(player, bullet.enemy_damage) + False -> player + } + + #([bullet.advance_bullet(b), ..bullets], player) + }) + + GameRunning(dungeon: dungeon, player: player, bullets: bullets) + } + _ -> state + } } pub fn main() { diff --git a/src/dungeon.gleam b/src/dungeon.gleam index 3d97242..8c900aa 100644 --- a/src/dungeon.gleam +++ b/src/dungeon.gleam @@ -33,7 +33,7 @@ pub fn total_size() -> Float { const pit_count = 5 -const pit_size = 30.0 +const pit_size = 20.0 const minimum_pit_distance = 80.0 @@ -385,3 +385,9 @@ pub fn can_move( _, _ -> False } } + +/// Checks if a point is on top of a pit. +pub fn is_over_pit(dungeon: Dungeon, position: vector.Vector) -> Bool { + use pit <- list.any(dungeon.pits) + vector.distance(pit.position, position) <. pit.size +} diff --git a/src/pit.gleam b/src/pit.gleam index 0f32a48..7a7bf63 100644 --- a/src/pit.gleam +++ b/src/pit.gleam @@ -8,7 +8,7 @@ pub type Pit { Pit( /// The position of the center of the pit. position: Vector, - /// The size of the pit. + /// The radius of the pit. size: Float, ) } @@ -21,5 +21,5 @@ pub fn draw(p: P5, pit: Pit) { |> p5.fill("#3C3C3C") |> p5.circle(pit.position.x -. 1.0, pit.position.y -. 1.0, pit.size +. 1.0) |> p5.fill("#000000") - |> p5.circle(pit.position.x, pit.position.y, pit.size) + |> p5.circle(pit.position.x, pit.position.y, pit.size *. 2.0) } diff --git a/test/dungeon_test.gleam b/test/dungeon_test.gleam index 5bed4f6..1e7b20f 100644 --- a/test/dungeon_test.gleam +++ b/test/dungeon_test.gleam @@ -3,6 +3,7 @@ import gleam/dict import gleam/int import gleam/iterator.{repeatedly, take} import gleam/list +import pit import room import startest.{describe, it} import startest/expect @@ -92,7 +93,7 @@ fn validate_pits(d: dungeon.Dungeon) { pub fn dungeon_tests() { describe("dungeon", [ - it("generated dungeons", fn() { + it("generate_dungeon", fn() { { use <- repeatedly @@ -104,7 +105,7 @@ pub fn dungeon_tests() { Nil }), - it("can_move in a dungeon", fn() { + it("can_move", fn() { let rooms = dict.new() |> dict.insert(#(1, 1), room.initialize_unbounded_room()) @@ -164,5 +165,46 @@ pub fn dungeon_tests() { Nil }), + it("is_over_pit", fn() { + let rooms = + dict.new() + |> dict.insert(#(1, 1), room.initialize_unbounded_room()) + |> dict.insert(#(1, 2), room.initialize_unbounded_room()) + |> dict.insert( + #(2, 1), + room.initialize_unbounded_room() + |> room.set_navigable(room.Bottom, True), + ) + |> dict.insert( + #(2, 2), + room.initialize_unbounded_room() + |> room.set_navigable(room.Top, True), + ) + let dungeon = + dungeon.Dungeon(rooms: rooms, pits: [ + pit.Pit(position: vector.Vector(0.0, 0.0, 0.0), size: 30.0), + ]) + + expect.to_be_true(dungeon.is_over_pit( + dungeon, + vector.Vector(0.0, 0.0, 0.0), + )) + expect.to_be_true(dungeon.is_over_pit( + dungeon, + vector.Vector(15.0, 0.0, 0.0), + )) + expect.to_be_true(dungeon.is_over_pit( + dungeon, + vector.Vector(20.0, 0.0, 0.0), + )) + expect.to_be_true(dungeon.is_over_pit( + dungeon, + vector.Vector(15.0, 15.0, 0.0), + )) + expect.to_be_false(dungeon.is_over_pit( + dungeon, + vector.Vector(35.0, 15.0, 0.0), + )) + }), ]) }