atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
8ab6a1382c4c997e5e0544c2af62018ca376c78b
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::{
aef6f6498ea2atagen2026-10-13 11:23 UTC5
    Cause, Floor, Item, Notice, Outcome, Run, Story,
0a0bfee8329eatagen2026-10-11 05:11 UTC6
    balance::{
f16ab17c8ff6atagen2026-10-13 11:14 UTC7
        CAUGHT_MELONED, COCONUT_ODDS, ENERGY_ODDS, KETTLE_ODDS, KITCHEN_ICE_ODDS, KITCHEN_TOLD,
f16ab17c8ff6atagen2026-10-13 11:14 UTC8
        MELON_SHAME, RAID_NOISE, RUPTURE_LINE, 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
0a0bfee8329eatagen2026-10-11 05:11 UTC13
impl Run {
c4d70621d377atagen2026-10-12 11:56 UTC14
    /// The household keeps count, so every raid makes noise and the noise makes the next telling likelier.
0a0bfee8329eatagen2026-10-11 05:11 UTC15
    pub fn raid_kitchen(&mut self) -> Result<Outcome, Problem> {
0a0bfee8329eatagen2026-10-11 05:11 UTC16
        if !self.floor.has_kitchen() {
0a0bfee8329eatagen2026-10-11 05:11 UTC17
            return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC18
                StatusCode::NOT_FOUND,
0a0bfee8329eatagen2026-10-11 05:11 UTC19
                "no-kitchen",
0a0bfee8329eatagen2026-10-11 05:11 UTC20
                "There is no kitchen on this floor.",
0a0bfee8329eatagen2026-10-11 05:11 UTC21
            ));
0a0bfee8329eatagen2026-10-11 05:11 UTC22
        }
f16ab17c8ff6atagen2026-10-13 11:14 UTC23
        if self.here.kitchen.closed {
f16ab17c8ff6atagen2026-10-13 11:14 UTC24
            return Err(self.off_limits());
0a0bfee8329eatagen2026-10-11 05:11 UTC25
        }
8b8e83329a1datagen2026-10-12 12:01 UTC26
        if self.online.story == Story::Pending {
0a0bfee8329eatagen2026-10-11 05:11 UTC27
            return Ok(self.branch_offer());
0a0bfee8329eatagen2026-10-11 05:11 UTC28
        }
3ac624339c98atagen2026-10-13 12:21 UTC29
        self.begin_turn()?;
3f9091557ec3atagen2026-10-11 11:43 UTC30
        if self.chance(
3f9091557ec3atagen2026-10-11 11:43 UTC31
            self.decision("rupture", "Does this raid of the fridge rupture something?"),
3f9091557ec3atagen2026-10-11 11:43 UTC32
            self.rupture_odds(),
3f9091557ec3atagen2026-10-11 11:43 UTC33
        ) {
b85a0d4536c3atagen2026-10-11 08:50 UTC34
            return Err(self.rupture());
b85a0d4536c3atagen2026-10-11 08:50 UTC35
        }
c4d70621d377atagen2026-10-12 11:56 UTC36
        self.make_noise(RAID_NOISE);
a6caa2f99786atagen2026-10-12 10:38 UTC37
        // One draw of 120 keeps the marginals at the documented 1 in 3, 1 in 5, 1 in 8 and 1 in 6.
3f9091557ec3atagen2026-10-11 11:43 UTC38
        let loot = self.oracle.pick(
3f9091557ec3atagen2026-10-11 11:43 UTC39
            self.decision("loot", "What does the fridge give up on this raid?"),
3f9091557ec3atagen2026-10-11 11:43 UTC40
            &[
3f9091557ec3atagen2026-10-11 11:43 UTC41
                120 / COCONUT_ODDS,
3f9091557ec3atagen2026-10-11 11:43 UTC42
                120 / ENERGY_ODDS,
3f9091557ec3atagen2026-10-11 11:43 UTC43
                120 / KITCHEN_ICE_ODDS,
a6caa2f99786atagen2026-10-12 10:38 UTC44
                120 / KETTLE_ODDS,
a6caa2f99786atagen2026-10-12 10:38 UTC45
                120 - (120 / COCONUT_ODDS
a6caa2f99786atagen2026-10-12 10:38 UTC46
                    + 120 / ENERGY_ODDS
a6caa2f99786atagen2026-10-12 10:38 UTC47
                    + 120 / KITCHEN_ICE_ODDS
a6caa2f99786atagen2026-10-12 10:38 UTC48
                    + 120 / KETTLE_ODDS),
3f9091557ec3atagen2026-10-11 11:43 UTC49
            ],
3f9091557ec3atagen2026-10-11 11:43 UTC50
            &[
3f9091557ec3atagen2026-10-11 11:43 UTC51
                "A jar of coconut oil, half of it already gone.",
3f9091557ec3atagen2026-10-11 11:43 UTC52
                "An energy drink, cold and faintly alarming.",
3f9091557ec3atagen2026-10-11 11:43 UTC53
                "An ice pack, buried under the peas.",
a6caa2f99786atagen2026-10-12 10:38 UTC54
                "The kettle has just boiled.",
3f9091557ec3atagen2026-10-11 11:43 UTC55
                "Nothing. The fridge is ordinary.",
3f9091557ec3atagen2026-10-11 11:43 UTC56
            ],
3f9091557ec3atagen2026-10-11 11:43 UTC57
        );
8b8e83329a1datagen2026-10-12 12:01 UTC58
        let melon = self.body.eq > RUPTURE_LINE
3f9091557ec3atagen2026-10-11 11:43 UTC59
            && self.oracle.ratio(
3f9091557ec3atagen2026-10-11 11:43 UTC60
                self.decision(
3f9091557ec3atagen2026-10-11 11:43 UTC61
                    "watermelon",
3f9091557ec3atagen2026-10-11 11:43 UTC62
                    "Is there a watermelon in the fridge, exactly the right shape?",
3f9091557ec3atagen2026-10-11 11:43 UTC63
                ),
3f9091557ec3atagen2026-10-11 11:43 UTC64
                WATERMELON.0,
3f9091557ec3atagen2026-10-11 11:43 UTC65
                WATERMELON.1,
c4d70621d377atagen2026-10-12 11:56 UTC66
            );
8b8e83329a1datagen2026-10-12 12:01 UTC67
        self.settle();
c4d70621d377atagen2026-10-12 11:56 UTC68
        if self.oracle.ratio(
3f9091557ec3atagen2026-10-11 11:43 UTC69
            self.decision(
3f9091557ec3atagen2026-10-11 11:43 UTC70
                "kitchen-told",
3f9091557ec3atagen2026-10-11 11:43 UTC71
                "Has the household had enough of the raiding?",
3f9091557ec3atagen2026-10-11 11:43 UTC72
            ),
8b8e83329a1datagen2026-10-12 12:01 UTC73
            u32::from(self.standing.suspicion) + 1,
3f9091557ec3atagen2026-10-11 11:43 UTC74
            KITCHEN_TOLD,
3f9091557ec3atagen2026-10-11 11:43 UTC75
        ) {
f16ab17c8ff6atagen2026-10-13 11:14 UTC76
            self.here.kitchen.closed = true;
f16ab17c8ff6atagen2026-10-13 11:14 UTC77
            self.end_kitchen_visit();
aef6f6498ea2atagen2026-10-13 11:23 UTC78
            self.notices.push(if self.floor == Floor::Nans {
aef6f6498ea2atagen2026-10-13 11:23 UTC79
                Notice::StopRaidingTheFridge
aef6f6498ea2atagen2026-10-13 11:23 UTC80
            } else {
aef6f6498ea2atagen2026-10-13 11:23 UTC81
                Notice::StopRaidingTheKitchen
aef6f6498ea2atagen2026-10-13 11:23 UTC82
            });
f16ab17c8ff6atagen2026-10-13 11:14 UTC83
            return Err(self.off_limits());
0a0bfee8329eatagen2026-10-11 05:11 UTC84
        }
c4d70621d377atagen2026-10-12 11:56 UTC85
        if melon {
8b8e83329a1datagen2026-10-12 12:01 UTC86
            self.here.kitchen.melon_offered = true;
c4d70621d377atagen2026-10-12 11:56 UTC87
            return Ok(self.melon_offer());
c4d70621d377atagen2026-10-12 11:56 UTC88
        }
c4d70621d377atagen2026-10-12 11:56 UTC89
        Ok(self.unpack(loot))
0a0bfee8329eatagen2026-10-11 05:11 UTC90
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC91
f16ab17c8ff6atagen2026-10-13 11:14 UTC92
    fn off_limits(&self) -> Problem {
aef6f6498ea2atagen2026-10-13 11:23 UTC93
        Problem::new(
aef6f6498ea2atagen2026-10-13 11:23 UTC94
            StatusCode::GONE,
aef6f6498ea2atagen2026-10-13 11:23 UTC95
            "kitchen-off-limits",
aef6f6498ea2atagen2026-10-13 11:23 UTC96
            "The kitchen is off limits.",
aef6f6498ea2atagen2026-10-13 11:23 UTC97
        )
f16ab17c8ff6atagen2026-10-13 11:14 UTC98
    }
f16ab17c8ff6atagen2026-10-13 11:14 UTC99
c4d70621d377atagen2026-10-12 11:56 UTC100
    fn unpack(&mut self, loot: usize) -> Outcome {
c4d70621d377atagen2026-10-12 11:56 UTC101
        let (item, detail) = match loot {
c4d70621d377atagen2026-10-12 11:56 UTC102
            0 => (
c4d70621d377atagen2026-10-12 11:56 UTC103
                Item::CoconutOil,
c4d70621d377atagen2026-10-12 11:56 UTC104
                "A jar of coconut oil, half of it already gone.",
c4d70621d377atagen2026-10-12 11:56 UTC105
            ),
c4d70621d377atagen2026-10-12 11:56 UTC106
            1 => (
c4d70621d377atagen2026-10-12 11:56 UTC107
                Item::EnergyDrink,
c4d70621d377atagen2026-10-12 11:56 UTC108
                "An energy drink, cold and faintly alarming.",
c4d70621d377atagen2026-10-12 11:56 UTC109
            ),
c4d70621d377atagen2026-10-12 11:56 UTC110
            2 => (Item::IcePack, "An ice pack, buried under the peas."),
c4d70621d377atagen2026-10-12 11:56 UTC111
            3 => {
8b8e83329a1datagen2026-10-12 12:01 UTC112
                self.here.kitchen.kettle_boiled = true;
c4d70621d377atagen2026-10-12 11:56 UTC113
                return Outcome::json(
c4d70621d377atagen2026-10-12 11:56 UTC114
                    StatusCode::OK,
c4d70621d377atagen2026-10-12 11:56 UTC115
                    json!({ "detail": "The kettle has just boiled. The towel can be warmed once more." }),
c4d70621d377atagen2026-10-12 11:56 UTC116
                );
c4d70621d377atagen2026-10-12 11:56 UTC117
            }
c4d70621d377atagen2026-10-12 11:56 UTC118
            _ => return Outcome::empty(StatusCode::NO_CONTENT),
c4d70621d377atagen2026-10-12 11:56 UTC119
        };
c4d70621d377atagen2026-10-12 11:56 UTC120
        self.items.grant(item);
c4d70621d377atagen2026-10-12 11:56 UTC121
        Outcome::json(StatusCode::OK, json!({ "detail": detail }))
c4d70621d377atagen2026-10-12 11:56 UTC122
    }
c4d70621d377atagen2026-10-12 11:56 UTC123
c4d70621d377atagen2026-10-12 11:56 UTC124
    fn melon_offer(&self) -> Outcome {
0a0bfee8329eatagen2026-10-11 05:11 UTC125
        let commit = self.path("/kitchen/watermelon");
c4d70621d377atagen2026-10-12 11:56 UTC126
        Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC127
            StatusCode::MULTIPLE_CHOICES,
0a0bfee8329eatagen2026-10-11 05:11 UTC128
            json!({
0a0bfee8329eatagen2026-10-11 05:11 UTC129
                "detail": "The fridge holds a watermelon. It is exactly the right shape.",
0a0bfee8329eatagen2026-10-11 05:11 UTC130
                "choices": [
0a0bfee8329eatagen2026-10-11 05:11 UTC131
                    { "href": commit, "method": "POST", "title": "Commit to the watermelon" },
0a0bfee8329eatagen2026-10-11 05:11 UTC132
                    { "href": self.path(""), "method": "GET", "title": "Close the fridge" },
0a0bfee8329eatagen2026-10-11 05:11 UTC133
                ],
0a0bfee8329eatagen2026-10-11 05:11 UTC134
            }),
0a0bfee8329eatagen2026-10-11 05:11 UTC135
        )
0a0bfee8329eatagen2026-10-11 05:11 UTC136
        .header(
0a0bfee8329eatagen2026-10-11 05:11 UTC137
            header::LINK,
0a0bfee8329eatagen2026-10-11 05:11 UTC138
            format!(
0a0bfee8329eatagen2026-10-11 05:11 UTC139
                "<{commit}>; rel=\"alternate\"; title=\"Commit to the watermelon\"; allow=\"POST\""
0a0bfee8329eatagen2026-10-11 05:11 UTC140
            ),
c4d70621d377atagen2026-10-12 11:56 UTC141
        )
0a0bfee8329eatagen2026-10-11 05:11 UTC142
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC143
c4d70621d377atagen2026-10-12 11:56 UTC144
    /// A bail-out that costs no turn. The more the household has already heard, the likelier Mum is in the doorway.
0a0bfee8329eatagen2026-10-11 05:11 UTC145
    pub fn commit_melon(&mut self) -> Result<Outcome, Problem> {
8b8e83329a1datagen2026-10-12 12:01 UTC146
        if !self.here.kitchen.melon_offered {
0a0bfee8329eatagen2026-10-11 05:11 UTC147
            return Err(Problem::new(
0a0bfee8329eatagen2026-10-11 05:11 UTC148
                StatusCode::CONFLICT,
0a0bfee8329eatagen2026-10-11 05:11 UTC149
                "no-melon-on-offer",
0a0bfee8329eatagen2026-10-11 05:11 UTC150
                "There is no watermelon on offer.",
0a0bfee8329eatagen2026-10-11 05:11 UTC151
            ));
0a0bfee8329eatagen2026-10-11 05:11 UTC152
        }
8b8e83329a1datagen2026-10-12 12:01 UTC153
        self.here.kitchen.melon_offered = false;
14de3e1e8dc2atagen2026-10-11 06:19 UTC154
        self.reset_eq(0);
8b8e83329a1datagen2026-10-12 12:01 UTC155
        self.standing.shame += MELON_SHAME;
0a0bfee8329eatagen2026-10-11 05:11 UTC156
        self.touch();
c4d70621d377atagen2026-10-12 11:56 UTC157
        if self.oracle.ratio(
3f9091557ec3atagen2026-10-11 11:43 UTC158
            self.decision(
3f9091557ec3atagen2026-10-11 11:43 UTC159
                "caught-meloned",
3f9091557ec3atagen2026-10-11 11:43 UTC160
                "Is Mum standing in the doorway as you look up?",
3f9091557ec3atagen2026-10-11 11:43 UTC161
            ),
8b8e83329a1datagen2026-10-12 12:01 UTC162
            u32::from(self.standing.suspicion) + 2,
3f9091557ec3atagen2026-10-11 11:43 UTC163
            CAUGHT_MELONED,
3f9091557ec3atagen2026-10-11 11:43 UTC164
        ) {
0a0bfee8329eatagen2026-10-11 05:11 UTC165
            return Err(self.end(Cause::Meloned));
0a0bfee8329eatagen2026-10-11 05:11 UTC166
        }
0a0bfee8329eatagen2026-10-11 05:11 UTC167
        Ok(Outcome::json(
0a0bfee8329eatagen2026-10-11 05:11 UTC168
            StatusCode::OK,
0a0bfee8329eatagen2026-10-11 05:11 UTC169
            json!({
0a0bfee8329eatagen2026-10-11 05:11 UTC170
                "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 UTC171
            }),
0a0bfee8329eatagen2026-10-11 05:11 UTC172
        ))
0a0bfee8329eatagen2026-10-11 05:11 UTC173
    }
0a0bfee8329eatagen2026-10-11 05:11 UTC174
}