atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
14de3e1e8dc2a460103dbac0f56b745af96da78e
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/game/hazards.rs

782069860236atagen2026-10-10 12:44 UTC1
use std::{ops::ControlFlow, time::SystemTime};
782069860236atagen2026-10-10 12:44 UTC2
782069860236atagen2026-10-10 12:44 UTC3
use axum::http::{StatusCode, header};
782069860236atagen2026-10-10 12:44 UTC4
use rand::Rng;
782069860236atagen2026-10-10 12:44 UTC5
use serde_json::json;
782069860236atagen2026-10-10 12:44 UTC6
782069860236atagen2026-10-10 12:44 UTC7
use super::{
782069860236atagen2026-10-10 12:44 UTC8
    Cause, Floor, Outcome, Run,
782069860236atagen2026-10-10 12:44 UTC9
    balance::{
139164489032atagen2026-10-11 05:46 UTC10
        BURN_RUPTURE, COLD_EQ, CURVE_MISSES, DANGER_EQ, DEGLOVING, DEGLOVING_INTENSITY, FRIEND,
139164489032atagen2026-10-11 05:46 UTC11
        HOT_EQ, PREMATURE_EQ, RUPTURE_HOT, RUPTURE_IN_BAND, RUPTURE_PREMATURE, RUPTURE_WARM,
782069860236atagen2026-10-10 12:44 UTC12
        SUSPICION_LIMIT, WANDERER_VISIT, WINDOW,
782069860236atagen2026-10-10 12:44 UTC13
    },
782069860236atagen2026-10-10 12:44 UTC14
    techniques::Training,
782069860236atagen2026-10-10 12:44 UTC15
};
782069860236atagen2026-10-10 12:44 UTC16
use crate::problem::Problem;
782069860236atagen2026-10-10 12:44 UTC17
782069860236atagen2026-10-10 12:44 UTC18
type Interruption = Result<ControlFlow<Outcome>, Problem>;
782069860236atagen2026-10-10 12:44 UTC19
782069860236atagen2026-10-10 12:44 UTC20
impl Run {
782069860236atagen2026-10-10 12:44 UTC21
    /// Accidents tied to a specific technique.
782069860236atagen2026-10-10 12:44 UTC22
    pub(super) fn mishaps(&mut self, training: Training, intensity: i32) -> Interruption {
782069860236atagen2026-10-10 12:44 UTC23
        if training == Training::Dry && intensity >= DEGLOVING_INTENSITY && self.chance(DEGLOVING) {
782069860236atagen2026-10-10 12:44 UTC24
            return Err(self.end(Cause::Degloved));
782069860236atagen2026-10-10 12:44 UTC25
        }
782069860236atagen2026-10-10 12:44 UTC26
        Ok(ControlFlow::Continue(()))
782069860236atagen2026-10-10 12:44 UTC27
    }
782069860236atagen2026-10-10 12:44 UTC28
782069860236atagen2026-10-10 12:44 UTC29
    /// Everything that can go wrong on a committed turn before the roll.
782069860236atagen2026-10-10 12:44 UTC30
    pub(super) fn hazards(&mut self, intensity: i32) -> Interruption {
139164489032atagen2026-10-11 05:46 UTC31
        let burn = if self.conditions.friction_burn {
139164489032atagen2026-10-11 05:46 UTC32
            BURN_RUPTURE
139164489032atagen2026-10-11 05:46 UTC33
        } else {
139164489032atagen2026-10-11 05:46 UTC34
            0
139164489032atagen2026-10-11 05:46 UTC35
        };
14de3e1e8dc2atagen2026-10-11 06:19 UTC36
        let odds = if self.eq > PREMATURE_EQ - burn {
14de3e1e8dc2atagen2026-10-11 06:19 UTC37
            None
14de3e1e8dc2atagen2026-10-11 06:19 UTC38
        } else if self.eq <= DANGER_EQ - burn {
14de3e1e8dc2atagen2026-10-11 06:19 UTC39
            Some(RUPTURE_IN_BAND)
139164489032atagen2026-10-11 05:46 UTC40
        } else if self.eq <= HOT_EQ - burn {
14de3e1e8dc2atagen2026-10-11 06:19 UTC41
            Some(RUPTURE_WARM)
782069860236atagen2026-10-10 12:44 UTC42
        } else {
14de3e1e8dc2atagen2026-10-11 06:19 UTC43
            Some(RUPTURE_HOT)
782069860236atagen2026-10-10 12:44 UTC44
        };
14de3e1e8dc2atagen2026-10-11 06:19 UTC45
        if odds.is_some_and(|odds| self.chance(odds)) {
782069860236atagen2026-10-10 12:44 UTC46
            return Err(self.rupture());
782069860236atagen2026-10-10 12:44 UTC47
        }
782069860236atagen2026-10-10 12:44 UTC48
        if self.floor.has_window() && !self.items.blinds && self.chance(WINDOW) {
782069860236atagen2026-10-10 12:44 UTC49
            self.shame += 30;
782069860236atagen2026-10-10 12:44 UTC50
            self.filmed_at.get_or_insert_with(SystemTime::now);
782069860236atagen2026-10-10 12:44 UTC51
            return Ok(ControlFlow::Break(
782069860236atagen2026-10-10 12:44 UTC52
                Outcome::json(
782069860236atagen2026-10-10 12:44 UTC53
                    StatusCode::CREATED,
782069860236atagen2026-10-10 12:44 UTC54
                    json!({ "detail": "A neighbour was at the window. A recording has been created. It has been shared." }),
782069860236atagen2026-10-10 12:44 UTC55
                )
782069860236atagen2026-10-10 12:44 UTC56
                .header(header::LOCATION, self.path("/footage")),
782069860236atagen2026-10-10 12:44 UTC57
            ));
782069860236atagen2026-10-10 12:44 UTC58
        }
9683736a2c5eatagen2026-10-10 13:15 UTC59
        if self.floor == Floor::ChangingRoom && self.chance(FRIEND) {
782069860236atagen2026-10-10 12:44 UTC60
            self.shame += 1;
782069860236atagen2026-10-10 12:44 UTC61
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC62
                StatusCode::USE_PROXY,
782069860236atagen2026-10-10 12:44 UTC63
                "friend",
782069860236atagen2026-10-10 12:44 UTC64
                "A friend has offered to help. Use Proxy is deprecated, and the server has declined on your behalf.",
782069860236atagen2026-10-10 12:44 UTC65
            ));
782069860236atagen2026-10-10 12:44 UTC66
        }
