atagen

jelq

public

an http based roguelike rpg about jelqing

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