Skip to content

Commit fdc8681

Browse files
committed
bullets can be fired by the player
1 parent fec16b7 commit fdc8681

7 files changed

+178
-105
lines changed

gleam.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ target = "javascript"
1515

1616
[dependencies]
1717
gleam_stdlib = ">= 0.36.0 and < 2.0.0"
18-
p5js_gleam = ">= 2.0.0 and < 3.0.0"
18+
p5js_gleam = ">= 2.0.1 and < 3.0.0"
1919
prng = ">= 3.0.2 and < 4.0.0"
2020
gleam_community_maths = ">= 1.1.0 and < 2.0.0"
2121

manifest.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ packages = [
1616
{ name = "gleam_json", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "8B197DD5D578EA6AC2C0D4BDC634C71A5BCA8E7DB5F47091C263ECB411A60DF3" },
1717
{ name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" },
1818
{ 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" },
19-
{ name = "p5js_gleam", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "p5js_gleam", source = "hex", outer_checksum = "28CBB1EE158BB025B122E3BB33645B8C60FA4EB7BBDBE2A16E08C0A0638150FB" },
19+
{ name = "p5js_gleam", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "p5js_gleam", source = "hex", outer_checksum = "2EA1C1FDB1D2748A7B02A786497BB50F7B86648C331D8A4E1274313F8F37C749" },
2020
{ name = "prng", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_bitwise", "gleam_stdlib"], otp_app = "prng", source = "hex", outer_checksum = "C61B103F9AF5031ADAA35187CCE7130845EF5088D88FD084E5995D4FBEC9D745" },
2121
{ name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" },
2222
{ 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 = [
3030
esgleam = { version = ">= 0.6.0 and < 1.0.0" }
3131
gleam_community_maths = { version = ">= 1.1.0 and < 2.0.0" }
3232
gleam_stdlib = { version = ">= 0.36.0 and < 2.0.0" }
33-
p5js_gleam = { version = ">= 2.0.0 and < 3.0.0"}
33+
p5js_gleam = { version = ">= 2.0.1 and < 3.0.0"}
3434
prng = { version = ">= 3.0.2 and < 4.0.0" }
3535
startest = { version = ">= 0.2.0 and < 1.0.0" }

src/bullet.gleam

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import p5js_gleam.{type P5}
2+
import p5js_gleam/bindings as p5
3+
import utils
4+
import vector.{type Vector}
5+
6+
const time_alive = 400
7+
8+
const bullet_size = 6.0
9+
10+
const bullet_speed = 5.0
11+
12+
pub const player_damage = 10
13+
14+
pub const enemy_damage = 5
15+
16+
/// Represents a fired bullet.
17+
pub type Bullet {
18+
/// Represents a fired bullet.
19+
Bullet(
20+
/// The bullet's current position in the dungeon.
21+
position: Vector,
22+
/// The bullet's current velocity.
23+
velocity: Vector,
24+
/// Was the bullet fired by the player.
25+
belongs_to_player: Bool,
26+
/// The time the bullet was spawned.
27+
time_spawned: Int,
28+
)
29+
}
30+
31+
/// Spawns a bullet at the given position going in the given direction.
32+
pub fn spawn_bullet(
33+
position: Vector,
34+
direction: Vector,
35+
belongs_to_player: Bool,
36+
) -> Bullet {
37+
Bullet(
38+
position: position,
39+
velocity: vector.multiply(vector.normalize(direction), bullet_speed),
40+
belongs_to_player: belongs_to_player,
41+
time_spawned: utils.now_in_milliseconds(),
42+
)
43+
}
44+
45+
/// Advances a bullet forward.
46+
pub fn advance_bullet(bullet: Bullet) -> Bullet {
47+
Bullet(..bullet, position: vector.add(bullet.position, bullet.velocity))
48+
}
49+
50+
/// Should the bullet be alive.
51+
pub fn is_still_alive(bullet: Bullet) -> Bool {
52+
bullet.time_spawned + time_alive > utils.now_in_milliseconds()
53+
}
54+
55+
/// Checks if the object at the given position with given radius collides with the bullet.
56+
pub fn collides_with(bullet: Bullet, position: Vector, size: Float) -> Bool {
57+
vector.distance(bullet.position, position)
58+
<. size /. 2.0 +. bullet_size /. 2.0
59+
}
60+
61+
const player_bullet = "#3030ff"
62+
63+
const enemy_bullet = "#f4424b"
64+
65+
/// Renders the bullet to the screen.
66+
pub fn draw(p: P5, bullet: Bullet) {
67+
p5.no_stroke(p)
68+
case bullet.belongs_to_player {
69+
True -> p5.fill(p, player_bullet)
70+
False -> p5.fill(p, enemy_bullet)
71+
}
72+
p5.circle(p, bullet.position.x, bullet.position.y, bullet_size)
73+
}

src/bullet_heck_gleam.gleam

+59-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import bullet
12
import dungeon
3+
import gleam/bool
4+
import gleam/list
25
import p5js_gleam.{type P5}
36
import p5js_gleam/bindings as p5
47
import player
@@ -7,10 +10,12 @@ import vector
710
/// Represents the overall game state
811
type WorldState {
912
/// State of the world when the game is active
10-
GameRunning(dungeon: dungeon.Dungeon, player: player.Player)
11-
// bullets: List(Bullet),
13+
GameRunning(
14+
dungeon: dungeon.Dungeon,
15+
player: player.Player,
16+
bullets: List(bullet.Bullet),
17+
)
1218
// enemies: List(Enemy),
13-
// player: Player,
1419
}
1520

1621
fn setup(p: P5) -> WorldState {
@@ -20,13 +25,15 @@ fn setup(p: P5) -> WorldState {
2025
GameRunning(
2126
dungeon,
2227
player.new_player(vector.Vector(canvas_size /. 2.0, canvas_size /. 2.0, 0.0)),
28+
[],
2329
)
2430
}
2531

2632
fn draw(p: P5, state: WorldState) {
2733
p5.background(p, "#000000")
2834
dungeon.draw(p, state.dungeon)
2935
player.draw(p, state.player)
36+
list.each(state.bullets, bullet.draw(p, _))
3037
}
3138

3239
fn on_key_pressed(key: String, _: Int, state: WorldState) -> WorldState {
@@ -50,43 +57,81 @@ fn on_key_released(key: String, _: Int, state: WorldState) -> WorldState {
5057
}
5158
}
5259

53-
fn on_mouse_moved(x: Float, y: Float, state: WorldState) -> WorldState {
60+
fn on_mouse_clicked(x: Float, y: Float, state: WorldState) -> WorldState {
61+
let firing_direction =
62+
vector.vector_2d(vector.subtract(
63+
vector.Vector(x, y, 0.0),
64+
state.player.position,
65+
))
66+
let player.Player(position: p, ..) = state.player
5467
GameRunning(
5568
..state,
56-
player: player.look_toward(state.player, vector.Vector(x, y, 0.0)),
69+
bullets: [
70+
bullet.spawn_bullet(vector.Vector(p.x, p.y, 0.0), firing_direction, True),
71+
..state.bullets
72+
],
5773
)
5874
}
5975

6076
fn on_tick(state: WorldState) -> WorldState {
77+
let GameRunning(dungeon, player, bullets) = state
6178
// Attempt to move player
62-
let old_position = state.player.position
63-
let moved = player.move(state.player)
64-
let player = case
65-
dungeon.can_move(state.dungeon, old_position, moved.position)
66-
{
79+
let old_position = player.position
80+
let moved = player.move(player)
81+
let player = case dungeon.can_move(dungeon, old_position, moved.position) {
6782
True -> moved
6883
// If they can't move then just apply gravity
6984
False ->
7085
player.Player(
71-
..state.player,
86+
..player,
7287
position: vector.Vector(
7388
old_position.x,
7489
old_position.y,
75-
old_position.z +. state.player.velocity.z,
90+
old_position.z +. player.velocity.z,
7691
),
7792
)
7893
}
7994

8095
let player = player.update_velocity(player)
8196
let player = player.apply_gravity(player)
82-
GameRunning(..state, player: player)
97+
98+
let bullets =
99+
bullets
100+
|> list.filter(bullet.is_still_alive)
101+
102+
let #(bullets, player) =
103+
list.fold(bullets, #([], player), fn(acc, b) {
104+
// If the bullet hits a wall then remove it
105+
use <- bool.guard(
106+
!dungeon.can_move(
107+
dungeon,
108+
b.position,
109+
bullet.advance_bullet(b).position,
110+
),
111+
acc,
112+
)
113+
114+
// Check if the bullet collides with something it can hit
115+
let #(bullets, player) = acc
116+
let player = case
117+
!b.belongs_to_player
118+
&& bullet.collides_with(b, player.position, player.player_size)
119+
{
120+
True -> player.apply_damage(player, bullet.enemy_damage)
121+
False -> player
122+
}
123+
124+
#([bullet.advance_bullet(b), ..bullets], player)
125+
})
126+
127+
GameRunning(..state, player: player, bullets: bullets)
83128
}
84129

85130
pub fn main() {
86131
p5js_gleam.create_sketch(init: setup, draw: draw)
87132
|> p5js_gleam.set_on_key_pressed(on_key_pressed)
88133
|> p5js_gleam.set_on_key_released(on_key_released)
89-
|> p5js_gleam.set_on_mouse_moved(on_mouse_moved)
134+
|> p5js_gleam.set_on_mouse_clicked(on_mouse_clicked)
90135
|> p5js_gleam.set_on_tick(on_tick)
91136
|> p5.start_sketch
92137
}

src/player.gleam

+5-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import p5js_gleam/bindings as p5
44
import utils
55
import vector.{type Vector}
66

7-
const player_size = 10.0
7+
pub const player_size = 10.0
88

99
const max_speed = 4.0
1010

@@ -30,8 +30,6 @@ pub type Player {
3030
velocity: Vector,
3131
/// The character's current acceleration.
3232
acceleration: Vector,
33-
/// The direction the player is firing relative to the player's position
34-
firing_direction: Vector,
3533
/// Last time that the player fired a bullet
3634
last_fire_time: Int,
3735
/// Last time that the player was hit
@@ -49,7 +47,6 @@ pub fn new_player(initial_position: Vector) -> Player {
4947
position: initial_position,
5048
velocity: vector.Vector(0.0, 0.0, 0.0),
5149
acceleration: vector.Vector(0.0, 0.0, 0.0),
52-
firing_direction: vector.Vector(0.0, 0.0, 0.0),
5350
last_fire_time: 0,
5451
last_hit_time: 0,
5552
current_health: base_max_health,
@@ -149,7 +146,7 @@ pub fn stop_y(player: Player) -> Player {
149146

150147
const player_gravity_strength = 0.02
151148

152-
// Applies gravity to the velocityy and resets z position to floor when appropriate.
149+
/// Applies gravity to the velocity and resets z position to floor when appropriate.
153150
pub fn apply_gravity(player: Player) -> Player {
154151
let position = case player.position.z {
155152
z if z <. 0.0 -> vector.Vector(player.position.x, player.position.y, 0.0)
@@ -166,12 +163,9 @@ pub fn apply_gravity(player: Player) -> Player {
166163
)
167164
}
168165

169-
/// Make player look towards a point.
170-
pub fn look_toward(player: Player, point: Vector) -> Player {
171-
Player(
172-
..player,
173-
firing_direction: vector.vector_2d(vector.subtract(point, player.position)),
174-
)
166+
/// Applies damage to the player
167+
pub fn apply_damage(player: Player, damage: Int) -> Player {
168+
Player(..player, current_health: player.current_health - damage)
175169
}
176170

177171
/// Is the player currently dead.

test/bullet_test.gleam

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import bullet.{type Bullet, Bullet}
2+
import startest.{describe, it}
3+
import startest/expect
4+
import vector.{Vector}
5+
6+
pub fn bullet_tests() {
7+
describe("bullet", [
8+
it("advance_bullet", fn() {
9+
expect.to_equal(
10+
bullet.advance_bullet(Bullet(
11+
Vector(0.0, 0.0, 0.0),
12+
Vector(5.0, 0.0, 0.0),
13+
True,
14+
0,
15+
)),
16+
Bullet(Vector(5.0, 0.0, 0.0), Vector(5.0, 0.0, 0.0), True, 0),
17+
)
18+
}),
19+
it("collides_with", fn() {
20+
expect.to_equal(
21+
bullet.collides_with(
22+
Bullet(Vector(0.0, 0.0, 0.0), Vector(5.0, 0.0, 0.0), True, 0),
23+
Vector(10.0, 0.0, 0.0),
24+
1.0,
25+
),
26+
False,
27+
)
28+
expect.to_equal(
29+
bullet.collides_with(
30+
Bullet(Vector(0.0, 0.0, 0.0), Vector(5.0, 0.0, 0.0), True, 0),
31+
Vector(0.0, 0.0, 0.0),
32+
1.0,
33+
),
34+
True,
35+
)
36+
}),
37+
])
38+
}

0 commit comments

Comments
 (0)