atagen

jelq

public

an http based roguelike rpg about jelqing

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

Blame: src/game/utility.rs

782069860236atagen2026-10-10 12:44 UTC1
use std::{
782069860236atagen2026-10-10 12:44 UTC2
    ops::ControlFlow,
a6caa2f99786atagen2026-10-12 10:38 UTC3
    time::{Duration, SystemTime},
782069860236atagen2026-10-10 12:44 UTC4
};
782069860236atagen2026-10-10 12:44 UTC5
782069860236atagen2026-10-10 12:44 UTC6
use axum::http::{StatusCode, header};
782069860236atagen2026-10-10 12:44 UTC7
use serde_json::json;
782069860236atagen2026-10-10 12:44 UTC8
782069860236atagen2026-10-10 12:44 UTC9
use super::{
782069860236atagen2026-10-10 12:44 UTC10
    Floor, Outcome, Run,
782069860236atagen2026-10-10 12:44 UTC11
    balance::{
0a0bfee8329eatagen2026-10-11 05:11 UTC12
        EDGE_EQ, EDGE_GIRTH, EDGE_LOOP, ENERGY_STAMINA, HOTEL_STRESS, ICE_EQ, PRIAPISM_EQ,
0a0bfee8329eatagen2026-10-11 05:11 UTC13
        PRIAPISM_TURNS, PUMP_GAIN, SHOWER_EQ, SUPPLEMENT_WORKS, TOWELLESS_EQ, WARMUP_EQ,
782069860236atagen2026-10-10 12:44 UTC14
    },
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
const WEEK: Duration = Duration::from_secs(7 * 24 * 60 * 60);
782069860236atagen2026-10-10 12:44 UTC19
782069860236atagen2026-10-10 12:44 UTC20
impl Run {
782069860236atagen2026-10-10 12:44 UTC21
    pub(super) fn warm_up(&mut self) -> Result<Outcome, Problem> {
4b1f5f88827datagen2026-10-13 10:18 UTC22
        if self.here.towel_warmed && !self.here.kitchen.kettle_boiled {
a6caa2f99786atagen2026-10-12 10:38 UTC23
            return Err(Problem::new(
a6caa2f99786atagen2026-10-12 10:38 UTC24
                StatusCode::CONFLICT,
a6caa2f99786atagen2026-10-12 10:38 UTC25
                "towel-cold",
4b1f5f88827datagen2026-10-13 10:18 UTC26
                "The towel has gone cold. It warms once per floor, unless someone boils the kettle.",
a6caa2f99786atagen2026-10-12 10:38 UTC27
            ));
a6caa2f99786atagen2026-10-12 10:38 UTC28
        }
3f9091557ec3atagen2026-10-11 11:43 UTC29
        if self.floor == Floor::Hotel
3f9091557ec3atagen2026-10-11 11:43 UTC30
            && self.chance(
3f9091557ec3atagen2026-10-11 11:43 UTC31
                self.decision(
3f9091557ec3atagen2026-10-11 11:43 UTC32
                    "hotel-stress",
3f9091557ec3atagen2026-10-11 11:43 UTC33
                    "Has the hotel's plumbing given up for the week?",
3f9091557ec3atagen2026-10-11 11:43 UTC34
                ),
3f9091557ec3atagen2026-10-11 11:43 UTC35
                HOTEL_STRESS,
3f9091557ec3atagen2026-10-11 11:43 UTC36
            )
3f9091557ec3atagen2026-10-11 11:43 UTC37
        {
782069860236atagen2026-10-10 12:44 UTC38
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC39
                StatusCode::SERVICE_UNAVAILABLE,
782069860236atagen2026-10-10 12:44 UTC40
                "service-unavailable",
782069860236atagen2026-10-10 12:44 UTC41
                "Service is unavailable. The server estimates next week. The server is being pessimistic.",
782069860236atagen2026-10-10 12:44 UTC42
            )
782069860236atagen2026-10-10 12:44 UTC43
            .header(
782069860236atagen2026-10-10 12:44 UTC44
                header::RETRY_AFTER,
782069860236atagen2026-10-10 12:44 UTC45
                httpdate::fmt_http_date(SystemTime::now() + WEEK),
782069860236atagen2026-10-10 12:44 UTC46
            ));
782069860236atagen2026-10-10 12:44 UTC47
        }
782069860236atagen2026-10-10 12:44 UTC48
        let (boost, detail) = if self.floor == Floor::Bathroom {
782069860236atagen2026-10-10 12:44 UTC49
            (
782069860236atagen2026-10-10 12:44 UTC50
                SHOWER_EQ,
7d3fb63208bfatagen2026-10-11 04:38 UTC51
                "Warmed up. The shower is hot. Your brother's will not be.",
782069860236atagen2026-10-10 12:44 UTC52
            )
782069860236atagen2026-10-10 12:44 UTC53
        } else if self.here.towel_stolen {
782069860236atagen2026-10-10 12:44 UTC54
            (TOWELLESS_EQ, "Warmed up, poorly. The cat has the towel.")
782069860236atagen2026-10-10 12:44 UTC55
        } else {
782069860236atagen2026-10-10 12:44 UTC56
            (WARMUP_EQ, "Warmed up. The towel has cooled.")
782069860236atagen2026-10-10 12:44 UTC57
        };
782069860236atagen2026-10-10 12:44 UTC58
        self.touch();
e807266c323batagen2026-10-12 09:46 UTC59
        self.step_away();
4b1f5f88827datagen2026-10-13 10:18 UTC60
        if self.here.towel_warmed {
8b8e83329a1datagen2026-10-12 12:01 UTC61
            self.here.kitchen.kettle_boiled = false;
a6caa2f99786atagen2026-10-12 10:38 UTC62
        }
4b1f5f88827datagen2026-10-13 10:18 UTC63
        self.here.towel_warmed = true;
8b8e83329a1datagen2026-10-12 12:01 UTC64
        self.body.eq = (self.body.eq + boost).min(100);
782069860236atagen2026-10-10 12:44 UTC65
        Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail })))
