atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
058650ec79d6eb6841ce768ab49335ed74ae8b97
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/kitchen.rsRust179 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, RUPTURE_LINE, SUSPICION_LIMIT, WATERMELON,
9
    },
10
};
11
use crate::problem::Problem;
12
13
impl Run {
14
    /// Raid the fridge. It costs the floor turn like anything else, and the household keeps count.
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
        // One draw of 120 keeps the marginals at the documented 1 in 3, 1 in 5, 1 in 8 and 1 in 6.
45
        let loot = self.oracle.pick(
46
            self.decision("loot", "What does the fridge give up on this raid?"),
47
            &[
48
                120 / COCONUT_ODDS,
49
                120 / ENERGY_ODDS,
50
                120 / KITCHEN_ICE_ODDS,
51
                120 / KETTLE_ODDS,
52
                120 - (120 / COCONUT_ODDS
53
                    + 120 / ENERGY_ODDS
54
                    + 120 / KITCHEN_ICE_ODDS
55
                    + 120 / KETTLE_ODDS),
56
            ],
57
            &[
58
                "A jar of coconut oil, half of it already gone.",
59
                "An energy drink, cold and faintly alarming.",
60
                "An ice pack, buried under the peas.",
61
                "The kettle has just boiled.",
62
                "Nothing. The fridge is ordinary.",
63
            ],
64
        );
65
        let outcome = if self.eq > RUPTURE_LINE
66
            && self.oracle.ratio(
67
                self.decision(
68
                    "watermelon",
69
                    "Is there a watermelon in the fridge, exactly the right shape?",
70
                ),
71
                WATERMELON.0,
72
                WATERMELON.1,
73
            ) {
74
            self.here.melon_offered = true;
75
            self.melon_offer()
76
        } else if loot == 0 {
77
            self.items.grant(Item::CoconutOil);
78
            Ok(Outcome::json(
79
                StatusCode::OK,
80
                json!({ "detail": "A jar of coconut oil, half of it already gone." }),
81
            ))
82
        } else if loot == 1 {
83
            self.items.grant(Item::EnergyDrink);
84
            Ok(Outcome::json(
85
                StatusCode::OK,
86
                json!({ "detail": "An energy drink, cold and faintly alarming." }),
87
            ))
88
        } else if loot == 2 {
89
            self.items.grant(Item::IcePack);
90
            Ok(Outcome::json(
91
                StatusCode::OK,
92
                json!({ "detail": "An ice pack, buried under the peas." }),
93
            ))
94
        } else if loot == 3 {
95
            self.here.kettle_boiled = true;
96
            Ok(Outcome::json(
97
                StatusCode::OK,
98
                json!({ "detail": "The kettle has just boiled. The towel can be warmed once more." }),
99
            ))
100
        } else {
101
            Ok(Outcome::empty(StatusCode::NO_CONTENT))
102
        };
103
        self.settle(was_below);
104
        let outcome = outcome?;
105
        if self.chance(
106
            self.decision(
107
                "kitchen-told",
108
                "Has the household had enough of the raiding?",
109
            ),
110
            KITCHEN_TOLD,
111
        ) {
112
            self.kitchen_tellings += 1;
113
            if self.kitchen_tellings >= KITCHEN_TELLINGS {
114
                self.end_kitchen_visit();
115
                return Err(Problem::new(
116
                    StatusCode::GONE,
117
                    "kitchen-off-limits",
118
                    "The kitchen is off limits. You have been told twice.",
119
                ));
120
            }
121
            self.notices.push(if self.floor == Floor::Nans {
122
                "Stop raiding the fridge."
123
            } else {
124
                "Stop raiding the kitchen."
125
            });
126
        }
127
        Ok(outcome)
128
    }
129
130
    fn melon_offer(&self) -> Result<Outcome, Problem> {
131
        let commit = self.path("/kitchen/watermelon");
132
        Ok(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
    /// The watermelon. It empties EQ, keeps the gains, costs shame, and the household may notice.
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.chance(
164
            self.decision(
165
                "caught-meloned",
166
                "Is Mum standing in the doorway as you look up?",
167
            ),
168
            CAUGHT_MELONED,
169
        ) {
170
            return Err(self.end(Cause::Meloned));
171
        }
172
        Ok(Outcome::json(
173
            StatusCode::OK,
174
            json!({
175
                "detail": "The watermelon did not survive, and neither did the mood. EQ is gone, the gains are kept, and the shame is considerable.",
176
            }),
177
        ))
178
    }
179
}