atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
35d571ccadc4c1643547ed0a6e9d3e1e99fc184f
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,
61fe020d61e0atagen2026-10-14 09:19 UTC6
        FRICTION_GRIP, FRICTION_LOTION, FRICTION_MAX, FRICTION_SPIT, GIRTH_THICK, HOT_EQ, OBSERVED,
61fe020d61e0atagen2026-10-14 09:19 UTC7
        PRIAPISM_EQ, PRIAPISM_TURNS, PULLED_MUSCLE, PULLED_MUSCLE_INTENSITY, RING_PRIAPISM,
61fe020d61e0atagen2026-10-14 09:19 UTC8
        RING_TIGHT_PRIAPISM, 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
        }
61fe020d61e0atagen2026-10-14 09:19 UTC38
        if lube == Some(Lube::Lotion) {
61fe020d61e0atagen2026-10-14 09:19 UTC39
            friction -= FRICTION_LOTION;
61fe020d61e0atagen2026-10-14 09:19 UTC40
        }
8b8e83329a1datagen2026-10-12 12:01 UTC41
        self.body.jelq_grip = Some(self.body.grip);
61fe020d61e0atagen2026-10-14 09:19 UTC42
        self.body.friction = (self.body.friction + friction).clamp(0, FRICTION_MAX);
2d9074e0b4deatagen2026-10-13 12:35 UTC43
        if lube == Some(Lube::Lotion) {
2d9074e0b4deatagen2026-10-13 12:35 UTC44
            if std::mem::take(&mut self.body.conditions.friction_burn) {
2d9074e0b4deatagen2026-10-13 12:35 UTC45
                self.notices.push(Notice::BurnSoothed);
2d9074e0b4deatagen2026-10-13 12:35 UTC46
            }
2d9074e0b4deatagen2026-10-13 12:35 UTC47
        } else if self.body.friction >= FRICTION_BURN_AT
8b8e83329a1datagen2026-10-12 12:01 UTC48
            && !self.body.conditions.friction_burn
8b8e83329a1datagen2026-10-12 12:01 UTC49
            && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC50
                self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC51
                    "friction-burn",
8b8e83329a1datagen2026-10-12 12:01 UTC52
                    "With the skin this raw, has it finally burned?",
8b8e83329a1datagen2026-10-12 12:01 UTC53
                ),
8b8e83329a1datagen2026-10-12 12:01 UTC54
                FRICTION_BURN,
8b8e83329a1datagen2026-10-12 12:01 UTC55
            )
8b8e83329a1datagen2026-10-12 12:01 UTC56
        {
8b8e83329a1datagen2026-10-12 12:01 UTC57
            self.body.conditions.friction_burn = true;
8b8e83329a1datagen2026-10-12 12:01 UTC58
        }
8b8e83329a1datagen2026-10-12 12:01 UTC59
    }
8b8e83329a1datagen2026-10-12 12:01 UTC60
3ac624339c98atagen2026-10-13 12:21 UTC61
    pub(super) fn commit(
3ac624339c98atagen2026-10-13 12:21 UTC62
        &mut self,
3ac624339c98atagen2026-10-13 12:21 UTC63
        intensity: i32,
3ac624339c98atagen2026-10-13 12:21 UTC64
        lube: Option<Lube>,
3ac624339c98atagen2026-10-13 12:21 UTC65
        cost: i32,
3ac624339c98atagen2026-10-13 12:21 UTC66
    ) -> Result<(), Problem> {
3ac624339c98atagen2026-10-13 12:21 UTC67
        self.begin_turn()?;
8b8e83329a1datagen2026-10-12 12:01 UTC68
        self.step_away();
8b8e83329a1datagen2026-10-12 12:01 UTC69
        if self.items.kit.extender {
8b8e83329a1datagen2026-10-12 12:01 UTC70
            self.body.length += EXTENDER_GAIN;
8b8e83329a1datagen2026-10-12 12:01 UTC71
        }
8b8e83329a1datagen2026-10-12 12:01 UTC72
        self.body.stamina -= cost + i32::from(lube == Some(Lube::Lotion));
8b8e83329a1datagen2026-10-12 12:01 UTC73
        if self.body.stamina <= 0 {
8b8e83329a1datagen2026-10-12 12:01 UTC74
            self.exhaust();
8b8e83329a1datagen2026-10-12 12:01 UTC75
        }
8b8e83329a1datagen2026-10-12 12:01 UTC76
        let chill = if self.floor == Floor::ChangingRoom {
8b8e83329a1datagen2026-10-12 12:01 UTC77
            CHANGING_ROOM_CHILL
8b8e83329a1datagen2026-10-12 12:01 UTC78
        } else {
8b8e83329a1datagen2026-10-12 12:01 UTC79
            0
8b8e83329a1datagen2026-10-12 12:01 UTC80
        };
8b8e83329a1datagen2026-10-12 12:01 UTC81
        let wobble = self.oracle.span_int(
8b8e83329a1datagen2026-10-12 12:01 UTC82
            self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC83
                "wobble",
8b8e83329a1datagen2026-10-12 12:01 UTC84
                "How does the count drift on this stroke, up or down?",
8b8e83329a1datagen2026-10-12 12:01 UTC85
            ),
8b8e83329a1datagen2026-10-12 12:01 UTC86
            -EQ_WOBBLE,
8b8e83329a1datagen2026-10-12 12:01 UTC87
            EQ_WOBBLE,
8b8e83329a1datagen2026-10-12 12:01 UTC88
        );
