atagen

jelq

public

an http based roguelike rpg about jelqing

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

Blame: src/game/progress.rs

8b8e83329a1datagen2026-10-12 12:01 UTC1
use super::{
8b8e83329a1datagen2026-10-12 12:01 UTC2
    Floor, Lube, MumVisit, Notice, Observer, Run, Story,
8b8e83329a1datagen2026-10-12 12:01 UTC3
    balance::{
8b8e83329a1datagen2026-10-12 12:01 UTC4
        BRANCH_TURN, CAT_TAKES_TOWEL, CHANGING_ROOM_CHILL, EQ_WOBBLE, EXTENDER_GAIN,
4b1f5f88827datagen2026-10-13 10:18 UTC5
        FORESKIN_GLIDE, FRICTION_BURN, FRICTION_BURN_AT, FRICTION_COLD, FRICTION_DRY,
4b1f5f88827datagen2026-10-13 10:18 UTC6
        FRICTION_GRIP, FRICTION_MAX, FRICTION_SPIT, GIRTH_THICK, HOT_EQ, OBSERVED, PRIAPISM_EQ,
4b1f5f88827datagen2026-10-13 10:18 UTC7
        PRIAPISM_TURNS, PULLED_MUSCLE, PULLED_MUSCLE_INTENSITY, RING_PRIAPISM, RING_TIGHT_PRIAPISM,
4b1f5f88827datagen2026-10-13 10:18 UTC8
        STROKE_EQ, STROKE_EQ_COST, SUSPICION_LIMIT, VIGOUR,
8b8e83329a1datagen2026-10-12 12:01 UTC9
    },
8b8e83329a1datagen2026-10-12 12:01 UTC10
};
3ac624339c98atagen2026-10-13 12:21 UTC11
use crate::problem::Problem;
8b8e83329a1datagen2026-10-12 12:01 UTC12
8b8e83329a1datagen2026-10-12 12:01 UTC13
impl Run {
bf5f8cb6f60fatagen2026-10-13 12:10 UTC14
    pub(super) fn chafe(&mut self, lube: Option<Lube>, intensity: i32, cold: bool) {
8b8e83329a1datagen2026-10-12 12:01 UTC15
        let (base, slip) = match lube {
8b8e83329a1datagen2026-10-12 12:01 UTC16
            None => (FRICTION_DRY, 0),
8b8e83329a1datagen2026-10-12 12:01 UTC17
            Some(Lube::Spit) => (FRICTION_SPIT, 0),
8b8e83329a1datagen2026-10-12 12:01 UTC18
            Some(Lube::Lotion) => (0, 0),
8b8e83329a1datagen2026-10-12 12:01 UTC19
            Some(Lube::BabyOil | Lube::CoconutOil) => (0, 1),
8b8e83329a1datagen2026-10-12 12:01 UTC20
            Some(Lube::Vaseline) => (0, 2),
8b8e83329a1datagen2026-10-12 12:01 UTC21
        };
8b8e83329a1datagen2026-10-12 12:01 UTC22
        let vigour = usize::try_from(intensity - 1).map_or(0, |i| VIGOUR[i.min(VIGOUR.len() - 1)]);
8b8e83329a1datagen2026-10-12 12:01 UTC23
        let unlubed = matches!(lube, None | Some(Lube::Spit));
8b8e83329a1datagen2026-10-12 12:01 UTC24
        let thick = i32::from(unlubed && self.body.girth > GIRTH_THICK);
8b8e83329a1datagen2026-10-12 12:01 UTC25
        let glide = i32::from(unlubed && self.body.foreskin.is_some_and(|f| f >= FORESKIN_GLIDE));
8b8e83329a1datagen2026-10-12 12:01 UTC26
        let mut friction = (base + vigour + thick - slip - glide).max(0);
8b8e83329a1datagen2026-10-12 12:01 UTC27
        if friction > 0
8b8e83329a1datagen2026-10-12 12:01 UTC28
            && self
8b8e83329a1datagen2026-10-12 12:01 UTC29
                .body
8b8e83329a1datagen2026-10-12 12:01 UTC30
                .jelq_grip
8b8e83329a1datagen2026-10-12 12:01 UTC31
                .is_some_and(|grip| grip != self.body.grip)
8b8e83329a1datagen2026-10-12 12:01 UTC32
        {
8b8e83329a1datagen2026-10-12 12:01 UTC33
            friction -= FRICTION_GRIP;
8b8e83329a1datagen2026-10-12 12:01 UTC34
        }
bf5f8cb6f60fatagen2026-10-13 12:10 UTC35
        if cold {
4b1f5f88827datagen2026-10-13 10:18 UTC36
            friction += FRICTION_COLD;
4b1f5f88827datagen2026-10-13 10:18 UTC37
        }
8b8e83329a1datagen2026-10-12 12:01 UTC38
        self.body.jelq_grip = Some(self.body.grip);
8b8e83329a1datagen2026-10-12 12:01 UTC39
        self.body.friction = (self.body.friction + friction).min(FRICTION_MAX);
8b8e83329a1datagen2026-10-12 12:01 UTC40
        if self.body.friction >= FRICTION_BURN_AT
8b8e83329a1datagen2026-10-12 12:01 UTC41
            && !self.body.conditions.friction_burn
8b8e83329a1datagen2026-10-12 12:01 UTC42
            && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC43
                self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC44
                    "friction-burn",
8b8e83329a1datagen2026-10-12 12:01 UTC45
                    "With the skin this raw, has it finally burned?",
8b8e83329a1datagen2026-10-12 12:01 UTC46
                ),
8b8e83329a1datagen2026-10-12 12:01 UTC47
                FRICTION_BURN,
8b8e83329a1datagen2026-10-12 12:01 UTC48
            )
