atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
3ac624339c982c4129228c05cb5332f4597fc3f7
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/kitchen.rsRust174 lines6.0 KB
1
use axum::http::{StatusCode, header};
2
use serde_json::json;
3
4
use super::{
5
    Cause, Floor, Item, Notice, 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
            self.notices.push(if self.floor == Floor::Nans {
79
                Notice::StopRaidingTheFridge
80
            } else {
81
                Notice::StopRaidingTheKitchen
82
            });
83
            return Err(self.off_limits());
84
        }
85
        if melon {
86
            self.here.kitchen.melon_offered = true;
87
            return Ok(self.melon_offer());
88
        }
89
        Ok(self.unpack(loot))
90
    }
91
92
    fn off_limits(&self) -> Problem {
93
        Problem::new(
94
            StatusCode::GONE,
95
            "kitchen-off-limits",
96
            "The kitchen is off limits.",
97
        )
98
    }
99
100
    fn unpack(&mut self, loot: usize) -> Outcome {
101
        let (item, detail) = match loot {
102
            0 => (
103
                Item::CoconutOil,
104
                "A jar of coconut oil, half of it already gone.",
105
            ),
106
            1 => (
107
                Item::EnergyDrink,
108
                "An energy drink, cold and faintly alarming.",
109
            ),
110
            2 => (Item::IcePack, "An ice pack, buried under the peas."),
111
            3 => {
112
                self.here.kitchen.kettle_boiled = true;
113
                return Outcome::json(
114
                    StatusCode::OK,
115
                    json!({ "detail": "The kettle has just boiled. The towel can be warmed once more." }),
116
                );
117
            }
118
            _ => return Outcome::empty(StatusCode::NO_CONTENT),
119
        };
120
        self.items.grant(item);
121
        Outcome::json(StatusCode::OK, json!({ "detail": detail }))
122
    }
123
124
    fn melon_offer(&self) -> Outcome {
125
        let commit = self.path("/kitchen/watermelon");
126
        Outcome::json(
127
            StatusCode::MULTIPLE_CHOICES,
128
            json!({
129
                "detail": "The fridge holds a watermelon. It is exactly the right shape.",
130
                "choices": [
131
                    { "href": commit, "method": "POST", "title": "Commit to the watermelon" },
132
                    { "href": self.path(""), "method": "GET", "title": "Close the fridge" },
133
                ],
134
            }),
135
        )
136
        .header(
137
            header::LINK,
138
            format!(
139
                "<{commit}>; rel=\"alternate\"; title=\"Commit to the watermelon\"; allow=\"POST\""
140
            ),
141
        )
142
    }
143
144
    /// A bail-out that costs no turn. The more the household has already heard, the likelier Mum is in the doorway.
145
    pub fn commit_melon(&mut self) -> Result<Outcome, Problem> {
146
        if !self.here.kitchen.melon_offered {
147
            return Err(Problem::new(
148
                StatusCode::CONFLICT,
149
                "no-melon-on-offer",
150
                "There is no watermelon on offer.",
151
            ));
152
        }
153
        self.here.kitchen.melon_offered = false;
154
        self.reset_eq(0);
155
        self.standing.shame += MELON_SHAME;
156
        self.touch();
157
        if self.oracle.ratio(
158
            self.decision(
159
                "caught-meloned",
160
                "Is Mum standing in the doorway as you look up?",
161
            ),
162
            u32::from(self.standing.suspicion) + 2,
163
            CAUGHT_MELONED,
164
        ) {
165
            return Err(self.end(Cause::Meloned));
166
        }
167
        Ok(Outcome::json(
168
            StatusCode::OK,
169
            json!({
170
                "detail": "The watermelon did not survive, and neither did the mood. EQ is gone, the gains are kept, and the shame is considerable.",
171
            }),
172
        ))
173
    }
174
}