atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
4b690ff501b89a8185e0f0c9ceaa61a89ce1a056
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/floors.rsRust193 lines6.8 KB
1
use axum::http::{StatusCode, header};
2
use serde_json::{Value, json};
3
4
use super::{
5
    Cause, Elder, Floor, FloorState, Item, Outcome, Run,
6
    balance::{FLOOR_ALLOWANCE, FLOOR_TURNS, STARTING_EQ, ZIP},
7
    round2,
8
};
9
use crate::problem::Problem;
10
11
impl Run {
12
    pub(super) fn next_floor(&mut self, paid: bool) {
13
        let Some(next) = self.floor.next() else {
14
            return;
15
        };
16
        self.floor = next;
17
        self.here = FloorState::default();
18
        self.warmed_up = false;
19
        self.eq = STARTING_EQ;
20
        self.suspicion = 0;
21
        self.mum = None;
22
        if paid {
23
            self.cash += FLOOR_ALLOWANCE;
24
        }
25
        self.touch();
26
    }
27
28
    pub fn advance(&mut self) -> Result<Outcome, Problem> {
29
        match self.floor {
30
            Floor::Clinic => {
31
                return Err(Problem::new(
32
                    StatusCode::CONFLICT,
33
                    "no-further-floor",
34
                    "There is no further floor. There is only the diagnosis.",
35
                ));
36
            }
37
            Floor::Forum if self.elder != Elder::Beaten => {
38
                return Err(Problem::new(
39
                    StatusCode::CONFLICT,
40
                    "elder-unaddressed",
41
                    "The Elder has not been addressed.",
42
                )
43
                .with("elder", self.path("/elder")));
44
            }
45
            floor if floor.timed() && self.here.turns < FLOOR_TURNS => {
46
                return Err(Problem::new(
47
                    StatusCode::TOO_EARLY,
48
                    "floor-not-over",
49
                    format!(
50
                        "The floor is not over. {} turns remain.",
51
                        FLOOR_TURNS - self.here.turns
52
                    ),
53
                ));
54
            }
55
            _ => {}
56
        }
57
        if self.chance(ZIP) {
58
            return Err(self.end(Cause::Zip));
59
        }
60
        let reverted = self.here.temp_length;
61
        self.next_floor(true);
62
        let mut body = json!({
63
            "detail": format!("You have arrived at {}.", self.floor.name()),
64
            "note": self.floor.note(),
65
        });
66
        if reverted > 0.0 {
67
            body["reverted"] = json!(round2(reverted));
68
        }
69
        Ok(
70
            Outcome::json(StatusCode::SEE_OTHER, body)
71
                .header(header::LOCATION, self.path("/floor")),
72
        )
73
    }
74
75
    pub fn floor_view(&self) -> Value {
76
        let mut view = json!({
77
            "name": self.floor.name(),
78
            "note": self.floor.note(),
79
            "turns": self.here.turns,
80
            "turns_required": self.floor.timed().then_some(FLOOR_TURNS),
81
            "exit": { "href": self.path("/floor"), "method": "POST" },
82
        });
83
        match self.floor {
84
            Floor::ChangingRoom => {
85
                view["others"] = json!([
86
                    { "name": "Greg", "self_reported_length": 9.5 },
87
                    { "name": "Dmitri", "self_reported_length": 8.75 },
88
                    { "name": "a man in a towel", "self_reported_length": 11.0 },
89
                ]);
90
            }
91
            Floor::Hotel => view["portal"] = json!(self.path("/portal")),
92
            Floor::Forum => {
93
                view["thread"] = json!(super::FORUM_THREAD);
94
                view["elder"] = json!(self.path("/elder"));
95
            }
96
            Floor::Clinic => view["diagnosis"] = json!(self.path("/diagnosis")),
97
            _ => {}
98
        }
99
        if self.floor.shop_open() {
100
            view["shop"] = json!(self.path("/shop"));
101
        }
102
        view
103
    }
104
105
    fn found(&self, item: Item, detail: &str) -> Outcome {
106
        Outcome::json(StatusCode::FOUND, json!({ "detail": detail })).header(
107
            header::LOCATION,
108
            self.path(&format!("/inventory/{}", item.slug())),
109
        )
110
    }
111
112
    pub fn search_drawer(&mut self) -> Result<Outcome, Problem> {
113
        self.here.drawer_searches += 1;
114
        self.touch();
115
        let first = self.here.drawer_searches == 1;
116
        match self.floor {
117
            Floor::Bedroom if first && !self.items.ruler => {
118
                self.items.grant(Item::Ruler);
119
                self.shame += 10;
120
                Ok(self.found(
121
                    Item::Ruler,
122
                    "You now know. This is the first honest measurement of the run.",
123
                ))
124
            }
125
            Floor::Bathroom if first => {
126
                self.items.grant(Item::DoorLock);
127
                Ok(self.found(Item::DoorLock, "A door lock. It works once."))
128
            }
129
            Floor::ChangingRoom => {
130
                self.shame += 3;
131
                Err(Problem::new(
132
                    StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
133
                    "not-your-locker",
134
                    "That locker belongs to Greg. The server cannot serve this request for legal reasons.",
135
                ))
136
            }
137
            Floor::Nans if first => {
138
                self.items.grant(Item::Cage);
139
                self.shame += 5;
140
                Ok(self.found(
141
                    Item::Cage,
142
                    "A small box. Inside was a cage. It is now on. The key is not in the box.",
143
                ))
144
            }
145
            Floor::Nans if self.items.cage => {
146
                self.items.grant(Item::CageKey);
147
                Ok(Outcome::json(
148
                    StatusCode::OK,
149
                    json!({ "detail": "The key was under where the box had been. The cage is off." }),
150
                ))
151
            }
152
            Floor::Hotel if first => {
153
                self.items.grant(Item::DndSign);
154
                self.items.grant(Item::IcePack);
155
                self.items.grant(Item::IcePack);
156
                Ok(self.found(
157
                    Item::DndSign,
158
                    "A Do Not Disturb sign, and two ice packs from the machine down the hall.",
159
                ))
160
            }
161
            Floor::Forum if first => {
162
                self.items.grant(Item::Spreadsheet);
163
                Ok(self.found(
164
                    Item::Spreadsheet,
165
                    "Your own spreadsheet. Every line is flat.",
166
                ))
167
            }
168
            Floor::Clinic => Err(Problem::new(
169
                StatusCode::FORBIDDEN,
170
                "doctors-drawer",
171
                "That is the doctor's drawer.",
172
            )),
173
            _ => Ok(Outcome::empty(StatusCode::NO_CONTENT)),
174
        }
175
    }
176
177
    pub fn accept_terms(&mut self) -> Result<Outcome, Problem> {
178
        if self.floor != Floor::Hotel {
179
            return Err(Problem::new(
180
                StatusCode::NOT_FOUND,
181
                "no-portal",
182
                "There is no captive portal here. There is only the regular kind of captivity.",
183
            ));
184
        }
185
        self.here.wifi_accepted = true;
186
        self.touch();
187
        Ok(Outcome::json(
188
            StatusCode::SEE_OTHER,
189
            json!({ "detail": "Connected. The hotel does not want to know." }),
190
        )
191
        .header(header::LOCATION, self.path("")))
192
    }
193
}