atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
8b8e83329a1daec70177de3e129964094a711509
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/hazards.rsRust208 lines7.4 KB
1
use std::{ops::ControlFlow, time::SystemTime};
2
3
use axum::http::{StatusCode, header};
4
use serde_json::json;
5
6
use super::{
7
    Cause, Floor, Notice, Outcome, Run,
8
    balance::{
9
        BURN_RUPTURE, COLD_EQ, CURVE_MISSES, DANGER_EQ, DEGLOVING, DEGLOVING_INTENSITY, EQ_BAND,
10
        FRIEND, HOT_EQ, LOST_IT_EQ, PREMATURE_EQ, RUPTURE_HOT, RUPTURE_IN_BAND, RUPTURE_PREMATURE,
11
        RUPTURE_WARM, SOFT_INTENSITY, SUSPICION_LIMIT, WANDERER_VISIT, WINDOW,
12
    },
13
    techniques::Training,
14
    travel::Exit,
15
};
16
use crate::problem::Problem;
17
18
type Interruption = Result<ControlFlow<Outcome>, Problem>;
19
20
impl Run {
21
    /// Whether EQ is past `line`, with a friction burn dragging every line down.
22
    pub(super) fn hotter_than(&self, line: i32) -> bool {
23
        let burn = if self.body.conditions.friction_burn {
24
            BURN_RUPTURE
25
        } else {
26
            0
27
        };
28
        self.body.eq > line - burn
29
    }
30
31
    /// The rupture tier for any turn that is not a jelq, so never the premature band's 1 in 3.
32
    pub(super) fn rupture_odds(&self) -> u32 {
33
        if !self.hotter_than(DANGER_EQ) {
34
            RUPTURE_IN_BAND
35
        } else if !self.hotter_than(HOT_EQ) {
36
            RUPTURE_WARM
37
        } else {
38
            RUPTURE_HOT
39
        }
40
    }
41
42
    pub(super) fn mishaps(&mut self, training: Training, intensity: i32) -> Interruption {
43
        if training == Training::Dry
44
            && intensity >= DEGLOVING_INTENSITY
45
            && self.chance(
46
                self.decision("degloving", "Dry at this intensity, does the skin tear?"),
47
                DEGLOVING,
48
            )
49
        {
50
            return Err(self.end(Cause::Degloved));
51
        }
52
        Ok(ControlFlow::Continue(()))
53
    }
54
55
    pub(super) fn hazards(&mut self, intensity: i32) -> Interruption {
56
        if !self.hotter_than(PREMATURE_EQ)
57
            && self.chance(
58
                self.decision("rupture", "Does this jelq rupture something?"),
59
                self.rupture_odds(),
60
            )
61
        {
62
            return Err(self.rupture());
63
        }
64
        if self.floor.has_window()
65
            && !self.items.kit.blinds
66
            && self.oracle.chance_not(
67
                self.decision("window", "Are the curtains still closed?"),
68
                WINDOW,
69
            )
70
        {
71
            self.standing.shame += 30;
72
            self.online.filmed_at.get_or_insert_with(SystemTime::now);
73
            return Ok(ControlFlow::Break(
74
                Outcome::json(
75
                    StatusCode::CREATED,
76
                    json!({ "detail": "A neighbour was at the window. A recording has been created. It has been shared." }),
77
                )
78
                .header(header::LOCATION, self.path("/footage")),
79
            ));
80
        }
81
        if self.floor == Floor::ChangingRoom
82
            && self.chance(
83
                self.decision("friend", "Does a friend walk in and offer to help?"),
84
                FRIEND,
85
            )
86
        {
87
            self.standing.shame += 1;
88
            return Err(Problem::new(
89
                StatusCode::USE_PROXY,
90
                "friend",
91
                "A friend has offered to help. Use Proxy is deprecated, and the server has declined on your behalf.",
92
            ));
93
        }
94
        if self.body.conditions.peyronie
95
            && self.chance(
96
                self.decision(
97
                    "misdirected",
98
                    "With the curve, does the stroke land sharply to the left?",
99
                ),
100
                CURVE_MISSES,
101
            )
102
        {
103
            return Err(Problem::new(
104
                StatusCode::MISDIRECTED_REQUEST,
105
                "misdirected",
106
                "The technique landed slightly to the left. It was meant for a different server.",
107
            ));
108
        }
109
        if intensity <= SOFT_INTENSITY && self.body.eq < *EQ_BAND.start() {
110
            self.body.eq = (self.body.eq - LOST_IT_EQ).max(0);
111
            return Err(Problem::new(
112
                StatusCode::REQUEST_TIMEOUT,
113
                "lost-it",
114
                "The request took too long. It was lost.",
115
            )
116
            .header(header::CONNECTION, "close"));
117
        }
118
        if self.hotter_than(PREMATURE_EQ) {
119
            if self.chance(
120
                self.decision(
121
                    "rupture",
122
                    "Right at the edge of finishing, does it rupture instead?",
123
                ),
124
                RUPTURE_PREMATURE,
125
            ) {
126
                return Err(self.rupture());
127
            }
128
            self.standing.shame += 5;
129
            self.next_floor(Exit::Premature);
130
            return Err(Problem::new(
131
                StatusCode::TOO_EARLY,
132
                "premature",
133
                "The floor has ended early. Everyone involved has agreed not to discuss it.",
134
            ));
135
        }
136
        if self.body.eq < COLD_EQ {
137
            return Err(Problem::new(
138
                StatusCode::NOT_FOUND,
139
                "cold",
140
                "The resource exists. It is cold.",
141
            ));
142
        }
143
        Ok(ControlFlow::Continue(()))
144
    }
145
146
    /// The enemies suspicion summons on floors without Mum.
147
    pub(super) fn floor_enemy(&mut self) -> Result<(), Problem> {
148
        let visits = u32::from(self.standing.suspicion) + 1;
149
        let wanders_in = matches!(self.floor, Floor::Bathroom | Floor::Hotel)
150
            && self.oracle.ratio_choice(
151
                self.decision("wanderer", "Does anyone else come through the door?"),
152
                visits,
153
                WANDERER_VISIT,
154
                "No",
155
                "Yes",
156
            );
157
        if self.standing.suspicion < SUSPICION_LIMIT && !wanders_in {
158
            return Ok(());
159
        }
160
        match self.floor {
161
            Floor::Bathroom => {
162
                self.standing.suspicion = 0;
163
                self.touch();
164
                if self.items.stores.door_locks > 0 {
165
                    self.items.stores.door_locks -= 1;
166
                    self.notices.push(Notice::BrotherTriedTheDoor);
167
                    return Ok(());
168
                }
169
                self.here.brother_told = true;
170
                Err(Problem::new(
171
                    StatusCode::CONFLICT,
172
                    "brother",
173
                    "Your brother needs the bathroom. He will tell Mum.",
174
                ))
175
            }
176
            Floor::ChangingRoom => {
177
                self.standing.suspicion = 0;
178
                self.touch();
179
                if self.girth_respected() {
180
                    self.notices.push(Notice::GymStaffLeaveIt);
181
                    return Ok(());
182
                }
183
                self.standing.shame += 5;
184
                Err(Problem::new(
185
                    StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
186
                    "gym-staff",
187
                    "Gym staff have been notified. The server cannot serve this request for legal reasons.",
188
                ))
189
            }
190
            Floor::Hotel => {
191
                self.standing.suspicion = 0;
192
                self.touch();
193
                if self.items.kit.dnd_sign {
194
                    self.notices.push(Notice::HousekeepingReadTheSign);
195
                    return Ok(());
196
                }
197
                self.standing.shame += 3;
198
                Err(Problem::new(
199
                    StatusCode::UNAUTHORIZED,
200
                    "housekeeping",
201
                    "Housekeeping has entered. Housekeeping accepts no excuses.",
202
                )
203
                .header(header::WWW_AUTHENTICATE, "Excuse realm=\"housekeeping\""))
204
            }
205
            _ => Ok(()),
206
        }
207
    }
208
}