atagen

jelq

public

an http based roguelike rpg about jelqing

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