atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
27256acf27800b63fd950b95ecc55b2a2486c1c5
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/game/gauges.rs

8b8e83329a1datagen2026-10-12 12:01 UTC1
use std::time::Instant;
8b8e83329a1datagen2026-10-12 12:01 UTC2
8b8e83329a1datagen2026-10-12 12:01 UTC3
use serde_json::{Value, json};
8b8e83329a1datagen2026-10-12 12:01 UTC4
8b8e83329a1datagen2026-10-12 12:01 UTC5
use super::{
f3a1913c1299atagen2026-10-14 11:42 UTC6
    Arousal, Arrival, Danger, Elder, Excuse, Floor, Grip, Item, Observer, Run, Story, Technique,
35d571ccadc4atagen2026-10-14 11:33 UTC7
    balance::{DANGER_EQ, FLOOR_LIMIT},
880b29a0c11eatagen2026-10-13 09:27 UTC8
    techniques::{Training, Utility},
8b8e83329a1datagen2026-10-12 12:01 UTC9
};
8b8e83329a1datagen2026-10-12 12:01 UTC10
8b8e83329a1datagen2026-10-12 12:01 UTC11
pub enum Shown {
8b8e83329a1datagen2026-10-12 12:01 UTC12
    Measured(f64),
8b8e83329a1datagen2026-10-12 12:01 UTC13
    SelfReported(f64),
8b8e83329a1datagen2026-10-12 12:01 UTC14
}
8b8e83329a1datagen2026-10-12 12:01 UTC15
8b8e83329a1datagen2026-10-12 12:01 UTC16
pub struct Gauges {
8b8e83329a1datagen2026-10-12 12:01 UTC17
    pub(crate) length: Shown,
8b8e83329a1datagen2026-10-12 12:01 UTC18
    pub(crate) girth: f64,
8b8e83329a1datagen2026-10-12 12:01 UTC19
    pub(crate) foreskin: Option<f64>,
8b8e83329a1datagen2026-10-12 12:01 UTC20
    pub(crate) friction: i32,
e028a4184d00atagen2026-10-14 10:49 UTC21
    pub(crate) arousal: Arousal,
8b8e83329a1datagen2026-10-12 12:01 UTC22
    pub(crate) stamina: i32,
8b8e83329a1datagen2026-10-12 12:01 UTC23
    pub(crate) max_stamina: i32,
8b8e83329a1datagen2026-10-12 12:01 UTC24
    pub(crate) suspicion: u8,
8b8e83329a1datagen2026-10-12 12:01 UTC25
    pub(crate) cash: u32,
8b8e83329a1datagen2026-10-12 12:01 UTC26
    pub(crate) floor: u8,
35d571ccadc4atagen2026-10-14 11:33 UTC27
    pub(crate) turn: u32,
35d571ccadc4atagen2026-10-14 11:33 UTC28
    pub(crate) floor_turns: u32,
35d571ccadc4atagen2026-10-14 11:33 UTC29
    pub(crate) floor_limit: Option<u32>,
624fe9c09c8eatagen2026-10-14 12:11 UTC30
    pub(crate) floor_over: bool,
f3a1913c1299atagen2026-10-14 11:42 UTC31
    pub(crate) dangers: Vec<(&'static str, Danger)>,
8b8e83329a1datagen2026-10-12 12:01 UTC32
    pub(crate) observed: Option<Observer>,
886159fd0fe7atagen2026-10-13 12:48 UTC33
    pub(crate) mum_challenge: Option<String>,
8b8e83329a1datagen2026-10-12 12:01 UTC34
    pub(crate) warnings: Vec<&'static str>,
fcc6cf6d5edcatagen2026-10-13 11:31 UTC35
    pub(crate) arrived: Option<Arrival>,
8b8e83329a1datagen2026-10-12 12:01 UTC36
    pub(crate) ruler: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC37
    pub(crate) ring: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC38
}
8b8e83329a1datagen2026-10-12 12:01 UTC39
8b8e83329a1datagen2026-10-12 12:01 UTC40
pub struct Reach {
8b8e83329a1datagen2026-10-12 12:01 UTC41
    pub(crate) foreskin: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC42
    pub(crate) drawer: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC43
    pub(crate) kitchen: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC44
    pub(crate) thread: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC45
    pub(crate) shop: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC46
    pub(crate) portal: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC47
    pub(crate) elder: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC48
    pub(crate) footage: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC49
    pub(crate) diagnosis: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC50
    pub(crate) branch_pending: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC51
    pub(crate) shopping: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC52
}
8b8e83329a1datagen2026-10-12 12:01 UTC53
8b8e83329a1datagen2026-10-12 12:01 UTC54
impl Run {
8b8e83329a1datagen2026-10-12 12:01 UTC55
    /// Drains the notices, so call it once per response.
8b8e83329a1datagen2026-10-12 12:01 UTC56
    pub fn gauges(&mut self) -> Gauges {
8b8e83329a1datagen2026-10-12 12:01 UTC57
        let conditions = &self.body.conditions;
8b8e83329a1datagen2026-10-12 12:01 UTC58
        let mut warnings = Vec::new();
8b8e83329a1datagen2026-10-12 12:01 UTC59
        if conditions.pulled_muscle {
8b8e83329a1datagen2026-10-12 12:01 UTC60
            warnings.push("a muscle has been pulled");
8b8e83329a1datagen2026-10-12 12:01 UTC61
        }
8b8e83329a1datagen2026-10-12 12:01 UTC62
        if conditions.friction_burn {
8b8e83329a1datagen2026-10-12 12:01 UTC63
            warnings.push("friction burn");
8b8e83329a1datagen2026-10-12 12:01 UTC64
        }
8b8e83329a1datagen2026-10-12 12:01 UTC65
        warnings.extend(self.notices.drain(..).map(|notice| notice.text()));
fcc6cf6d5edcatagen2026-10-13 11:31 UTC66
        let arrived = self.body.arrived.take();
fcc6cf6d5edcatagen2026-10-13 11:31 UTC67
        if arrived.is_some() {
fcc6cf6d5edcatagen2026-10-13 11:31 UTC68
            warnings.push("the queued gains have arrived");
fcc6cf6d5edcatagen2026-10-13 11:31 UTC69
        }
e028a4184d00atagen2026-10-14 10:49 UTC70
        if self.hotter_than(DANGER_EQ) && self.ended.is_none() {
8b8e83329a1datagen2026-10-12 12:01 UTC71
            warnings.push("it is turning purple");
8b8e83329a1datagen2026-10-12 12:01 UTC72
        }
8b8e83329a1datagen2026-10-12 12:01 UTC73
        Gauges {
8b8e83329a1datagen2026-10-12 12:01 UTC74
            length: if self.items.kit.ruler {
8b8e83329a1datagen2026-10-12 12:01 UTC75
                Shown::Measured(self.current_length())
8b8e83329a1datagen2026-10-12 12:01 UTC76
            } else {
8b8e83329a1datagen2026-10-12 12:01 UTC77
                Shown::SelfReported(self.self_reported())
8b8e83329a1datagen2026-10-12 12:01 UTC78
            },
8b8e83329a1datagen2026-10-12 12:01 UTC79
            girth: self.body.girth,
8b8e83329a1datagen2026-10-12 12:01 UTC80
            foreskin: self.body.foreskin,
8b8e83329a1datagen2026-10-12 12:01 UTC81
            friction: self.body.friction,
e028a4184d00atagen2026-10-14 10:49 UTC82
            arousal: self.arousal(),
8b8e83329a1datagen2026-10-12 12:01 UTC83
            stamina: self.body.stamina,
8b8e83329a1datagen2026-10-12 12:01 UTC84
            max_stamina: self.max_stamina(),
8b8e83329a1datagen2026-10-12 12:01 UTC85
            suspicion: self.standing.suspicion,
8b8e83329a1datagen2026-10-12 12:01 UTC86
            cash: self.standing.cash,
8b8e83329a1datagen2026-10-12 12:01 UTC87
            floor: self.floor.number(),
35d571ccadc4atagen2026-10-14 11:33 UTC88
            turn: self.turn,
35d571ccadc4atagen2026-10-14 11:33 UTC89
            floor_turns: self.here.turns,
35d571ccadc4atagen2026-10-14 11:33 UTC90
            floor_limit: self.floor.timed().then_some(FLOOR_LIMIT),
624fe9c09c8eatagen2026-10-14 12:11 UTC91
            floor_over: self.floor.timed() && self.served_floor(),
f3a1913c1299atagen2026-10-14 11:42 UTC92
            dangers: self.dangers(),
8b8e83329a1datagen2026-10-12 12:01 UTC93
            observed: self.observed,
886159fd0fe7atagen2026-10-13 12:48 UTC94
            mum_challenge: self.mum_challenge(),
8b8e83329a1datagen2026-10-12 12:01 UTC95
            warnings,
fcc6cf6d5edcatagen2026-10-13 11:31 UTC96
            arrived,
8b8e83329a1datagen2026-10-12 12:01 UTC97
            ruler: self.items.kit.ruler,
8b8e83329a1datagen2026-10-12 12:01 UTC98
            ring: self.items.kit.ring,
8b8e83329a1datagen2026-10-12 12:01 UTC99
        }
8b8e83329a1datagen2026-10-12 12:01 UTC100
    }
8b8e83329a1datagen2026-10-12 12:01 UTC101
8b8e83329a1datagen2026-10-12 12:01 UTC102
    pub fn reach(&self) -> Reach {
8b8e83329a1datagen2026-10-12 12:01 UTC103
        Reach {
8b8e83329a1datagen2026-10-12 12:01 UTC104
            foreskin: self.body.foreskin.is_some(),
8b8e83329a1datagen2026-10-12 12:01 UTC105
            drawer: self.drawer_worth_searching(),
f16ab17c8ff6atagen2026-10-13 11:14 UTC106
            kitchen: self.floor.has_kitchen() && !self.here.kitchen.closed,
8b8e83329a1datagen2026-10-12 12:01 UTC107
            thread: self.thread_reachable(),
8b8e83329a1datagen2026-10-12 12:01 UTC108
            shop: self.floor.shop_open(),
8b8e83329a1datagen2026-10-12 12:01 UTC109
            portal: self.needs_portal(),
8b8e83329a1datagen2026-10-12 12:01 UTC110
            elder: self.floor == Floor::Forum && self.online.elder != Elder::Beaten,
8b8e83329a1datagen2026-10-12 12:01 UTC111
            footage: self.online.filmed_at.is_some(),
8b8e83329a1datagen2026-10-12 12:01 UTC112
            diagnosis: self.floor == Floor::Clinic,
8b8e83329a1datagen2026-10-12 12:01 UTC113
            branch_pending: self.online.story == Story::Pending,
8b8e83329a1datagen2026-10-12 12:01 UTC114
            shopping: self.here.shopping,
8b8e83329a1datagen2026-10-12 12:01 UTC115
        }
8b8e83329a1datagen2026-10-12 12:01 UTC116
    }
8b8e83329a1datagen2026-10-12 12:01 UTC117
8b8e83329a1datagen2026-10-12 12:01 UTC118
    pub fn needs_portal(&self) -> bool {
8b8e83329a1datagen2026-10-12 12:01 UTC119
        self.floor == Floor::Hotel && !self.here.wifi_accepted
8b8e83329a1datagen2026-10-12 12:01 UTC120
    }
8b8e83329a1datagen2026-10-12 12:01 UTC121
8b8e83329a1datagen2026-10-12 12:01 UTC122
    /// Shame follows the player across runs in a signed cookie. Each response adds this run's new
8b8e83329a1datagen2026-10-12 12:01 UTC123
    /// shame to whatever total the client presents, so two runs at once both count.
8b8e83329a1datagen2026-10-12 12:01 UTC124
    pub fn settle_shame(&mut self, presented: Option<u32>) -> u32 {
8b8e83329a1datagen2026-10-12 12:01 UTC125
        let standing = &mut self.standing;
8b8e83329a1datagen2026-10-12 12:01 UTC126
        let total =
8b8e83329a1datagen2026-10-12 12:01 UTC127
            presented.unwrap_or(standing.shame_total) + (standing.shame - standing.shame_reported);
8b8e83329a1datagen2026-10-12 12:01 UTC128
        standing.shame_reported = standing.shame;
8b8e83329a1datagen2026-10-12 12:01 UTC129
        standing.shame_total = total;
8b8e83329a1datagen2026-10-12 12:01 UTC130
        total
8b8e83329a1datagen2026-10-12 12:01 UTC131
    }
8b8e83329a1datagen2026-10-12 12:01 UTC132
8b8e83329a1datagen2026-10-12 12:01 UTC133
    pub fn trace(&mut self) {
8b8e83329a1datagen2026-10-12 12:01 UTC134
        self.standing.shame += 1;
8b8e83329a1datagen2026-10-12 12:01 UTC135
    }
8b8e83329a1datagen2026-10-12 12:01 UTC136
8b8e83329a1datagen2026-10-12 12:01 UTC137
    pub fn measured(&self) -> bool {
8b8e83329a1datagen2026-10-12 12:01 UTC138
        self.items.kit.ruler
8b8e83329a1datagen2026-10-12 12:01 UTC139
    }
8b8e83329a1datagen2026-10-12 12:01 UTC140
8b8e83329a1datagen2026-10-12 12:01 UTC141
    pub fn id(&self) -> &str {
8b8e83329a1datagen2026-10-12 12:01 UTC142
        &self.ident.id
8b8e83329a1datagen2026-10-12 12:01 UTC143
    }
8b8e83329a1datagen2026-10-12 12:01 UTC144
8b8e83329a1datagen2026-10-12 12:01 UTC145
    pub fn last_seen(&self) -> Instant {
8b8e83329a1datagen2026-10-12 12:01 UTC146
        self.ident.last_seen
8b8e83329a1datagen2026-10-12 12:01 UTC147
    }
8b8e83329a1datagen2026-10-12 12:01 UTC148
8b8e83329a1datagen2026-10-12 12:01 UTC149
    /// A request has arrived. Whoever was watching last time has looked away.
8b8e83329a1datagen2026-10-12 12:01 UTC150
    pub fn seen(&mut self, now: Instant) {
8b8e83329a1datagen2026-10-12 12:01 UTC151
        self.ident.last_seen = now;
8b8e83329a1datagen2026-10-12 12:01 UTC152
        self.observed = None;
8b8e83329a1datagen2026-10-12 12:01 UTC153
    }
8b8e83329a1datagen2026-10-12 12:01 UTC154
8b8e83329a1datagen2026-10-12 12:01 UTC155
    pub fn foreskin(&self) -> Option<f64> {
8b8e83329a1datagen2026-10-12 12:01 UTC156
        self.body.foreskin
8b8e83329a1datagen2026-10-12 12:01 UTC157
    }
8b8e83329a1datagen2026-10-12 12:01 UTC158
8b8e83329a1datagen2026-10-12 12:01 UTC159
    pub fn grip(&self) -> Grip {
8b8e83329a1datagen2026-10-12 12:01 UTC160
        self.body.grip
8b8e83329a1datagen2026-10-12 12:01 UTC161
    }
8b8e83329a1datagen2026-10-12 12:01 UTC162
880b29a0c11eatagen2026-10-13 09:27 UTC163
    pub fn knows(&self, technique: Technique) -> bool {
880b29a0c11eatagen2026-10-13 09:27 UTC164
        match technique {
880b29a0c11eatagen2026-10-13 09:27 UTC165
            Technique::Training(Training::Uli) => self.online.uli_taught,
880b29a0c11eatagen2026-10-13 09:27 UTC166
            Technique::Utility(Utility::Meditate) => self.online.meditation_taught,
880b29a0c11eatagen2026-10-13 09:27 UTC167
            _ => true,
880b29a0c11eatagen2026-10-13 09:27 UTC168
        }
8b8e83329a1datagen2026-10-12 12:01 UTC169
    }
8b8e83329a1datagen2026-10-12 12:01 UTC170
8b8e83329a1datagen2026-10-12 12:01 UTC171
    pub fn holds(&self, item: Item) -> bool {
8b8e83329a1datagen2026-10-12 12:01 UTC172
        self.items.has(item)
8b8e83329a1datagen2026-10-12 12:01 UTC173
    }
8b8e83329a1datagen2026-10-12 12:01 UTC174
8b8e83329a1datagen2026-10-12 12:01 UTC175
    pub fn inventory_view(&self) -> Value {
8b8e83329a1datagen2026-10-12 12:01 UTC176
        let items: Vec<Value> = self
8b8e83329a1datagen2026-10-12 12:01 UTC177
            .items
8b8e83329a1datagen2026-10-12 12:01 UTC178
            .owned()
8b8e83329a1datagen2026-10-12 12:01 UTC179
            .into_iter()
8b8e83329a1datagen2026-10-12 12:01 UTC180
            .map(|item| {
8b8e83329a1datagen2026-10-12 12:01 UTC181
                json!({
8b8e83329a1datagen2026-10-12 12:01 UTC182
                    "name": item.slug(),
4f54028cba69atagen2026-10-14 11:45 UTC183
                    "href": self.item_path(item),
8b8e83329a1datagen2026-10-12 12:01 UTC184
                })
8b8e83329a1datagen2026-10-12 12:01 UTC185
            })
8b8e83329a1datagen2026-10-12 12:01 UTC186
            .collect();
8b8e83329a1datagen2026-10-12 12:01 UTC187
        json!({
8b8e83329a1datagen2026-10-12 12:01 UTC188
            "items": items,
8b8e83329a1datagen2026-10-12 12:01 UTC189
            "ice_packs": self.items.stores.ice_packs,
8b8e83329a1datagen2026-10-12 12:01 UTC190
            "supplements": self.items.stores.supplements,
8b8e83329a1datagen2026-10-12 12:01 UTC191
            "excuses": self.excuses().into_iter().map(Excuse::phrase).collect::<Vec<_>>(),
8b8e83329a1datagen2026-10-12 12:01 UTC192
        })
8b8e83329a1datagen2026-10-12 12:01 UTC193
    }
8b8e83329a1datagen2026-10-12 12:01 UTC194
}