atagen

jelq

public

an http based roguelike rpg about jelqing

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