atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
727d064fae3881054441752bc0a2afb71e1e63dd
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/gauges.rsRust173 lines5.1 KB
1
use std::time::Instant;
2
3
use serde_json::{Value, json};
4
5
use super::{
6
    Elder, Excuse, Floor, Grip, Item, Observer, Run, Story,
7
    balance::{DANGER_EQ, KITCHEN_TELLINGS},
8
};
9
10
pub enum Shown {
11
    Measured(f64),
12
    SelfReported(f64),
13
}
14
15
pub struct Gauges {
16
    pub(crate) length: Shown,
17
    pub(crate) girth: f64,
18
    pub(crate) foreskin: Option<f64>,
19
    pub(crate) friction: i32,
20
    pub(crate) eq: i32,
21
    pub(crate) stamina: i32,
22
    pub(crate) max_stamina: i32,
23
    pub(crate) suspicion: u8,
24
    pub(crate) cash: u32,
25
    pub(crate) floor: u8,
26
    pub(crate) observed: Option<Observer>,
27
    pub(crate) mum_at_door: bool,
28
    pub(crate) warnings: Vec<&'static str>,
29
    pub(crate) ruler: bool,
30
    pub(crate) ring: bool,
31
}
32
33
pub struct Reach {
34
    pub(crate) foreskin: bool,
35
    pub(crate) drawer: bool,
36
    pub(crate) kitchen: bool,
37
    pub(crate) thread: bool,
38
    pub(crate) shop: bool,
39
    pub(crate) portal: bool,
40
    pub(crate) elder: bool,
41
    pub(crate) footage: bool,
42
    pub(crate) diagnosis: bool,
43
    pub(crate) branch_pending: bool,
44
    pub(crate) shopping: bool,
45
}
46
47
impl Run {
48
    /// Drains the notices, so call it once per response.
49
    pub fn gauges(&mut self) -> Gauges {
50
        let conditions = &self.body.conditions;
51
        let mut warnings = Vec::new();
52
        if conditions.pulled_muscle {
53
            warnings.push("a muscle has been pulled");
54
        }
55
        if conditions.friction_burn {
56
            warnings.push("friction burn");
57
        }
58
        warnings.extend(self.notices.drain(..).map(|notice| notice.text()));
59
        if self.body.eq > DANGER_EQ && self.ended.is_none() {
60
            warnings.push("it is turning purple");
61
        }
62
        Gauges {
63
            length: if self.items.kit.ruler {
64
                Shown::Measured(self.current_length())
65
            } else {
66
                Shown::SelfReported(self.self_reported())
67
            },
68
            girth: self.body.girth,
69
            foreskin: self.body.foreskin,
70
            friction: self.body.friction,
71
            eq: self.body.eq,
72
            stamina: self.body.stamina,
73
            max_stamina: self.max_stamina(),
74
            suspicion: self.standing.suspicion,
75
            cash: self.standing.cash,
76
            floor: self.floor.number(),
77
            observed: self.observed,
78
            mum_at_door: self.household.mum.is_some(),
79
            warnings,
80
            ruler: self.items.kit.ruler,
81
            ring: self.items.kit.ring,
82
        }
83
    }
84
85
    pub fn reach(&self) -> Reach {
86
        Reach {
87
            foreskin: self.body.foreskin.is_some(),
88
            drawer: self.drawer_worth_searching(),
89
            kitchen: self.floor.has_kitchen() && self.household.kitchen_tellings < KITCHEN_TELLINGS,
90
            thread: self.thread_reachable(),
91
            shop: self.floor.shop_open(),
92
            portal: self.needs_portal(),
93
            elder: self.floor == Floor::Forum && self.online.elder != Elder::Beaten,
94
            footage: self.online.filmed_at.is_some(),
95
            diagnosis: self.floor == Floor::Clinic,
96
            branch_pending: self.online.story == Story::Pending,
97
            shopping: self.here.shopping,
98
        }
99
    }
100
101
    pub fn needs_portal(&self) -> bool {
102
        self.floor == Floor::Hotel && !self.here.wifi_accepted
103
    }
104
105
    /// Shame follows the player across runs in a signed cookie. Each response adds this run's new
106
    /// shame to whatever total the client presents, so two runs at once both count.
107
    pub fn settle_shame(&mut self, presented: Option<u32>) -> u32 {
108
        let standing = &mut self.standing;
109
        let total =
110
            presented.unwrap_or(standing.shame_total) + (standing.shame - standing.shame_reported);
111
        standing.shame_reported = standing.shame;
112
        standing.shame_total = total;
113
        total
114
    }
115
116
    pub fn trace(&mut self) {
117
        self.standing.shame += 1;
118
    }
119
120
    pub fn measured(&self) -> bool {
121
        self.items.kit.ruler
122
    }
123
124
    pub fn id(&self) -> &str {
125
        &self.ident.id
126
    }
127
128
    pub fn last_seen(&self) -> Instant {
129
        self.ident.last_seen
130
    }
131
132
    /// A request has arrived. Whoever was watching last time has looked away.
133
    pub fn seen(&mut self, now: Instant) {
134
        self.ident.last_seen = now;
135
        self.observed = None;
136
    }
137
138
    pub fn foreskin(&self) -> Option<f64> {
139
        self.body.foreskin
140
    }
141
142
    pub fn grip(&self) -> Grip {
143
        self.body.grip
144
    }
145
146
    pub fn knows_uli(&self) -> bool {
147
        self.online.uli_taught
148
    }
149
150
    pub fn holds(&self, item: Item) -> bool {
151
        self.items.has(item)
152
    }
153
154
    pub fn inventory_view(&self) -> Value {
155
        let items: Vec<Value> = self
156
            .items
157
            .owned()
158
            .into_iter()
159
            .map(|item| {
160
                json!({
161
                    "name": item.slug(),
162
                    "href": self.path(&format!("/inventory/{}", item.slug())),
163
                })
164
            })
165
            .collect();
166
        json!({
167
            "items": items,
168
            "ice_packs": self.items.stores.ice_packs,
169
            "supplements": self.items.stores.supplements,
170
            "excuses": self.excuses().into_iter().map(Excuse::phrase).collect::<Vec<_>>(),
171
        })
172
    }
173
}