8b8e83329a1datagen2026-10-12 12:01 UTC89
        self.body.eq =
8b8e83329a1datagen2026-10-12 12:01 UTC90
            (self.body.eq + intensity * STROKE_EQ - STROKE_EQ_COST - chill + wobble).clamp(0, 100);
8b8e83329a1datagen2026-10-12 12:01 UTC91
8b8e83329a1datagen2026-10-12 12:01 UTC92
        self.make_noise(intensity / 5 + i32::from(lube == Some(Lube::Vaseline)));
8b8e83329a1datagen2026-10-12 12:01 UTC93
        if self.floor == Floor::Nans && lube == Some(Lube::CoconutOil) {
8b8e83329a1datagen2026-10-12 12:01 UTC94
            self.add_suspicion(2);
8b8e83329a1datagen2026-10-12 12:01 UTC95
        }
8b8e83329a1datagen2026-10-12 12:01 UTC96
8b8e83329a1datagen2026-10-12 12:01 UTC97
        if intensity >= PULLED_MUSCLE_INTENSITY
8b8e83329a1datagen2026-10-12 12:01 UTC98
            && !self.body.conditions.pulled_muscle
8b8e83329a1datagen2026-10-12 12:01 UTC99
            && self.oracle.chance_not(
8b8e83329a1datagen2026-10-12 12:01 UTC100
                self.decision("pulled-muscle", "Do the muscles hold?"),
8b8e83329a1datagen2026-10-12 12:01 UTC101
                PULLED_MUSCLE,
8b8e83329a1datagen2026-10-12 12:01 UTC102
            )
8b8e83329a1datagen2026-10-12 12:01 UTC103
        {
8b8e83329a1datagen2026-10-12 12:01 UTC104
            self.body.conditions.pulled_muscle = true;
8b8e83329a1datagen2026-10-12 12:01 UTC105
            self.body.stamina = self.body.stamina.min(self.max_stamina());
8b8e83329a1datagen2026-10-12 12:01 UTC106
        }
8b8e83329a1datagen2026-10-12 12:01 UTC107
        self.settle();
3ac624339c98atagen2026-10-13 12:21 UTC108
        Ok(())
8b8e83329a1datagen2026-10-12 12:01 UTC109
    }
8b8e83329a1datagen2026-10-12 12:01 UTC110
8b8e83329a1datagen2026-10-12 12:01 UTC111
    pub(super) fn make_noise(&mut self, noise: i32) {
8b8e83329a1datagen2026-10-12 12:01 UTC112
        let doubled = match self.floor {
8b8e83329a1datagen2026-10-12 12:01 UTC113
            Floor::Nans => true,
8b8e83329a1datagen2026-10-12 12:01 UTC114
            Floor::ChangingRoom => !self.holds_own_in_the_locker_room(),
8b8e83329a1datagen2026-10-12 12:01 UTC115
            _ => false,
8b8e83329a1datagen2026-10-12 12:01 UTC116
        };
8b8e83329a1datagen2026-10-12 12:01 UTC117
        self.add_suspicion(if doubled { noise * 2 } else { noise });
8b8e83329a1datagen2026-10-12 12:01 UTC118
    }
