~ntgg/fundies2-game

ref: 1aea715cac95d81482da2b0f4cfd636f65c4f666 fundies2-game/src/systems.rs -rw-r--r-- 4.9 KiB
1aea715c — Noah Graff works with ggez and stable rust 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use crate::vec2::Vec2;
use crate::components::*;
use pyro::{All, Read, Write};
use ggez::Context;
use ggez::graphics::{self, DrawMode, FillOptions, Color};

pub fn shoot_bullet(ctx: &mut Context, world: &mut pyro::World) {
    let screen_rect = graphics::screen_coordinates(ctx);
    let pos = Pos(Vec2::new(screen_rect.w / 2.0, screen_rect.bottom()));
    let vel = Vel(Vec2::new(0.0, -Bullet::SPEED));
    let mesh = graphics::Mesh::new_circle(
        ctx,
        DrawMode::Fill(FillOptions::DEFAULT),
        Vec2::new(0.0, 0.0),
        1.0,
        0.0,
        Color::new(1.0, 0.0, 0.0, 1.0),
    ).expect("could not create mesh");
    world.append_components(Some((pos, vel, Bullet(0), mesh)))
}

pub fn move_balls(ctx: &mut Context, world: &mut pyro::World) {
    let dt = ggez::timer::delta(ctx).as_secs_f32();

    world.matcher::<All<(Write<Pos>, Read<Vel>)>>()
        .for_each(|(pos, vel)| {
            pos.0 += vel.0 * dt;
        });
}

pub fn remove_offscreen(ctx: &mut Context, world: &mut pyro::World) {
    let screen_bounds = graphics::screen_coordinates(ctx);

    let offscreen: Vec<_> = world.matcher_with_entities::<All<(Read<Pos>,)>>()
        .filter_map(|(entity, (pos,))| if pos.0.in_rect(screen_bounds) {
            None
        } else {
            Some(entity)
        }).collect();
    
    world.remove_entities(offscreen);
}

// returns the amount of ships destroyed.
pub fn handle_collision(ctx: &mut Context, world: &mut pyro::World) -> u32 {
    use std::f32;
    let mut spawn = Vec::new();
    let mut to_remove = Vec::new();
    let mut ships_destroyed = 0;

    world
        .matcher_with_entities::<All<(Read<Ship>, Read<Pos>)>>()
        .for_each(|(ship_entity, (_ship, ship_pos))| {
            world
                .matcher_with_entities::<All<(Read<Bullet>, Read<Pos>)>>()
                .for_each(|(bullet_entity, (bullet, bullet_pos))| {
                    if ship_pos.0.distance_to(bullet_pos.0) <= (bullet.size() + Ship::size(ctx))
                        && !to_remove.contains(&ship_entity)
                    {
                        to_remove.push(ship_entity);
                        to_remove.push(bullet_entity);

                        ships_destroyed += 1;

                        let to_spawn = std::cmp::min(bullet.0 + 1, 9);
                        println!("a ship and bullet hit! spawning {} bullets", to_spawn + 1);

                        for i in 0..=to_spawn {
                            let b = Bullet(to_spawn);
                            let mesh = graphics::Mesh::new_circle(
                                ctx,
                                DrawMode::Fill(FillOptions::DEFAULT),
                                Vec2::new(0.0, 0.0),
                                b.size(),
                                0.1,
                                Color::new(1.0, 0.0, 0.0, 1.0),
                            ).expect("could not create mesh");
                            
                            let angle = i as f32 * (2.0 * f32::consts::PI / (to_spawn + 1) as f32);

                            spawn.push((*bullet_pos, Vel(Vec2::from_angle(angle, Bullet::SPEED)), b, mesh));
                        }
                    }
                });
        });
    
    world.remove_entities(to_remove);
    world.append_components(spawn);

    ships_destroyed
}

pub fn draw_world(ctx: &mut Context, world: &mut pyro::World) -> ggez::GameResult {
    for (pos, mesh) in world.matcher::<All<(Read<Pos>, Read<graphics::Mesh>)>>() {
        graphics::draw(
            ctx,
            mesh,
            graphics::DrawParam::default()
                .dest(pos.0),
        )?;
    }

    Ok(())
}

pub fn spawn_ships(ctx: &mut Context, world: &mut pyro::World) {
    use rand::Rng;

    let screen_bounds = graphics::screen_coordinates(ctx);
    let mut rng = rand::thread_rng();
    let count = rng.gen_range(0, 5) + 1;
    let mesh = graphics::Mesh::new_circle(
        ctx,
        DrawMode::Fill(FillOptions::DEFAULT),
        Vec2::new(0.0, 0.0),
        Ship::size(ctx),
        0.1,
        Color::new(0.2, 0.3, 0.6, 1.0),
    ).expect("could not create mesh");

    println!("spawning {} ship(s)", count);

    let mut ships = Vec::with_capacity(count as usize);
    for _ in 0..count {
        let side = rng.gen();
        let x = if side {
            screen_bounds.w
        } else {
            0.0
        };
        let y = rng.gen_range(screen_bounds.h / 7.0, 6.0 * screen_bounds.h / 7.0);

        ships.push(
            (
                Pos(Vec2::new(x, y)),

                if side {
                    Vel(Vec2::new(-300.0, 0.0))
                } else {
                    Vel(Vec2::new(300.0, 0.0))
                },

                Ship,

                mesh.to_owned(),
            )
        );
    }

    world.append_components(ships);
}

pub fn bullets_on_screen(world: &mut pyro::World) -> usize {
    world.matcher::<All<(Read<Bullet>,)>>().count()
}