module main import ( std.math math vendor.raylib rl ) let calculate_flap_velocity(): f64 = math.sqrt(-2.0 * gravity * flap_height) let random_pipe_gap_y(): f64 { let margin: f64 = 0.05 let minimum_gap_y = base_height_fraction + pipe_gap_size / 2.0 + margin let maximum_gap_y = 1.0 - pipe_gap_size / 2.0 - margin let random_value = rl.get_random_value(0, 1000).(f64) / 1000.0 return minimum_gap_y + random_value * (maximum_gap_y - minimum_gap_y) } let initialise_pipes(pipes: []mut Pipe) { let first_pipe_x: f64 = 1.2 for let mut i: usz = 0; i < @len(pipes); i += 1 { pipes[i].x = first_pipe_x + i.(f64) * pipe_spacing pipes[i].gap_y = random_pipe_gap_y() pipes[i].passed = false } } let update_bird(state: *mut GameState, assets: *GameAssets, flap: bool) { if flap { state.bird_velocity_y = calculate_flap_velocity() rl.play_sound(assets.wing) } state.bird_velocity_y = max( state.bird_velocity_y + gravity * state.delta_time, terminal_velocity, ) state.bird_y = state.bird_y + state.bird_velocity_y * state.delta_time let floor_y = bird_size / 2.0 let ceiling_y = 1.0 - bird_size / 2.0 if state.bird_y <= floor_y { state.bird_y = floor_y state.bird_velocity_y = 0.0 state.lost = true return } if state.bird_y >= ceiling_y { state.bird_y = ceiling_y state.bird_velocity_y = 0.0 state.lost = true } } let find_rightmost_pipe_x(pipes: []Pipe): f64 { let mut rightmost_x = pipes[0].x for pipe in pipes { rightmost_x = max(rightmost_x, pipe.x) } return rightmost_x } let update_pipes(state: *mut GameState): bool { let speed_multiplier = if state.double_pipe_speed { double_pipe_speed_multiplier } else { 1.0 } let movement = pipe_speed * speed_multiplier * state.delta_time let mut passed_pipe = false for let mut i: usz = 0; i < @len(state.pipes); i += 1 { state.pipes[i].x = state.pipes[i].x - movement if !state.pipes[i].passed && state.pipes[i].x < bird_x { state.pipes[i].passed = true passed_pipe = true } } let pipe_half_width = pipe_width / 2.0 let mut rightmost_x = find_rightmost_pipe_x(state.pipes) // Reuse pipes that have left the screen instead of shifting or allocating. for let mut i: usz = 0; i < @len(state.pipes); i += 1 { if state.pipes[i].x + pipe_half_width < 0.0 { state.pipes[i].x = rightmost_x + pipe_spacing state.pipes[i].gap_y = random_pipe_gap_y() state.pipes[i].passed = false rightmost_x = state.pipes[i].x } } if passed_pipe { state.points += if state.double_pipe_speed { double_pipe_points_multiplier } else { 1 } } return passed_pipe } let reset_game(state: *mut GameState) { state.bird_y = 0.5 state.bird_velocity_y = 0.0 state.delta_time = 0.0 state.points = 0 state.lost = false state.time_since_loss = 0.0 initialise_pipes(state.pipes) } let play_loss_sounds(assets: *GameAssets) { rl.play_sound(assets.hit) rl.play_sound(assets.die) } let update(state: *mut GameState, assets: *GameAssets) { state.delta_time = rl.get_frame_time() let action_pressed = rl.is_key_pressed(.Space) || rl.is_mouse_button_pressed(.Left) if state.started && !state.lost && rl.is_key_pressed(.Escape) { state.paused = !state.paused } if rl.is_key_pressed(.F3) { state.show_collision_boxes = !state.show_collision_boxes } if (!state.started || state.lost) && rl.is_key_pressed(.F9) { state.double_pipe_speed = !state.double_pipe_speed } if !state.started { if action_pressed { state.started = true } else { return } } if state.paused && action_pressed { state.paused = false } if state.paused { return } if state.lost { state.time_since_loss += state.delta_time if state.time_since_loss < restart_grace_period || !action_pressed { return } reset_game(state) } state.background_scroll = state.background_scroll + pipe_speed * background_scroll_speed * state.delta_time if state.background_scroll >= 1.0 { state.background_scroll = state.background_scroll - 1.0 } state.base_scroll = state.base_scroll + pipe_speed * base_scroll_speed * state.delta_time if state.base_scroll >= 1.0 { state.base_scroll = state.base_scroll - 1.0 } update_bird(state, assets, action_pressed) if state.lost { play_loss_sounds(assets) return } if update_pipes(state) { rl.play_sound(assets.point) } if bird_collides_with_pipes(state) { state.lost = true play_loss_sounds(assets) } }