782069860236atagen2026-10-10 12:44 UTC67
        if self.conditions.peyronie && self.chance(CURVE_MISSES) {
782069860236atagen2026-10-10 12:44 UTC68
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC69
                StatusCode::MISDIRECTED_REQUEST,
782069860236atagen2026-10-10 12:44 UTC70
                "misdirected",
782069860236atagen2026-10-10 12:44 UTC71
                "The technique landed slightly to the left. It was meant for a different server.",
782069860236atagen2026-10-10 12:44 UTC72
            ));
782069860236atagen2026-10-10 12:44 UTC73
        }
782069860236atagen2026-10-10 12:44 UTC74
        if intensity <= 2 && self.eq < 50 {
782069860236atagen2026-10-10 12:44 UTC75
            self.eq = (self.eq - 10).max(0);
782069860236atagen2026-10-10 12:44 UTC76
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC77
                StatusCode::REQUEST_TIMEOUT,
782069860236atagen2026-10-10 12:44 UTC78
                "lost-it",
782069860236atagen2026-10-10 12:44 UTC79
                "The request took too long. It was lost.",
782069860236atagen2026-10-10 12:44 UTC80
            )
782069860236atagen2026-10-10 12:44 UTC81
            .header(header::CONNECTION, "close"));
782069860236atagen2026-10-10 12:44 UTC82
        }
139164489032atagen2026-10-11 05:46 UTC83
        if self.eq > PREMATURE_EQ - burn {
782069860236atagen2026-10-10 12:44 UTC84
            if self.chance(RUPTURE_PREMATURE) {
782069860236atagen2026-10-10 12:44 UTC85
                return Err(self.rupture());
782069860236atagen2026-10-10 12:44 UTC86
            }
782069860236atagen2026-10-10 12:44 UTC87
            self.shame += 5;
782069860236atagen2026-10-10 12:44 UTC88
            self.next_floor(false);
782069860236atagen2026-10-10 12:44 UTC89
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC90
                StatusCode::TOO_EARLY,
782069860236atagen2026-10-10 12:44 UTC91
                "premature",
782069860236atagen2026-10-10 12:44 UTC92
                "The floor has ended early. Everyone involved has agreed not to discuss it.",
782069860236atagen2026-10-10 12:44 UTC93
            ));
782069860236atagen2026-10-10 12:44 UTC94
        }
782069860236atagen2026-10-10 12:44 UTC95
        if self.eq < COLD_EQ {
782069860236atagen2026-10-10 12:44 UTC96
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC97
                StatusCode::NOT_FOUND,
782069860236atagen2026-10-10 12:44 UTC98
                "cold",
782069860236atagen2026-10-10 12:44 UTC99
                "The resource exists. It is cold.",
782069860236atagen2026-10-10 12:44 UTC100
            ));
782069860236atagen2026-10-10 12:44 UTC101
        }
782069860236atagen2026-10-10 12:44 UTC102
        Ok(ControlFlow::Continue(()))
