atagen

jelq

public

an http based roguelike rpg about jelqing

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