8b8e83329a1datagen2026-10-12 12:01 UTC49
        {
8b8e83329a1datagen2026-10-12 12:01 UTC50
            self.body.conditions.friction_burn = true;
8b8e83329a1datagen2026-10-12 12:01 UTC51
        }
8b8e83329a1datagen2026-10-12 12:01 UTC52
    }
8b8e83329a1datagen2026-10-12 12:01 UTC53
3ac624339c98atagen2026-10-13 12:21 UTC54
    pub(super) fn commit(
3ac624339c98atagen2026-10-13 12:21 UTC55
        &mut self,
3ac624339c98atagen2026-10-13 12:21 UTC56
        intensity: i32,
3ac624339c98atagen2026-10-13 12:21 UTC57
        lube: Option<Lube>,
3ac624339c98atagen2026-10-13 12:21 UTC58
        cost: i32,
3ac624339c98atagen2026-10-13 12:21 UTC59
    ) -> Result<(), Problem> {
3ac624339c98atagen2026-10-13 12:21 UTC60
        self.begin_turn()?;
8b8e83329a1datagen2026-10-12 12:01 UTC61
        self.step_away();
8b8e83329a1datagen2026-10-12 12:01 UTC62
        if self.items.kit.extender {
8b8e83329a1datagen2026-10-12 12:01 UTC63
            self.body.length += EXTENDER_GAIN;
8b8e83329a1datagen2026-10-12 12:01 UTC64
        }
8b8e83329a1datagen2026-10-12 12:01 UTC65
        self.body.stamina -= cost + i32::from(lube == Some(Lube::Lotion));
8b8e83329a1datagen2026-10-12 12:01 UTC66
        if self.body.stamina <= 0 {
8b8e83329a1datagen2026-10-12 12:01 UTC67
            self.exhaust();
8b8e83329a1datagen2026-10-12 12:01 UTC68
        }
8b8e83329a1datagen2026-10-12 12:01 UTC69
        let chill = if self.floor == Floor::ChangingRoom {
8b8e83329a1datagen2026-10-12 12:01 UTC70
            CHANGING_ROOM_CHILL
8b8e83329a1datagen2026-10-12 12:01 UTC71
        } else {
8b8e83329a1datagen2026-10-12 12:01 UTC72
            0
8b8e83329a1datagen2026-10-12 12:01 UTC73
        };
8b8e83329a1datagen2026-10-12 12:01 UTC74
        let wobble = self.oracle.span_int(
8b8e83329a1datagen2026-10-12 12:01 UTC75
            self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC76
                "wobble",
8b8e83329a1datagen2026-10-12 12:01 UTC77
                "How does the count drift on this stroke, up or down?",
8b8e83329a1datagen2026-10-12 12:01 UTC78
            ),
8b8e83329a1datagen2026-10-12 12:01 UTC79
            -EQ_WOBBLE,
8b8e83329a1datagen2026-10-12 12:01 UTC80
            EQ_WOBBLE,
8b8e83329a1datagen2026-10-12 12:01 UTC81
        );