8b8e83329a1datagen2026-10-12 12:01 UTC119
8b8e83329a1datagen2026-10-12 12:01 UTC120
    pub(super) fn add_suspicion(&mut self, amount: i32) {
8b8e83329a1datagen2026-10-12 12:01 UTC121
        self.standing.suspicion = u8::try_from(
8b8e83329a1datagen2026-10-12 12:01 UTC122
            (i32::from(self.standing.suspicion) + amount).clamp(0, i32::from(SUSPICION_LIMIT)),
8b8e83329a1datagen2026-10-12 12:01 UTC123
        )
8b8e83329a1datagen2026-10-12 12:01 UTC124
        .unwrap_or(SUSPICION_LIMIT);
8b8e83329a1datagen2026-10-12 12:01 UTC125
    }
8b8e83329a1datagen2026-10-12 12:01 UTC126
8b8e83329a1datagen2026-10-12 12:01 UTC127
    pub(super) fn settle(&mut self) {
8b8e83329a1datagen2026-10-12 12:01 UTC128
        self.maybe_summon_mum();
8b8e83329a1datagen2026-10-12 12:01 UTC129
        if let Some(observer) = self.floor.observer()
8b8e83329a1datagen2026-10-12 12:01 UTC130
            && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC131
                self.decision("observed", "Has someone quietly noticed what is going on?"),
8b8e83329a1datagen2026-10-12 12:01 UTC132
                OBSERVED,
8b8e83329a1datagen2026-10-12 12:01 UTC133
            )
8b8e83329a1datagen2026-10-12 12:01 UTC134
        {
8b8e83329a1datagen2026-10-12 12:01 UTC135
            self.observed = Some(observer);
8b8e83329a1datagen2026-10-12 12:01 UTC136
            self.standing.shame += 1;
8b8e83329a1datagen2026-10-12 12:01 UTC137
            if observer == Observer::Cat
8b8e83329a1datagen2026-10-12 12:01 UTC138
                && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC139
                    self.decision("cat-takes-towel", "Has the cat taken the towel?"),
8b8e83329a1datagen2026-10-12 12:01 UTC140
                    CAT_TAKES_TOWEL,
8b8e83329a1datagen2026-10-12 12:01 UTC141
                )
8b8e83329a1datagen2026-10-12 12:01 UTC142
            {
8b8e83329a1datagen2026-10-12 12:01 UTC143
                self.here.towel_stolen = true;
8b8e83329a1datagen2026-10-12 12:01 UTC144
            }
8b8e83329a1datagen2026-10-12 12:01 UTC145
        }
8b8e83329a1datagen2026-10-12 12:01 UTC146
        if self.items.kit.ring
8b8e83329a1datagen2026-10-12 12:01 UTC147
            && self.body.eq > HOT_EQ
8b8e83329a1datagen2026-10-12 12:01 UTC148
            && self.chance(
8b8e83329a1datagen2026-10-12 12:01 UTC149
                self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC150
                    "ring-priapism",
8b8e83329a1datagen2026-10-12 12:01 UTC151
                    "With the ring still on and everything this warm, will it not go down?",
8b8e83329a1datagen2026-10-12 12:01 UTC152
                ),
8b8e83329a1datagen2026-10-12 12:01 UTC153
                if self.body.girth > GIRTH_THICK {
8b8e83329a1datagen2026-10-12 12:01 UTC154
                    RING_TIGHT_PRIAPISM
8b8e83329a1datagen2026-10-12 12:01 UTC155
                } else {
8b8e83329a1datagen2026-10-12 12:01 UTC156
                    RING_PRIAPISM
8b8e83329a1datagen2026-10-12 12:01 UTC157
                },
8b8e83329a1datagen2026-10-12 12:01 UTC158
            )
8b8e83329a1datagen2026-10-12 12:01 UTC159
        {
8b8e83329a1datagen2026-10-12 12:01 UTC160
            self.body.conditions.priapism_turns = PRIAPISM_TURNS;
8b8e83329a1datagen2026-10-12 12:01 UTC161
        }
