atagen

jelq

public

an http based roguelike rpg about jelqing

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

Blame: src/game/kitchen.rs

0a0bfee8329eatagen2026-10-11 05:11 UTC1
use axum::http::{StatusCode, header};
0a0bfee8329eatagen2026-10-11 05:11 UTC2
use serde_json::json;
0a0bfee8329eatagen2026-10-11 05:11 UTC3
0a0bfee8329eatagen2026-10-11 05:11 UTC4
use super::{
0a0bfee8329eatagen2026-10-11 05:11 UTC5
    Cause, Floor, Item, Outcome, Run, Story,
0a0bfee8329eatagen2026-10-11 05:11 UTC6
    balance::{
0a0bfee8329eatagen2026-10-11 05:11 UTC7
        CAUGHT_MELONED, COCONUT_ODDS, ENERGY_ODDS, KITCHEN_ICE_ODDS, KITCHEN_TELLINGS,
0a0bfee8329eatagen2026-10-11 05:11 UTC8
        KITCHEN_TOLD, MELON_SHAME, RUPTURE_LINE, SUSPICION_LIMIT, WATERMELON,
0a0bfee8329eatagen2026-10-11 05:11 UTC9
    },
0a0bfee8329eatagen2026-10-11 05:11 UTC10
};
0a0bfee8329eatagen2026-10-11 05:11 UTC11
use crate::problem::Problem;
0a0bfee8329eatagen2026-10-11 05:11 UTC12
use rand::Rng;
0a0bfee8329eatagen2026-10-11 05:11 UTC13
0a0bfee8329eatagen2026-10-11 05:11 UTC14
impl Run {
0a0bfee8329eatagen2026-10-11 05:11 UTC15
    /// Raid the fridge. It costs the floor turn like anything else, and the household keeps count.
0a0bfee8329eatagen2026-10-11 05:11 UTC16
    pub fn raid_kitchen(&mut self) -> Result<Outcome, Problem> {
0a0bfee8329eatagen2026-10-11 05:11 UTC17
        if !self.floor.has_kitchen() {
0a0bfee8329eatagen2026-10-11 05:11 UTC18
            return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC19
                StatusCode::NOT_FOUND,
0a0bfee8329eatagen2026-10-11 05:11 UTC20
                "no-kitchen",
0a0bfee8329eatagen2026-10-11 05:11 UTC21
                "There is no kitchen on this floor.",
0a0bfee8329eatagen2026-10-11 05:11 UTC22
            ));
0a0bfee8329eatagen2026-10-11 05:11 UTC23
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC24
        if self.kitchen_tellings >= KITCHEN_TELLINGS {
0a0bfee8329eatagen2026-10-11 05:11 UTC25
            return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC26
                StatusCode::GONE,
0a0bfee8329eatagen2026-10-11 05:11 UTC27
                "kitchen-off-limits",
0a0bfee8329eatagen2026-10-11 05:11 UTC28
                "The kitchen is off limits. You have been told twice.",
0a0bfee8329eatagen2026-10-11 05:11 UTC29
            ));