782069860236atagen2026-10-10 12:44 UTC66
    }
782069860236atagen2026-10-10 12:44 UTC67
782069860236atagen2026-10-10 12:44 UTC68
    pub(super) fn ice(&mut self) -> Result<Outcome, Problem> {
8b8e83329a1datagen2026-10-12 12:01 UTC69
        if self.items.stores.ice_packs == 0 {
782069860236atagen2026-10-10 12:44 UTC70
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC71
                StatusCode::FAILED_DEPENDENCY,
782069860236atagen2026-10-10 12:44 UTC72
                "no-ice",
782069860236atagen2026-10-10 12:44 UTC73
                "The ice technique depends on an ice pack. There are none.",
782069860236atagen2026-10-10 12:44 UTC74
            ));
782069860236atagen2026-10-10 12:44 UTC75
        }
782069860236atagen2026-10-10 12:44 UTC76
        self.touch();
e807266c323batagen2026-10-12 09:46 UTC77
        self.step_away();
8b8e83329a1datagen2026-10-12 12:01 UTC78
        self.items.stores.ice_packs -= 1;
e2727527c151atagen2026-10-13 12:38 UTC79
        let treated = std::mem::take(&mut self.body.conditions.friction_burn);
e2727527c151atagen2026-10-13 12:38 UTC80
        let detail = match (self.body.conditions.priapism_turns > 0, treated) {
e2727527c151atagen2026-10-13 12:38 UTC81
            (true, true) => {
e2727527c151atagen2026-10-13 12:38 UTC82
                "The friction burn is treated. EQ has not moved. The condition persists. The ice pack is gone."
e2727527c151atagen2026-10-13 12:38 UTC83
            }
e2727527c151atagen2026-10-13 12:38 UTC84
            (true, false) => "The ice has no effect. The condition persists. The ice pack is gone.",
e2727527c151atagen2026-10-13 12:38 UTC85
            (false, true) => {
e2727527c151atagen2026-10-13 12:38 UTC86
                "The friction burn is treated. EQ has been reduced. The ice pack is gone."
e2727527c151atagen2026-10-13 12:38 UTC87
            }
e2727527c151atagen2026-10-13 12:38 UTC88
            (false, false) => "EQ has been reduced. The ice pack is gone.",
5dfebcc0ac43atagen2026-10-11 05:28 UTC89
        };
e2727527c151atagen2026-10-13 12:38 UTC90
        if self.body.conditions.priapism_turns == 0 {
e2727527c151atagen2026-10-13 12:38 UTC91
            self.body.eq = (self.body.eq - ICE_EQ).max(0);
e2727527c151atagen2026-10-13 12:38 UTC92
        }