782069860236atagen2026-10-10 12:44 UTC103
    }
782069860236atagen2026-10-10 12:44 UTC104
9683736a2c5eatagen2026-10-10 13:15 UTC105
    /// The enemies suspicion summons on floors without Mum.
782069860236atagen2026-10-10 12:44 UTC106
    pub(super) fn floor_enemy(&mut self) -> Result<(), Problem> {
782069860236atagen2026-10-10 12:44 UTC107
        let visits = u32::from(self.suspicion) + 1;
782069860236atagen2026-10-10 12:44 UTC108
        let wanders_in = matches!(self.floor, Floor::Bathroom | Floor::Hotel)
782069860236atagen2026-10-10 12:44 UTC109
            && self.rng.random_ratio(visits, WANDERER_VISIT);
782069860236atagen2026-10-10 12:44 UTC110
        if self.suspicion < SUSPICION_LIMIT && !wanders_in {
782069860236atagen2026-10-10 12:44 UTC111
            return Ok(());
782069860236atagen2026-10-10 12:44 UTC112
        }
782069860236atagen2026-10-10 12:44 UTC113
        match self.floor {
782069860236atagen2026-10-10 12:44 UTC114
            Floor::Bathroom => {
782069860236atagen2026-10-10 12:44 UTC115
                self.suspicion = 0;
782069860236atagen2026-10-10 12:44 UTC116
                self.touch();
782069860236atagen2026-10-10 12:44 UTC117
                if self.items.door_locks > 0 {
782069860236atagen2026-10-10 12:44 UTC118
                    self.items.door_locks -= 1;
7d3fb63208bfatagen2026-10-11 04:38 UTC119
                    self.notices.push("your brother tried the door");
782069860236atagen2026-10-10 12:44 UTC120
                    return Ok(());
782069860236atagen2026-10-10 12:44 UTC121
                }
7d3fb63208bfatagen2026-10-11 04:38 UTC122
                self.here.brother_told = true;
782069860236atagen2026-10-10 12:44 UTC123
                Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC124
                    StatusCode::CONFLICT,
7d3fb63208bfatagen2026-10-11 04:38 UTC125
                    "brother",
7d3fb63208bfatagen2026-10-11 04:38 UTC126
                    "Your brother needs the bathroom. He will tell Mum.",
782069860236atagen2026-10-10 12:44 UTC127
                ))
782069860236atagen2026-10-10 12:44 UTC128
            }
9683736a2c5eatagen2026-10-10 13:15 UTC129
            Floor::ChangingRoom => {
782069860236atagen2026-10-10 12:44 UTC130
                self.suspicion = 0;
782069860236atagen2026-10-10 12:44 UTC131
                self.shame += 5;
782069860236atagen2026-10-10 12:44 UTC132
                self.touch();
782069860236atagen2026-10-10 12:44 UTC133
                Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC134
                    StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
782069860236atagen2026-10-10 12:44 UTC135
                    "gym-staff",
782069860236atagen2026-10-10 12:44 UTC136
                    "Gym staff have been notified. The server cannot serve this request for legal reasons.",
782069860236atagen2026-10-10 12:44 UTC137
                ))
782069860236atagen2026-10-10 12:44 UTC138
            }
782069860236atagen2026-10-10 12:44 UTC139
            Floor::Hotel => {
782069860236atagen2026-10-10 12:44 UTC140
                self.suspicion = 0;
782069860236atagen2026-10-10 12:44 UTC141
                self.touch();
782069860236atagen2026-10-10 12:44 UTC142
                if self.items.dnd_sign {
d46f9d0b953aatagen2026-10-10 14:41 UTC143
                    self.notices.push("housekeeping read the sign");
782069860236atagen2026-10-10 12:44 UTC144
                    return Ok(());
782069860236atagen2026-10-10 12:44 UTC145
                }
782069860236atagen2026-10-10 12:44 UTC146
                self.shame += 3;
782069860236atagen2026-10-10 12:44 UTC147
                Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC148
                    StatusCode::UNAUTHORIZED,
782069860236atagen2026-10-10 12:44 UTC149
                    "housekeeping",
782069860236atagen2026-10-10 12:44 UTC150
                    "Housekeeping has entered. Housekeeping accepts no excuses.",
782069860236atagen2026-10-10 12:44 UTC151
                )
782069860236atagen2026-10-10 12:44 UTC152
                .header(header::WWW_AUTHENTICATE, "Excuse realm=\"housekeeping\""))
782069860236atagen2026-10-10 12:44 UTC153
            }
782069860236atagen2026-10-10 12:44 UTC154
            _ => Ok(()),
782069860236atagen2026-10-10 12:44 UTC155
        }
782069860236atagen2026-10-10 12:44 UTC156
    }
782069860236atagen2026-10-10 12:44 UTC157
}