atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
e807266c323bcc1fd00f59aa160b3b94d8168beb
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/kitchen.rsRust168 lines5.9 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
                self.end_kitchen_visit();
104
                return Err(Problem::new(
105
                    StatusCode::GONE,
106
                    "kitchen-off-limits",
107
                    "The kitchen is off limits. You have been told twice.",
108
                ));
109
            }
110
            self.notices.push(if self.floor == Floor::Nans {
111
                "Stop raiding the fridge."
112
            } else {
113
                "Stop raiding the kitchen."
114
            });
115
        }
116
        Ok(outcome)
117
    }
118
119
    fn melon_offer(&self) -> Result<Outcome, Problem> {
120
        let commit = self.path("/kitchen/watermelon");
121
        Ok(Outcome::json(
122
            StatusCode::MULTIPLE_CHOICES,
123
            json!({
124
                "detail": "The fridge holds a watermelon. It is exactly the right shape.",
125
                "choices": [
126
                    { "href": commit, "method": "POST", "title": "Commit to the watermelon" },
127
                    { "href": self.path(""), "method": "GET", "title": "Close the fridge" },
128
                ],
129
            }),
130
        )
131
        .header(
132
            header::LINK,
133
            format!(
134
                "<{commit}>; rel=\"alternate\"; title=\"Commit to the watermelon\"; allow=\"POST\""
135
            ),
136
        ))
137
    }
138
139
    /// The watermelon. It empties EQ, keeps the gains, costs shame, and the household may notice.
140
    pub fn commit_melon(&mut self) -> Result<Outcome, Problem> {
141
        if !self.here.melon_offered {
142
            return Err(Problem::new(
143
                StatusCode::CONFLICT,
144
                "no-melon-on-offer",
145
                "There is no watermelon on offer.",
146
            ));
147
        }
148
        self.here.melon_offered = false;
149
        self.reset_eq(0);
150
        self.shame += MELON_SHAME;
151
        self.touch();
152
        if self.chance(
153
            self.decision(
154
                "caught-meloned",
155
                "Is Mum standing in the doorway as you look up?",
156
            ),
157
            CAUGHT_MELONED,
158
        ) {
159
            return Err(self.end(Cause::Meloned));
160
        }
161
        Ok(Outcome::json(
162
            StatusCode::OK,
163
            json!({
164
                "detail": "The watermelon did not survive, and neither did the mood. EQ is gone, the gains are kept, and the shame is considerable.",
165
            }),
166
        ))
167
    }
168
}