8b8e83329a1datagen2026-10-12 12:01 UTC162
        if self.body.conditions.priapism_turns > 0 {
8b8e83329a1datagen2026-10-12 12:01 UTC163
            self.body.conditions.priapism_turns -= 1;
8b8e83329a1datagen2026-10-12 12:01 UTC164
            self.body.eq = self.body.eq.max(PRIAPISM_EQ);
8b8e83329a1datagen2026-10-12 12:01 UTC165
        }
8b8e83329a1datagen2026-10-12 12:01 UTC166
        if self.turn == BRANCH_TURN && self.online.story == Story::Unopened {
8b8e83329a1datagen2026-10-12 12:01 UTC167
            self.online.story = Story::Pending;
8b8e83329a1datagen2026-10-12 12:01 UTC168
            self.notices.push(Notice::ThreadHasReplies);
8b8e83329a1datagen2026-10-12 12:01 UTC169
        }
8b8e83329a1datagen2026-10-12 12:01 UTC170
    }
8b8e83329a1datagen2026-10-12 12:01 UTC171
8b8e83329a1datagen2026-10-12 12:01 UTC172
    pub(super) fn maybe_summon_mum(&mut self) {
8b8e83329a1datagen2026-10-12 12:01 UTC173
        let was_below = self.standing.at_turn_start < SUSPICION_LIMIT;
2176d9a94bd9atagen2026-10-14 11:03 UTC174
        if !self.floor.has_mum() || self.household.mum.is_some() || self.household.just_left {
8b8e83329a1datagen2026-10-12 12:01 UTC175
            return;
8b8e83329a1datagen2026-10-12 12:01 UTC176
        }
8b8e83329a1datagen2026-10-12 12:01 UTC177
        let summoned = was_below && self.standing.suspicion == SUSPICION_LIMIT;
8b8e83329a1datagen2026-10-12 12:01 UTC178
        let mut visits = u32::from(self.standing.suspicion) + 1;
8b8e83329a1datagen2026-10-12 12:01 UTC179
        if self.here.brother_told {
8b8e83329a1datagen2026-10-12 12:01 UTC180
            visits *= 2;
8b8e83329a1datagen2026-10-12 12:01 UTC181
        }
8b8e83329a1datagen2026-10-12 12:01 UTC182
        if summoned
8b8e83329a1datagen2026-10-12 12:01 UTC183
            || self.oracle.ratio(
8b8e83329a1datagen2026-10-12 12:01 UTC184
                self.decision(
8b8e83329a1datagen2026-10-12 12:01 UTC185
                    "mum-summon",
8b8e83329a1datagen2026-10-12 12:01 UTC186
                    "With this much noise in the house, is Mum on her way up?",
8b8e83329a1datagen2026-10-12 12:01 UTC187
                ),
8b8e83329a1datagen2026-10-12 12:01 UTC188
                visits,
8b8e83329a1datagen2026-10-12 12:01 UTC189
                super::balance::MUM_VISIT,
8b8e83329a1datagen2026-10-12 12:01 UTC190
            )
8b8e83329a1datagen2026-10-12 12:01 UTC191
        {
8b8e83329a1datagen2026-10-12 12:01 UTC192
            let errand = self.errand(summoned);
2176d9a94bd9atagen2026-10-14 11:03 UTC193
            self.notices.push(Notice::MumAtTheDoor(errand.arrival));
8b8e83329a1datagen2026-10-12 12:01 UTC194
            let nonce = self.fresh_nonce();
8b8e83329a1datagen2026-10-12 12:01 UTC195
            self.household.mum = Some(MumVisit {
8b8e83329a1datagen2026-10-12 12:01 UTC196
                errand,
8b8e83329a1datagen2026-10-12 12:01 UTC197
                nonce,
8b8e83329a1datagen2026-10-12 12:01 UTC198
                strikes: 0,
8b8e83329a1datagen2026-10-12 12:01 UTC199
                knocked: false,
8b8e83329a1datagen2026-10-12 12:01 UTC200
            });
8b8e83329a1datagen2026-10-12 12:01 UTC201
        }
8b8e83329a1datagen2026-10-12 12:01 UTC202
    }
8b8e83329a1datagen2026-10-12 12:01 UTC203
}