atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
3f9091557ec3a198324147b05397e1bf5c50247a
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/kitchen.rsRust167 lines5.8 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, 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 and 1 in 8.
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 - (120 / COCONUT_ODDS + 120 / ENERGY_ODDS + 120 / KITCHEN_ICE_ODDS),
52
            ],
53
            &[
54
                "A jar of coconut oil, half of it already gone.",
55
                "An energy drink, cold and faintly alarming.",
56
                "An ice pack, buried under the peas.",
57
                "Nothing. The fridge is ordinary.",
58
            ],
59
        );
60
        let outcome = if self.eq > RUPTURE_LINE
61
            && self.oracle.ratio(
62
                self.decision(
63
                    "watermelon",
64
                    "Is there a watermelon in the fridge, exactly the right shape?",
65
                ),
66
                WATERMELON.0,
67
                WATERMELON.1,
68
            ) {
69
            self.here.melon_offered = true;
70
            self.melon_offer()
71
        } else if loot == 0 {
72
            self.items.grant(Item::CoconutOil);
73
            Ok(Outcome::json(
74
                StatusCode::OK,
75
                json!({ "detail": "A jar of coconut oil, half of it already gone." }),
76
            ))
77
        } else if loot == 1 {
78
            self.items.grant(Item::EnergyDrink);
79
            Ok(Outcome::json(
80
                StatusCode::OK,
81
                json!({ "detail": "An energy drink, cold and faintly alarming." }),
82
            ))
83
        } else if loot == 2 {
84
            self.items.grant(Item::IcePack);
85
            Ok(Outcome::json(
86
                StatusCode::OK,
87
                json!({ "detail": "An ice pack, buried under the peas." }),
88
            ))
89
        } else {
90
            Ok(Outcome::empty(StatusCode::NO_CONTENT))
91
        };
92
        self.settle(was_below);
93
        let outcome = outcome?;
94
        if self.chance(
95
            self.decision(
96
                "kitchen-told",
97
                "Has the household had enough of the raiding?",
98
            ),
99
            KITCHEN_TOLD,
100
        ) {
101
            self.kitchen_tellings += 1;
102
            if self.kitchen_tellings >= KITCHEN_TELLINGS {
103
                return Err(Problem::new(
104
                    StatusCode::GONE,
105
                    "kitchen-off-limits",
106
                    "The kitchen is off limits. You have been told twice.",
107
                ));
108
            }
109
            self.notices.push(if self.floor == Floor::Nans {
110
                "Stop raiding the fridge."
111
            } else {
112
                "Stop raiding the kitchen."
113
            });
114
        }
115
        Ok(outcome)
116
    }
117
118
    fn melon_offer(&self) -> Result<Outcome, Problem> {
119
        let commit = self.path("/kitchen/watermelon");
120
        Ok(Outcome::json(
121
            StatusCode::MULTIPLE_CHOICES,
122
            json!({
123
                "detail": "The fridge holds a watermelon. It is exactly the right shape.",
124
                "choices": [
125
                    { "href": commit, "method": "POST", "title": "Commit to the watermelon" },
126
                    { "href": self.path(""), "method": "GET", "title": "Close the fridge" },
127
                ],
128
            }),
129
        )
130
        .header(
131
            header::LINK,
132
            format!(
133
                "<{commit}>; rel=\"alternate\"; title=\"Commit to the watermelon\"; allow=\"POST\""
134
            ),
135
        ))
136
    }
137
138
    /// The watermelon. It empties EQ, keeps the gains, costs shame, and the household may notice.
139
    pub fn commit_melon(&mut self) -> Result<Outcome, Problem> {
140
        if !self.here.melon_offered {
141
            return Err(Problem::new(
142
                StatusCode::CONFLICT,
143
                "no-melon-on-offer",
144
                "There is no watermelon on offer.",
145
            ));
146
        }
147
        self.here.melon_offered = false;
148
        self.reset_eq(0);
149
        self.shame += MELON_SHAME;
150
        self.touch();
151
        if self.chance(
152
            self.decision(
153
                "caught-meloned",
154
                "Is Mum standing in the doorway as you look up?",
155
            ),
156
            CAUGHT_MELONED,
157
        ) {
158
            return Err(self.end(Cause::Meloned));
159
        }
160
        Ok(Outcome::json(
161
            StatusCode::OK,
162
            json!({
163
                "detail": "The watermelon did not survive, and neither did the mood. EQ is gone, the gains are kept, and the shame is considerable.",
164
            }),
165
        ))
166
    }
167
}