5dfebcc0ac43atagen2026-10-11 05:28 UTC93
        Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail })))
782069860236atagen2026-10-10 12:44 UTC94
    }
782069860236atagen2026-10-10 12:44 UTC95
0a0bfee8329eatagen2026-10-11 05:11 UTC96
    pub(super) fn energy(&mut self) -> Result<Outcome, Problem> {
8b8e83329a1datagen2026-10-12 12:01 UTC97
        if self.items.stores.energy_drinks == 0 {
0a0bfee8329eatagen2026-10-11 05:11 UTC98
            return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC99
                StatusCode::FAILED_DEPENDENCY,
0a0bfee8329eatagen2026-10-11 05:11 UTC100
                "no-energy-drink",
0a0bfee8329eatagen2026-10-11 05:11 UTC101
                "The energy technique depends on an energy drink.",
0a0bfee8329eatagen2026-10-11 05:11 UTC102
            ));
0a0bfee8329eatagen2026-10-11 05:11 UTC103
        }
8b8e83329a1datagen2026-10-12 12:01 UTC104
        self.items.stores.energy_drinks -= 1;
e807266c323batagen2026-10-12 09:46 UTC105
        self.step_away();
a11497af08aaatagen2026-10-13 12:04 UTC106
        self.body.exhausted = false;
8b8e83329a1datagen2026-10-12 12:01 UTC107
        self.body.stamina = (self.body.stamina + ENERGY_STAMINA).min(self.max_stamina());
0a0bfee8329eatagen2026-10-11 05:11 UTC108
        self.touch();
0a0bfee8329eatagen2026-10-11 05:11 UTC109
        Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC110
            StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC111
            json!({ "detail": "The energy arrives before the regret. Stamina +3." }),
0a0bfee8329eatagen2026-10-11 05:11 UTC112
        ))
0a0bfee8329eatagen2026-10-11 05:11 UTC113
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC114
782069860236atagen2026-10-10 12:44 UTC115
    pub(super) fn supplement(&mut self) -> Result<Outcome, Problem> {
8b8e83329a1datagen2026-10-12 12:01 UTC116
        if self.items.stores.supplements == 0 {
782069860236atagen2026-10-10 12:44 UTC117
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC118
                StatusCode::FAILED_DEPENDENCY,
782069860236atagen2026-10-10 12:44 UTC119
                "no-supplement",
782069860236atagen2026-10-10 12:44 UTC120
                "The supplement technique depends on a supplement.",
782069860236atagen2026-10-10 12:44 UTC121
            ));
782069860236atagen2026-10-10 12:44 UTC122
        }
8b8e83329a1datagen2026-10-12 12:01 UTC123
        self.items.stores.supplements -= 1;
e807266c323batagen2026-10-12 09:46 UTC124
        self.step_away();
782069860236atagen2026-10-10 12:44 UTC125
        self.touch();
3f9091557ec3atagen2026-10-11 11:43 UTC126
        if self.chance(
3f9091557ec3atagen2026-10-11 11:43 UTC127
            self.decision(
3f9091557ec3atagen2026-10-11 11:43 UTC128
                "supplement",
3f9091557ec3atagen2026-10-11 11:43 UTC129
                "Does the supplement from Pump-Bro actually work?",
3f9091557ec3atagen2026-10-11 11:43 UTC130
            ),
3f9091557ec3atagen2026-10-11 11:43 UTC131
            SUPPLEMENT_WORKS,
3f9091557ec3atagen2026-10-11 11:43 UTC132
        ) {
8b8e83329a1datagen2026-10-12 12:01 UTC133
            self.body.stamina = self.max_stamina();
8b8e83329a1datagen2026-10-12 12:01 UTC134
            self.body.conditions.priapism_turns = PRIAPISM_TURNS;
8b8e83329a1datagen2026-10-12 12:01 UTC135
            self.body.eq = self.body.eq.max(PRIAPISM_EQ);
782069860236atagen2026-10-10 12:44 UTC136
            return Ok(Outcome::json(
782069860236atagen2026-10-10 12:44 UTC137
                StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC138
                json!({ "detail": "The upstream returned a valid response. It is still returning it. Stamina is fully restored." }),
782069860236atagen2026-10-10 12:44 UTC139
            ));
782069860236atagen2026-10-10 12:44 UTC140
        }
