atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
f16ab17c8ff6e8e90a8d6658a1fa79039509d7d7
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/kitchen.rsRust170 lines6.0 KB
1
use axum::http::{StatusCode, header};
2
use serde_json::json;
3
4
use super::{
5
    Cause, Floor, Item, Outcome, Run, Story,
6
    balance::{
7
        CAUGHT_MELONED, COCONUT_ODDS, ENERGY_ODDS, KETTLE_ODDS, KITCHEN_ICE_ODDS, KITCHEN_TOLD,
8
        MELON_SHAME, RAID_NOISE, RUPTURE_LINE, WATERMELON,
9
    },
10
};
11
use crate::problem::Problem;
12
13
impl Run {
14
    /// The household keeps count, so every raid makes noise and the noise makes the next telling likelier.
15
    pub fn raid_kitchen(&mut self) -> Result<Outcome, Problem> {
16
        if !self.floor.has_kitchen() {
17
            return Err(Problem::new(
18
                StatusCode::NOT_FOUND,
19
                "no-kitchen",
20
                "There is no kitchen on this floor.",
21
            ));
22
        }
23
        if self.here.kitchen.closed {
24
            return Err(self.off_limits());
25
        }
26
        if self.online.story == Story::Pending {
27
            return Ok(self.branch_offer());
28
        }
29
        self.begin_turn();
30
        if self.chance(
31
            self.decision("rupture", "Does this raid of the fridge rupture something?"),
32
            self.rupture_odds(),
33
        ) {
34
            return Err(self.rupture());
35
        }
36
        self.make_noise(RAID_NOISE);
37
        // One draw of 120 keeps the marginals at the documented 1 in 3, 1 in 5, 1 in 8 and 1 in 6.
38
        let loot = self.oracle.pick(
39
            self.decision("loot", "What does the fridge give up on this raid?"),
40
            &[
41
                120 / COCONUT_ODDS,
42
                120 / ENERGY_ODDS,
43
                120 / KITCHEN_ICE_ODDS,
44
                120 / KETTLE_ODDS,
45
                120 - (120 / COCONUT_ODDS
46
                    + 120 / ENERGY_ODDS
47
                    + 120 / KITCHEN_ICE_ODDS
48
                    + 120 / KETTLE_ODDS),
49
            ],
50
            &[
51
                "A jar of coconut oil, half of it already gone.",
52
                "An energy drink, cold and faintly alarming.",
53
                "An ice pack, buried under the peas.",
54
                "The kettle has just boiled.",
55
                "Nothing. The fridge is ordinary.",
56
            ],
57
        );
58
        let melon = self.body.eq > RUPTURE_LINE
59
            && self.oracle.ratio(
60
                self.decision(
61
                    "watermelon",
62
                    "Is there a watermelon in the fridge, exactly the right shape?",
63
                ),
64
                WATERMELON.0,
65
                WATERMELON.1,
66
            );
67
        self.settle();
68
        if self.oracle.ratio(
69
            self.decision(
70
                "kitchen-told",
71
                "Has the household had enough of the raiding?",
72
            ),
73
            u32::from(self.standing.suspicion) + 1,
74
            KITCHEN_TOLD,
75
        ) {
76
            self.here.kitchen.closed = true;
77
            self.end_kitchen_visit();
78
            return Err(self.off_limits());
79
        }
80
        if melon {
81
            self.here.kitchen.melon_offered = true;
82
            return Ok(self.melon_offer());
83
        }
84
        Ok(self.unpack(loot))
85
    }
86
87
    fn off_limits(&self) -> Problem {
88
        let detail = if self.floor == Floor::Nans {
89
            "Stop raiding the fridge. The kitchen is off limits."
90
        } else {
91
            "Stop raiding the kitchen. The kitchen is off limits."
92
        };
93
        Problem::new(StatusCode::GONE, "kitchen-off-limits", detail)
94
    }
95
96
    fn unpack(&mut self, loot: usize) -> Outcome {
97
        let (item, detail) = match loot {
98
            0 => (
99
                Item::CoconutOil,
100
                "A jar of coconut oil, half of it already gone.",
101
            ),
102
            1 => (
103
                Item::EnergyDrink,
104
                "An energy drink, cold and faintly alarming.",
105
            ),
106
            2 => (Item::IcePack, "An ice pack, buried under the peas."),
107
            3 => {
108
                self.here.kitchen.kettle_boiled = true;
109
                return Outcome::json(
110
                    StatusCode::OK,
111
                    json!({ "detail": "The kettle has just boiled. The towel can be warmed once more." }),
112
                );
113
            }
114
            _ => return Outcome::empty(StatusCode::NO_CONTENT),
115
        };
116
        self.items.grant(item);
117
        Outcome::json(StatusCode::OK, json!({ "detail": detail }))
118
    }
119
120
    fn melon_offer(&self) -> Outcome {
121
        let commit = self.path("/kitchen/watermelon");
122
        Outcome::json(
123
            StatusCode::MULTIPLE_CHOICES,
124
            json!({
125
                "detail": "The fridge holds a watermelon. It is exactly the right shape.",
126
                "choices": [
127
                    { "href": commit, "method": "POST", "title": "Commit to the watermelon" },
128
                    { "href": self.path(""), "method": "GET", "title": "Close the fridge" },
129
                ],
130
            }),
131
        )
132
        .header(
133
            header::LINK,
134
            format!(
135
                "<{commit}>; rel=\"alternate\"; title=\"Commit to the watermelon\"; allow=\"POST\""
136
            ),
137
        )
138
    }
139
140
    /// A bail-out that costs no turn. The more the household has already heard, the likelier Mum is in the doorway.
141
    pub fn commit_melon(&mut self) -> Result<Outcome, Problem> {
142
        if !self.here.kitchen.melon_offered {
143
            return Err(Problem::new(
144
                StatusCode::CONFLICT,
145
                "no-melon-on-offer",
146
                "There is no watermelon on offer.",
147
            ));
148
        }
149
        self.here.kitchen.melon_offered = false;
150
        self.reset_eq(0);
151
        self.standing.shame += MELON_SHAME;
152
        self.touch();
153
        if self.oracle.ratio(
154
            self.decision(
155
                "caught-meloned",
156
                "Is Mum standing in the doorway as you look up?",
157
            ),
158
            u32::from(self.standing.suspicion) + 2,
159
            CAUGHT_MELONED,
160
        ) {
161
            return Err(self.end(Cause::Meloned));
162
        }
163
        Ok(Outcome::json(
164
            StatusCode::OK,
165
            json!({
166
                "detail": "The watermelon did not survive, and neither did the mood. EQ is gone, the gains are kept, and the shame is considerable.",
167
            }),
168
        ))
169
    }
170
}