atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
97c05b741d8f0a39cec7b9e24bf5491f18c819c5
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/travel.rsRust152 lines5.1 KB
1
use super::{
2
    Cause, Elder, Floor, FloorState, Notice, Outcome, Run,
3
    balance::{
4
        EQ_BAND, FLOOR_ALLOWANCE, FLOOR_TURNS, FRICTION_FLOOR, LOCKER_ROOM_BAR, LOCKER_ROOM_OTHERS,
5
        LOCKER_ROOM_SHAME, STARTING_EQ, ZIP,
6
    },
7
    measure::round2,
8
};
9
use crate::problem::Problem;
10
use axum::http::{StatusCode, header};
11
use serde_json::{Value, json};
12
13
/// How a floor was left. Only a proper exit is paid.
14
#[derive(Clone, Copy, PartialEq, Eq)]
15
pub(super) enum Exit {
16
    Proper,
17
    Premature,
18
}
19
20
impl Run {
21
    pub(super) fn next_floor(&mut self, exit: Exit) {
22
        let Some(next) = self.floor.next() else {
23
            return;
24
        };
25
        let warm = EQ_BAND.contains(&self.body.eq);
26
        self.floor = next;
27
        self.here = FloorState {
28
            warm_arrival: warm,
29
            ..FloorState::default()
30
        };
31
        self.body.warmed_up = false;
32
        self.reset_eq(STARTING_EQ);
33
        self.standing.suspicion = 0;
34
        self.shed(FRICTION_FLOOR);
35
        self.household.mum = None;
36
        if self.floor == Floor::ChangingRoom && !self.holds_own_in_the_locker_room() {
37
            self.standing.shame += LOCKER_ROOM_SHAME;
38
            self.notices.push(Notice::KevIsTheBar);
39
        }
40
        if exit == Exit::Proper {
41
            self.standing.cash += FLOOR_ALLOWANCE;
42
        }
43
        self.touch();
44
    }
45
46
    pub fn advance(&mut self) -> Result<Outcome, Problem> {
47
        match self.floor {
48
            Floor::Clinic => {
49
                return Err(Problem::new(
50
                    StatusCode::CONFLICT,
51
                    "no-further-floor",
52
                    "There is no further floor. There is only the diagnosis.",
53
                ));
54
            }
55
            Floor::Forum if self.online.elder != Elder::Beaten => {
56
                return Err(Problem::new(
57
                    StatusCode::CONFLICT,
58
                    "elder-unaddressed",
59
                    "The Elder has not been addressed.",
60
                )
61
                .with("elder", self.path("/elder")));
62
            }
63
            floor if floor.timed() && self.here.turns < FLOOR_TURNS => {
64
                return Err(Problem::new(
65
                    StatusCode::TOO_EARLY,
66
                    "floor-not-over",
67
                    format!(
68
                        "The floor is not over. {} turns remain.",
69
                        FLOOR_TURNS - self.here.turns
70
                    ),
71
                ));
72
            }
73
            _ => {}
74
        }
75
        if self.chance(
76
            self.decision(
77
                "zip",
78
                "As the floor ends and you put yourself away, does the zip catch?",
79
            ),
80
            ZIP,
81
        ) {
82
            return Err(self.end(Cause::Zip));
83
        }
84
        let left = self.floor.name();
85
        let reverted = self.here.temp_length;
86
        self.next_floor(Exit::Proper);
87
        let warm = if self.here.warm_arrival {
88
            ", and the warmth came with you"
89
        } else {
90
            ""
91
        };
92
        let mut body = json!({
93
            "detail": format!("You leave {left}. You have arrived at {}{warm}.", self.floor.name()),
94
            "note": self.floor.note(),
95
        });
96
        if reverted > 0.0 {
97
            body["reverted"] = json!(round2(reverted));
98
        }
99
        Ok(
100
            Outcome::json(StatusCode::SEE_OTHER, body)
101
                .header(header::LOCATION, self.path("/floor")),
102
        )
103
    }
104
105
    pub fn floor_view(&self) -> Value {
106
        let mut view = json!({
107
            "name": self.floor.name(),
108
            "note": self.floor.note(),
109
            "turns": self.here.turns,
110
            "turns_required": self.floor.timed().then_some(FLOOR_TURNS),
111
            "exit": { "href": self.path("/floor"), "method": "POST" },
112
        });
113
        match self.floor {
114
            Floor::ChangingRoom => {
115
                let others: Vec<Value> = LOCKER_ROOM_OTHERS
116
                    .into_iter()
117
                    .chain([("Kev", LOCKER_ROOM_BAR)])
118
                    .map(|(name, length)| json!({ "name": name, "self_reported_length": length }))
119
                    .collect();
120
                view["others"] = json!(others);
121
            }
122
            Floor::Hotel => view["portal"] = json!(self.path("/portal")),
123
            Floor::Forum => {
124
                view["thread"] = json!(super::FORUM_THREAD);
125
                view["elder"] = json!(self.path("/elder"));
126
            }
127
            Floor::Clinic => view["diagnosis"] = json!(self.path("/diagnosis")),
128
            _ => {}
129
        }
130
        if self.floor.shop_open() {
131
            view["shop"] = json!(self.path("/shop"));
132
        }
133
        view
134
    }
135
136
    pub fn accept_terms(&mut self) -> Result<Outcome, Problem> {
137
        if self.floor != Floor::Hotel {
138
            return Err(Problem::new(
139
                StatusCode::NOT_FOUND,
140
                "no-portal",
141
                "There is no captive portal here. There is only the regular kind of captivity.",
142
            ));
143
        }
144
        self.here.wifi_accepted = true;
145
        self.touch();
146
        Ok(Outcome::json(
147
            StatusCode::SEE_OTHER,
148
            json!({ "detail": "Connected. The hotel does not want to know." }),
149
        )
150
        .header(header::LOCATION, self.path("")))
151
    }
152
}