summaryrefslogtreecommitdiff
path: root/2026/games_submissions/rpc2dot0/src/disquette.rs
blob: 7940d1769f9fd4b76f5c9e1a0bab778c8da2bedc (plain)
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use crate::math::{ContainsPoint, Rectangle};
use crate::random::RandomState;
use teleia::state;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisquettePhase {
    Free,
    Inserted,
}

impl From<DisquettePhase> for &'static str {
    fn from(value: DisquettePhase) -> Self {
        match value {
            DisquettePhase::Free => "Free",
            DisquettePhase::Inserted => "Inserted",
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct Disquette {
    pub coord: glam::Vec2,
    pub vel: glam::Vec2,
    pub size: f32,
    pub phase: DisquettePhase,
}

impl Disquette {
    pub fn new(
        logical_width: f32,
        logical_height: f32,
        rng: &mut RandomState,
        computer_hitzone: Option<Rectangle>,
    ) -> Self {
        const SIZE: f32 = 6.4;
        const HALF: f32 = SIZE / 2.0;
        log::info!(
            "eatyourgreens:: Disquette new at ({}, {})",
            logical_width,
            logical_height
        );

        let x = rng.next_range(HALF, logical_width - HALF);
        let y = rng.next_range(HALF, logical_height - HALF);
        let mut coord = glam::Vec2::new(x, y);

        // prevent spawning inside the computer hitzone
        // this is a bit stupidos, but it works for now.
        // @TODO: find a better way to do this. like using rng api (random.rs) where you can
        // specify the excluded area, or something less retarded.
        if let Some(hitzone) = computer_hitzone {
            // wasting few cpu cycles doesn't hurt anybody
            while hitzone.contains(coord) {
                coord.x = rng.next_range(HALF, logical_width - HALF);
                coord.y = rng.next_range(HALF, logical_height - HALF);
            }
        }

        Self {
            coord,
            vel: glam::Vec2::new(
                // -/+ 21.3 as value cause it works, yeah idc
                if rng.next_bool(0.5) { 21.3 } else { -21.3 },
                if rng.next_bool(0.5) { 21.3 } else { -21.3 },
            ),
            size: SIZE,
            phase: DisquettePhase::Free,
        }
    }

    pub fn is_inserted(&self) -> bool {
        self.phase == DisquettePhase::Inserted
    }

    pub fn update(
        &mut self,
        width: f32,
        height: f32,
        keys: &state::Keys,
        delta_ms: f32,
        difficulty: f32,
        computer_hitzone: Option<Rectangle>,
    ) {
        let dt = delta_ms / 1000.0;
        let impulse = 106.7 * difficulty;

        let driven_by_user = keys.left() || keys.right() || keys.up() || keys.down();

        if keys.left() {
            self.vel.x -= impulse * dt;
        }
        if keys.right() {
            self.vel.x += impulse * dt;
        }
        if keys.up() {
            self.vel.y -= impulse * dt;
        }
        if keys.down() {
            self.vel.y += impulse * dt;
        }

        // speed limiting
        // @TODO : hum value below was adjusted manualy it might
        // be wrong with game rendering speed? ??
        let max_speed = 53.3 * difficulty;
        let speed = self.vel.length();
        if speed > max_speed {
            self.vel = self.vel.normalize() * max_speed;
        }

        self.coord += self.vel * dt;

        let half = self.size / 2.0;
        let min_bounds = glam::Vec2::splat(half);
        let max_bounds = glam::Vec2::new(width - half, height - half);

        // boundary collision and bouncing
        // left edge : clamp position and ensure it moves right
        if self.coord.x < min_bounds.x {
            self.coord.x = min_bounds.x;
            // .abs() guarantees velocity is positive (moving right)
            self.vel.x = self.vel.x.abs();
        }
        // right edge : clamp position and ensure it moves left
        else if self.coord.x > max_bounds.x {
            self.coord.x = max_bounds.x;
            // -.abs() guarantees velocity is negative (moving left)
            self.vel.x = -self.vel.x.abs();
        }

        // top and bottom edges
        if self.coord.y < min_bounds.y {
            self.coord.y = min_bounds.y;
            // .abs() guarantees velocity is positive (moving down)
            self.vel.y = self.vel.y.abs();
        } else if self.coord.y > max_bounds.y {
            self.coord.y = max_bounds.y;
            // -.abs() guarantees velocity is negative (moving up)
            self.vel.y = -self.vel.y.abs();
        }

        // bounce off computer hitzone unless user is actively driving it in
        if !driven_by_user && let Some(hitzone) = computer_hitzone {
            let prev_pos = self.coord - self.vel * dt;

            if hitzone.contains(self.coord) {
                let was_outside = !hitzone.contains(prev_pos);
                if was_outside {
                    let was_left = prev_pos.x < hitzone.min.x;
                    let was_right = prev_pos.x > hitzone.max.x;
                    let was_top = prev_pos.y < hitzone.min.y;
                    let was_bottom = prev_pos.y > hitzone.max.y;

                    if was_left || was_right {
                        // reverse horizontal velocity to bounce off
                        self.vel.x = -self.vel.x;
                        // +/- 0.1 is a tiny extra push away from the hitzone.
                        // this prevent the disquette from getting stuck exactly on the boundary
                        // and repeatedly triggers collisions
                        // this bug took 2h from my life, fuck you disquette, you floppy disk :/.
                        self.coord.x = if was_left {
                            hitzone.min.x - 0.1
                        } else {
                            hitzone.max.x + 0.1
                        };
                    }
                    if was_top || was_bottom {
                        // reverse vertical velocity to bounce off
                        self.vel.y = -self.vel.y;
                        // +/- 0.1 adds a tiny gap to safely escape the hitzone
                        self.coord.y = if was_top {
                            hitzone.min.y - 0.1
                        } else {
                            hitzone.max.y + 0.1
                        };
                    }
                }
            }
        }
    }
}

// the hours spent on this zone/bouncing detection bs, I should've done something more productive, but
// whatever.