8b8e83329a1datagen2026-10-12 12:01 UTC82
        self.body.eq =
8b8e83329a1datagen2026-10-12 12:01 UTC83
            (self.body.eq + intensity * STROKE_EQ - STROKE_EQ_COST - chill + wobble).clamp(0, 100);
8b8e83329a1datagen2026-10-12 12:01 UTC84
8b8e83329a1datagen2026-10-12 12:01 UTC85
        self.make_noise(intensity / 5 + i32::from(lube == Some(Lube::Vaseline)));
8b8e83329a1datagen2026-10-12 12:01 UTC86
        if self.floor == Floor::Nans && lube == Some(Lube::CoconutOil) {
8b8e83329a1datagen2026-10-12 12:01 UTC87
            self.add_suspicion(2);
8b8e83329a1datagen2026-10-12 12:01 UTC88
        }
8b8e83329a1datagen2026-10-12 12:01 UTC89
8b8e83329a1datagen2026-10-12 12:01 UTC90
        if intensity >= PULLED_MUSCLE_INTENSITY
8b8e83329a1datagen2026-10-12 12:01 UTC91
            && !self.body.conditions.pulled_muscle
8b8e83329a1datagen2026-10-12 12:01 UTC92
            && self.oracle.chance_not(
8b8e83329a1datagen2026-10-12 12:01 UTC93
                self.decision("pulled-muscle", "Do the muscles hold?"),
8b8e83329a1datagen2026-10-12 12:01 UTC94
                PULLED_MUSCLE,
8b8e83329a1datagen2026-10-12 12:01 UTC95
            )
8b8e83329a1datagen2026-10-12 12:01 UTC96
        {
8b8e83329a1datagen2026-10-12 12:01 UTC97
            self.body.conditions.pulled_muscle = true;
8b8e83329a1datagen2026-10-12 12:01 UTC98
            self.body.stamina = self.body.stamina.min(self.max_stamina());
8b8e83329a1datagen2026-10-12 12:01 UTC99
        }
8b8e83329a1datagen2026-10-12 12:01 UTC100
        self.settle();
3ac624339c98atagen2026-10-13 12:21 UTC101
        Ok(())
8b8e83329a1datagen2026-10-12 12:01 UTC102
    }
8b8e83329a1datagen2026-10-12 12:01 UTC103
8b8e83329a1datagen2026-10-12 12:01 UTC104
    pub(super) fn make_noise(&mut self, noise: i32) {
8b8e83329a1datagen2026-10-12 12:01 UTC105
        let doubled = match self.floor {
8b8e83329a1datagen2026-10-12 12:01 UTC106
            Floor::Nans => true,
8b8e83329a1datagen2026-10-12 12:01 UTC107
            Floor::ChangingRoom => !self.holds_own_in_the_locker_room(),
8b8e83329a1datagen2026-10-12 12:01 UTC108
            _ => false,
8b8e83329a1datagen2026-10-12 12:01 UTC109
        };
8b8e83329a1datagen2026-10-12 12:01 UTC110
        self.add_suspicion(if doubled { noise * 2 } else { noise });
8b8e83329a1datagen2026-10-12 12:01 UTC111
    }
