atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
14de3e1e8dc2a460103dbac0f56b745af96da78e
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/hazards.rsRust157 lines5.7 KB
1
use std::{ops::ControlFlow, time::SystemTime};
2
3
use axum::http::{StatusCode, header};
4
use rand::Rng;
5
use serde_json::json;
6
7
use super::{
8
    Cause, Floor, Outcome, Run,
9
    balance::{
10
        BURN_RUPTURE, COLD_EQ, CURVE_MISSES, DANGER_EQ, DEGLOVING, DEGLOVING_INTENSITY, FRIEND,
11
        HOT_EQ, PREMATURE_EQ, RUPTURE_HOT, RUPTURE_IN_BAND, RUPTURE_PREMATURE, RUPTURE_WARM,
12
        SUSPICION_LIMIT, WANDERER_VISIT, WINDOW,
13
    },
14
    techniques::Training,
15
};
16
use crate::problem::Problem;
17
18
type Interruption = Result<ControlFlow<Outcome>, Problem>;
19
20
impl Run {
21
    /// Accidents tied to a specific technique.
22
    pub(super) fn mishaps(&mut self, training: Training, intensity: i32) -> Interruption {
23
        if training == Training::Dry && intensity >= DEGLOVING_INTENSITY && self.chance(DEGLOVING) {
24
            return Err(self.end(Cause::Degloved));
25
        }
26
        Ok(ControlFlow::Continue(()))
27
    }
28
29
    /// Everything that can go wrong on a committed turn before the roll.
30
    pub(super) fn hazards(&mut self, intensity: i32) -> Interruption {
31
        let burn = if self.conditions.friction_burn {
32
            BURN_RUPTURE
33
        } else {
34
            0
35
        };
36
        let odds = if self.eq > PREMATURE_EQ - burn {
37
            None
38
        } else if self.eq <= DANGER_EQ - burn {
39
            Some(RUPTURE_IN_BAND)
40
        } else if self.eq <= HOT_EQ - burn {
41
            Some(RUPTURE_WARM)
42
        } else {
43
            Some(RUPTURE_HOT)
44
        };
45
        if odds.is_some_and(|odds| self.chance(odds)) {
46
            return Err(self.rupture());
47
        }
48
        if self.floor.has_window() && !self.items.blinds && self.chance(WINDOW) {
49
            self.shame += 30;
50
            self.filmed_at.get_or_insert_with(SystemTime::now);
51
            return Ok(ControlFlow::Break(
52
                Outcome::json(
53
                    StatusCode::CREATED,
54
                    json!({ "detail": "A neighbour was at the window. A recording has been created. It has been shared." }),
55
                )
56
                .header(header::LOCATION, self.path("/footage")),
57
            ));
58
        }
59
        if self.floor == Floor::ChangingRoom && self.chance(FRIEND) {
60
            self.shame += 1;
61
            return Err(Problem::new(
62
                StatusCode::USE_PROXY,
63
                "friend",
64
                "A friend has offered to help. Use Proxy is deprecated, and the server has declined on your behalf.",
65
            ));
66
        }
67
        if self.conditions.peyronie && self.chance(CURVE_MISSES) {
68
            return Err(Problem::new(
69
                StatusCode::MISDIRECTED_REQUEST,
70
                "misdirected",
71
                "The technique landed slightly to the left. It was meant for a different server.",
72
            ));
73
        }
74
        if intensity <= 2 && self.eq < 50 {
75
            self.eq = (self.eq - 10).max(0);
76
            return Err(Problem::new(
77
                StatusCode::REQUEST_TIMEOUT,
78
                "lost-it",
79
                "The request took too long. It was lost.",
80
            )
81
            .header(header::CONNECTION, "close"));
82
        }
83
        if self.eq > PREMATURE_EQ - burn {
84
            if self.chance(RUPTURE_PREMATURE) {
85
                return Err(self.rupture());
86
            }
87
            self.shame += 5;
88
            self.next_floor(false);
89
            return Err(Problem::new(
90
                StatusCode::TOO_EARLY,
91
                "premature",
92
                "The floor has ended early. Everyone involved has agreed not to discuss it.",
93
            ));
94
        }
95
        if self.eq < COLD_EQ {
96
            return Err(Problem::new(
97
                StatusCode::NOT_FOUND,
98
                "cold",
99
                "The resource exists. It is cold.",
100
            ));
101
        }
102
        Ok(ControlFlow::Continue(()))
103
    }
104
105
    /// The enemies suspicion summons on floors without Mum.
106
    pub(super) fn floor_enemy(&mut self) -> Result<(), Problem> {
107
        let visits = u32::from(self.suspicion) + 1;
108
        let wanders_in = matches!(self.floor, Floor::Bathroom | Floor::Hotel)
109
            && self.rng.random_ratio(visits, WANDERER_VISIT);
110
        if self.suspicion < SUSPICION_LIMIT && !wanders_in {
111
            return Ok(());
112
        }
113
        match self.floor {
114
            Floor::Bathroom => {
115
                self.suspicion = 0;
116
                self.touch();
117
                if self.items.door_locks > 0 {
118
                    self.items.door_locks -= 1;
119
                    self.notices.push("your brother tried the door");
120
                    return Ok(());
121
                }
122
                self.here.brother_told = true;
123
                Err(Problem::new(
124
                    StatusCode::CONFLICT,
125
                    "brother",
126
                    "Your brother needs the bathroom. He will tell Mum.",
127
                ))
128
            }
129
            Floor::ChangingRoom => {
130
                self.suspicion = 0;
131
                self.shame += 5;
132
                self.touch();
133
                Err(Problem::new(
134
                    StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
135
                    "gym-staff",
136
                    "Gym staff have been notified. The server cannot serve this request for legal reasons.",
137
                ))
138
            }
139
            Floor::Hotel => {
140
                self.suspicion = 0;
141
                self.touch();
142
                if self.items.dnd_sign {
143
                    self.notices.push("housekeeping read the sign");
144
                    return Ok(());
145
                }
146
                self.shame += 3;
147
                Err(Problem::new(
148
                    StatusCode::UNAUTHORIZED,
149
                    "housekeeping",
150
                    "Housekeeping has entered. Housekeeping accepts no excuses.",
151
                )
152
                .header(header::WWW_AUTHENTICATE, "Excuse realm=\"housekeeping\""))
153
            }
154
            _ => Ok(()),
155
        }
156
    }
157
}