atagen

jelq

public

an http based roguelike rpg about jelqing

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