8b8e83329a1datagen2026-10-12 12:01 UTC112
8b8e83329a1datagen2026-10-12 12:01 UTC113
    pub(super) fn add_suspicion(&mut self, amount: i32) {
8b8e83329a1datagen2026-10-12 12:01 UTC114
        self.standing.suspicion = u8::try_from(
8b8e83329a1datagen2026-10-12 12:01 UTC115
            (i32::from(self.standing.suspicion) + amount).clamp(0, i32::from(SUSPICION_LIMIT)),
8b8e83329a1datagen2026-10-12 12:01 UTC116
        )
8b8e83329a1datagen2026-10-12 12:01 UTC117
        .unwrap_or(SUSPICION_LIMIT);
8b8e83329a1datagen2026-10-12 12:01 UTC118
    }
8b8e83329a1datagen2026-10-12 12:01 UTC119
8b8e83329a1datagen2026-10-12 12:01 UTC120
    pub(super) fn settle(&mut self) {
8b8e83329a1datagen2026-10-12 12:01 UTC121
        self.maybe_summon_mum();
8b8e83329a1datagen2026-10-12 12:01 UTC122
        if let Some(observer) = self.floor.observer()
8b8e83329a1datagen2026-10-12 12:01 UTC123
            && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC124
                self.decision("observed", "Has someone quietly noticed what is going on?"),
8b8e83329a1datagen2026-10-12 12:01 UTC125
                OBSERVED,
8b8e83329a1datagen2026-10-12 12:01 UTC126
            )
8b8e83329a1datagen2026-10-12 12:01 UTC127
        {
8b8e83329a1datagen2026-10-12 12:01 UTC128
            self.observed = Some(observer);
8b8e83329a1datagen2026-10-12 12:01 UTC129
            self.standing.shame += 1;
8b8e83329a1datagen2026-10-12 12:01 UTC130
            if observer == Observer::Cat
8b8e83329a1datagen2026-10-12 12:01 UTC131
                && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC132
                    self.decision("cat-takes-towel", "Has the cat taken the towel?"),
8b8e83329a1datagen2026-10-12 12:01 UTC133
                    CAT_TAKES_TOWEL,
8b8e83329a1datagen2026-10-12 12:01 UTC134
                )
8b8e83329a1datagen2026-10-12 12:01 UTC135
            {
8b8e83329a1datagen2026-10-12 12:01 UTC136
                self.here.towel_stolen = true;
8b8e83329a1datagen2026-10-12 12:01 UTC137
            }
8b8e83329a1datagen2026-10-12 12:01 UTC138
        }
8b8e83329a1datagen2026-10-12 12:01 UTC139
        if self.items.kit.ring
8b8e83329a1datagen2026-10-12 12:01 UTC140
            && self.body.eq > HOT_EQ
8b8e83329a1datagen2026-10-12 12:01 UTC141
            && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC142
                self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC143
                    "ring-priapism",
8b8e83329a1datagen2026-10-12 12:01 UTC144
                    "With the ring still on and everything this warm, will it not go down?",
8b8e83329a1datagen2026-10-12 12:01 UTC145
                ),
8b8e83329a1datagen2026-10-12 12:01 UTC146
                if self.body.girth > GIRTH_THICK {
8b8e83329a1datagen2026-10-12 12:01 UTC147
                    RING_TIGHT_PRIAPISM
8b8e83329a1datagen2026-10-12 12:01 UTC148
                } else {
8b8e83329a1datagen2026-10-12 12:01 UTC149
                    RING_PRIAPISM
8b8e83329a1datagen2026-10-12 12:01 UTC150
                },
8b8e83329a1datagen2026-10-12 12:01 UTC151
            )
8b8e83329a1datagen2026-10-12 12:01 UTC152
        {
8b8e83329a1datagen2026-10-12 12:01 UTC153
            self.body.conditions.priapism_turns = PRIAPISM_TURNS;
8b8e83329a1datagen2026-10-12 12:01 UTC154
        }
