atagen

jelq

public

an http based roguelike rpg about jelqing

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