atagen

jelq

public

an http based roguelike rpg about jelqing

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