0a0bfee8329eatagen2026-10-11 05:11 UTC30
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC31
        if self.story == Story::Pending {
0a0bfee8329eatagen2026-10-11 05:11 UTC32
            return Ok(self.branch_offer());
0a0bfee8329eatagen2026-10-11 05:11 UTC33
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC34
        self.touch();
0a0bfee8329eatagen2026-10-11 05:11 UTC35
        self.turn += 1;
0a0bfee8329eatagen2026-10-11 05:11 UTC36
        self.here.turns += 1;
0a0bfee8329eatagen2026-10-11 05:11 UTC37
        self.realise();
0a0bfee8329eatagen2026-10-11 05:11 UTC38
        let was_below = self.suspicion < SUSPICION_LIMIT;
139164489032atagen2026-10-11 05:46 UTC39
        // One draw of 120 keeps the marginals at the documented 1 in 3, 1 in 5 and 1 in 8.
139164489032atagen2026-10-11 05:46 UTC40
        let draw = self.rng.random_range(0..120);
139164489032atagen2026-10-11 05:46 UTC41
        let oil = 120 / COCONUT_ODDS;
139164489032atagen2026-10-11 05:46 UTC42
        let energy = oil + 120 / ENERGY_ODDS;
139164489032atagen2026-10-11 05:46 UTC43
        let ice = energy + 120 / KITCHEN_ICE_ODDS;
0a0bfee8329eatagen2026-10-11 05:11 UTC44
        let outcome = if self.eq > RUPTURE_LINE && self.rng.random_ratio(WATERMELON.0, WATERMELON.1)
0a0bfee8329eatagen2026-10-11 05:11 UTC45
        {
0a0bfee8329eatagen2026-10-11 05:11 UTC46
            self.here.melon_offered = true;
0a0bfee8329eatagen2026-10-11 05:11 UTC47
            self.melon_offer()
139164489032atagen2026-10-11 05:46 UTC48
        } else if draw < oil {
0a0bfee8329eatagen2026-10-11 05:11 UTC49
            self.items.grant(Item::CoconutOil);
0a0bfee8329eatagen2026-10-11 05:11 UTC50
            Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC51
                StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC52
                json!({ "detail": "A jar of coconut oil, half of it already gone." }),
0a0bfee8329eatagen2026-10-11 05:11 UTC53
            ))
139164489032atagen2026-10-11 05:46 UTC54
        } else if draw < energy {
0a0bfee8329eatagen2026-10-11 05:11 UTC55
            self.items.grant(Item::EnergyDrink);
0a0bfee8329eatagen2026-10-11 05:11 UTC56
            Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC57
                StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC58
                json!({ "detail": "An energy drink, cold and faintly alarming." }),
0a0bfee8329eatagen2026-10-11 05:11 UTC59
            ))
139164489032atagen2026-10-11 05:46 UTC60
        } else if draw < ice {
0a0bfee8329eatagen2026-10-11 05:11 UTC61
            self.items.grant(Item::IcePack);
0a0bfee8329eatagen2026-10-11 05:11 UTC62
            Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC63
                StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC64
                json!({ "detail": "An ice pack, buried under the peas." }),
0a0bfee8329eatagen2026-10-11 05:11 UTC65
            ))
0a0bfee8329eatagen2026-10-11 05:11 UTC66
        } else {
0a0bfee8329eatagen2026-10-11 05:11 UTC67
            Ok(Outcome::empty(StatusCode::NO_CONTENT))
0a0bfee8329eatagen2026-10-11 05:11 UTC68
        };
0a0bfee8329eatagen2026-10-11 05:11 UTC69
        self.settle(was_below);
0a0bfee8329eatagen2026-10-11 05:11 UTC70
        let outcome = outcome?;
0a0bfee8329eatagen2026-10-11 05:11 UTC71
        if self.chance(KITCHEN_TOLD) {
0a0bfee8329eatagen2026-10-11 05:11 UTC72
            self.kitchen_tellings += 1;
0a0bfee8329eatagen2026-10-11 05:11 UTC73
            if self.kitchen_tellings >= KITCHEN_TELLINGS {
0a0bfee8329eatagen2026-10-11 05:11 UTC74
                return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC75
                    StatusCode::GONE,
0a0bfee8329eatagen2026-10-11 05:11 UTC76
                    "kitchen-off-limits",
0a0bfee8329eatagen2026-10-11 05:11 UTC77
                    "The kitchen is off limits. You have been told twice.",
0a0bfee8329eatagen2026-10-11 05:11 UTC78
                ));
0a0bfee8329eatagen2026-10-11 05:11 UTC79
            }
0a0bfee8329eatagen2026-10-11 05:11 UTC80
            self.notices.push(if self.floor == Floor::Nans {
0a0bfee8329eatagen2026-10-11 05:11 UTC81
                "Stop raiding the fridge."
0a0bfee8329eatagen2026-10-11 05:11 UTC82
            } else {
0a0bfee8329eatagen2026-10-11 05:11 UTC83
                "Stop raiding the kitchen."
0a0bfee8329eatagen2026-10-11 05:11 UTC84
            });
0a0bfee8329eatagen2026-10-11 05:11 UTC85
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC86
        Ok(outcome)