8b8e83329a1datagen2026-10-12 12:01 UTC155
        if self.body.conditions.priapism_turns > 0 {
8b8e83329a1datagen2026-10-12 12:01 UTC156
            self.body.conditions.priapism_turns -= 1;
8b8e83329a1datagen2026-10-12 12:01 UTC157
            self.body.eq = self.body.eq.max(PRIAPISM_EQ);
8b8e83329a1datagen2026-10-12 12:01 UTC158
        }
8b8e83329a1datagen2026-10-12 12:01 UTC159
        if self.turn == BRANCH_TURN && self.online.story == Story::Unopened {
8b8e83329a1datagen2026-10-12 12:01 UTC160
            self.online.story = Story::Pending;
8b8e83329a1datagen2026-10-12 12:01 UTC161
            self.notices.push(Notice::ThreadHasReplies);
8b8e83329a1datagen2026-10-12 12:01 UTC162
        }
8b8e83329a1datagen2026-10-12 12:01 UTC163
    }
8b8e83329a1datagen2026-10-12 12:01 UTC164
8b8e83329a1datagen2026-10-12 12:01 UTC165
    pub(super) fn maybe_summon_mum(&mut self) {
8b8e83329a1datagen2026-10-12 12:01 UTC166
        let was_below = self.standing.at_turn_start < SUSPICION_LIMIT;
8b8e83329a1datagen2026-10-12 12:01 UTC167
        if !self.floor.has_mum() || self.household.mum.is_some() {
8b8e83329a1datagen2026-10-12 12:01 UTC168
            return;
8b8e83329a1datagen2026-10-12 12:01 UTC169
        }
8b8e83329a1datagen2026-10-12 12:01 UTC170
        let summoned = was_below && self.standing.suspicion == SUSPICION_LIMIT;
8b8e83329a1datagen2026-10-12 12:01 UTC171
        let mut visits = u32::from(self.standing.suspicion) + 1;
8b8e83329a1datagen2026-10-12 12:01 UTC172
        if self.here.brother_told {
8b8e83329a1datagen2026-10-12 12:01 UTC173
            visits *= 2;
8b8e83329a1datagen2026-10-12 12:01 UTC174
        }
8b8e83329a1datagen2026-10-12 12:01 UTC175
        if summoned
8b8e83329a1datagen2026-10-12 12:01 UTC176
            || self.oracle.ratio(
8b8e83329a1datagen2026-10-12 12:01 UTC177
                self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC178
                    "mum-summon",
8b8e83329a1datagen2026-10-12 12:01 UTC179
                    "With this much noise in the house, is Mum on her way up?",
8b8e83329a1datagen2026-10-12 12:01 UTC180
                ),
8b8e83329a1datagen2026-10-12 12:01 UTC181
                visits,
8b8e83329a1datagen2026-10-12 12:01 UTC182
                super::balance::MUM_VISIT,
8b8e83329a1datagen2026-10-12 12:01 UTC183
            )
8b8e83329a1datagen2026-10-12 12:01 UTC184
        {
8b8e83329a1datagen2026-10-12 12:01 UTC185
            let errand = self.errand(summoned);
8b8e83329a1datagen2026-10-12 12:01 UTC186
            let nonce = self.fresh_nonce();
8b8e83329a1datagen2026-10-12 12:01 UTC187
            self.household.mum = Some(MumVisit {
8b8e83329a1datagen2026-10-12 12:01 UTC188
                errand,
8b8e83329a1datagen2026-10-12 12:01 UTC189
                nonce,
8b8e83329a1datagen2026-10-12 12:01 UTC190
                strikes: 0,
8b8e83329a1datagen2026-10-12 12:01 UTC191
                knocked: false,
8b8e83329a1datagen2026-10-12 12:01 UTC192
            });
8b8e83329a1datagen2026-10-12 12:01 UTC193
        }
8b8e83329a1datagen2026-10-12 12:01 UTC194
    }
8b8e83329a1datagen2026-10-12 12:01 UTC195
}