782069860236atagen2026-10-10 12:44 UTC141
        Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC142
            StatusCode::BAD_GATEWAY,
782069860236atagen2026-10-10 12:44 UTC143
            "bad-gateway",
782069860236atagen2026-10-10 12:44 UTC144
            "The upstream returned an invalid response.",
782069860236atagen2026-10-10 12:44 UTC145
        )
782069860236atagen2026-10-10 12:44 UTC146
        .with("upstream", "Pump-Bro"))
782069860236atagen2026-10-10 12:44 UTC147
    }
782069860236atagen2026-10-10 12:44 UTC148
a6caa2f99786atagen2026-10-12 10:38 UTC149
    pub(super) fn edge(&mut self) -> Result<Outcome, Problem> {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC150
        self.chafe(None, 5, false);
3ac624339c98atagen2026-10-13 12:21 UTC151
        self.commit(5, None, 1)?;
e807266c323batagen2026-10-12 09:46 UTC152
        if let ControlFlow::Break(interrupted) = self.hazards(5)? {
e807266c323batagen2026-10-12 09:46 UTC153
            return Ok(interrupted);
e807266c323batagen2026-10-12 09:46 UTC154
        }
782069860236atagen2026-10-10 12:44 UTC155
        self.here.edges += 1;
8b8e83329a1datagen2026-10-12 12:01 UTC156
        self.body.eq = (self.body.eq + EDGE_EQ).min(100);
8b8e83329a1datagen2026-10-12 12:01 UTC157
        self.body.girth += EDGE_GIRTH;
782069860236atagen2026-10-10 12:44 UTC158
        if self.here.edges >= EDGE_LOOP {
782069860236atagen2026-10-10 12:44 UTC159
            self.here.edges = 0;
a6caa2f99786atagen2026-10-12 10:38 UTC160
            self.exhaust();
782069860236atagen2026-10-10 12:44 UTC161
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC162
                StatusCode::LOOP_DETECTED,
782069860236atagen2026-10-10 12:44 UTC163
                "edging-loop",
782069860236atagen2026-10-10 12:44 UTC164
                "Edging has looped. The server detected an infinite loop and terminated it.",
782069860236atagen2026-10-10 12:44 UTC165
            ));
782069860236atagen2026-10-10 12:44 UTC166
        }
782069860236atagen2026-10-10 12:44 UTC167
        Ok(Outcome::json(
782069860236atagen2026-10-10 12:44 UTC168
            StatusCode::OK,
782069860236atagen2026-10-10 12:44 UTC169
            json!({ "detail": "Edged. EQ is rising. Nothing else is.", "edges": self.here.edges }),
782069860236atagen2026-10-10 12:44 UTC170
        ))
782069860236atagen2026-10-10 12:44 UTC171
    }
782069860236atagen2026-10-10 12:44 UTC172
a6caa2f99786atagen2026-10-12 10:38 UTC173
    pub(super) fn pump(&mut self) -> Result<Outcome, Problem> {
3ac624339c98atagen2026-10-13 12:21 UTC174
        self.commit(4, None, 1)?;
782069860236atagen2026-10-10 12:44 UTC175
        if let ControlFlow::Break(interrupted) = self.hazards(4)? {
782069860236atagen2026-10-10 12:44 UTC176
            return Ok(interrupted);
782069860236atagen2026-10-10 12:44 UTC177
        }
782069860236atagen2026-10-10 12:44 UTC178
        self.here.temp_length += PUMP_GAIN;
782069860236atagen2026-10-10 12:44 UTC179
        Ok(Outcome::json(
782069860236atagen2026-10-10 12:44 UTC180
            StatusCode::TEMPORARY_REDIRECT,
782069860236atagen2026-10-10 12:44 UTC181
            json!({ "detail": "Gains applied. They are temporary.", "length": PUMP_GAIN }),
782069860236atagen2026-10-10 12:44 UTC182
        )
782069860236atagen2026-10-10 12:44 UTC183
        .header(header::LOCATION, self.path("/length")))
782069860236atagen2026-10-10 12:44 UTC184
    }
782069860236atagen2026-10-10 12:44 UTC185
}