0a0bfee8329eatagen2026-10-11 05:11 UTC87
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC88
0a0bfee8329eatagen2026-10-11 05:11 UTC89
    fn melon_offer(&self) -> Result<Outcome, Problem> {
0a0bfee8329eatagen2026-10-11 05:11 UTC90
        let commit = self.path("/kitchen/watermelon");
0a0bfee8329eatagen2026-10-11 05:11 UTC91
        Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC92
            StatusCode::MULTIPLE_CHOICES,
0a0bfee8329eatagen2026-10-11 05:11 UTC93
            json!({
0a0bfee8329eatagen2026-10-11 05:11 UTC94
                "detail": "The fridge holds a watermelon. It is exactly the right shape.",
0a0bfee8329eatagen2026-10-11 05:11 UTC95
                "choices": [
0a0bfee8329eatagen2026-10-11 05:11 UTC96
                    { "href": commit, "method": "POST", "title": "Commit to the watermelon" },
0a0bfee8329eatagen2026-10-11 05:11 UTC97
                    { "href": self.path(""), "method": "GET", "title": "Close the fridge" },
0a0bfee8329eatagen2026-10-11 05:11 UTC98
                ],
0a0bfee8329eatagen2026-10-11 05:11 UTC99
            }),
0a0bfee8329eatagen2026-10-11 05:11 UTC100
        )
0a0bfee8329eatagen2026-10-11 05:11 UTC101
        .header(
0a0bfee8329eatagen2026-10-11 05:11 UTC102
            header::LINK,
0a0bfee8329eatagen2026-10-11 05:11 UTC103
            format!(
0a0bfee8329eatagen2026-10-11 05:11 UTC104
                "<{commit}>; rel=\"alternate\"; title=\"Commit to the watermelon\"; allow=\"POST\""
0a0bfee8329eatagen2026-10-11 05:11 UTC105
            ),
0a0bfee8329eatagen2026-10-11 05:11 UTC106
        ))
0a0bfee8329eatagen2026-10-11 05:11 UTC107
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC108
0a0bfee8329eatagen2026-10-11 05:11 UTC109
    /// The watermelon. It empties EQ, keeps the gains, costs shame, and the household may notice.
0a0bfee8329eatagen2026-10-11 05:11 UTC110
    pub fn commit_melon(&mut self) -> Result<Outcome, Problem> {
0a0bfee8329eatagen2026-10-11 05:11 UTC111
        if !self.here.melon_offered {
0a0bfee8329eatagen2026-10-11 05:11 UTC112
            return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC113
                StatusCode::CONFLICT,
0a0bfee8329eatagen2026-10-11 05:11 UTC114
                "no-melon-on-offer",
0a0bfee8329eatagen2026-10-11 05:11 UTC115
                "There is no watermelon on offer.",
0a0bfee8329eatagen2026-10-11 05:11 UTC116
            ));
0a0bfee8329eatagen2026-10-11 05:11 UTC117
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC118
        self.here.melon_offered = false;
0a0bfee8329eatagen2026-10-11 05:11 UTC119
        self.eq = 0;
0a0bfee8329eatagen2026-10-11 05:11 UTC120
        self.shame += MELON_SHAME;
0a0bfee8329eatagen2026-10-11 05:11 UTC121
        self.touch();
0a0bfee8329eatagen2026-10-11 05:11 UTC122
        if self.chance(CAUGHT_MELONED) {
0a0bfee8329eatagen2026-10-11 05:11 UTC123
            return Err(self.end(Cause::Meloned));
0a0bfee8329eatagen2026-10-11 05:11 UTC124
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC125
        Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC126
            StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC127
            json!({
0a0bfee8329eatagen2026-10-11 05:11 UTC128
                "detail": "The watermelon did not survive, and neither did the mood. EQ is gone, the gains are kept, and the shame is considerable.",
0a0bfee8329eatagen2026-10-11 05:11 UTC129
            }),
0a0bfee8329eatagen2026-10-11 05:11 UTC130
        ))
0a0bfee8329eatagen2026-10-11 05:11 UTC131
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC132
}