an http based roguelike rpg about jelqing
| 1 | use super::{ |
| 2 | Floor, Lube, MumVisit, Notice, Observer, Run, Story, |
| 3 | balance::{ |
| 4 | BRANCH_TURN, CAT_TAKES_TOWEL, CHANGING_ROOM_CHILL, EQ_WOBBLE, EXTENDER_GAIN, |
| 5 | FORESKIN_GLIDE, FRICTION_BURN, FRICTION_BURN_AT, FRICTION_COLD, FRICTION_DRY, |
| 6 | FRICTION_GRIP, FRICTION_MAX, FRICTION_SPIT, GIRTH_THICK, HOT_EQ, OBSERVED, PRIAPISM_EQ, |
| 7 | PRIAPISM_TURNS, PULLED_MUSCLE, PULLED_MUSCLE_INTENSITY, RING_PRIAPISM, RING_TIGHT_PRIAPISM, |
| 8 | STROKE_EQ, STROKE_EQ_COST, SUSPICION_LIMIT, VIGOUR, |
| 9 | }, |
| 10 | }; |
| 11 | use crate::problem::Problem; |
| 12 | |
| 13 | impl Run { |
| 14 | pub(super) fn chafe(&mut self, lube: Option<Lube>, intensity: i32, cold: bool) { |
| 15 | let (base, slip) = match lube { |
| 16 | None => (FRICTION_DRY, 0), |
| 17 | Some(Lube::Spit) => (FRICTION_SPIT, 0), |
| 18 | Some(Lube::Lotion) => (0, 0), |
| 19 | Some(Lube::BabyOil | Lube::CoconutOil) => (0, 1), |
| 20 | Some(Lube::Vaseline) => (0, 2), |
| 21 | }; |
| 22 | let vigour = usize::try_from(intensity - 1).map_or(0, |i| VIGOUR[i.min(VIGOUR.len() - 1)]); |
| 23 | let unlubed = matches!(lube, None | Some(Lube::Spit)); |
| 24 | let thick = i32::from(unlubed && self.body.girth > GIRTH_THICK); |
| 25 | let glide = i32::from(unlubed && self.body.foreskin.is_some_and(|f| f >= FORESKIN_GLIDE)); |
| 26 | let mut friction = (base + vigour + thick - slip - glide).max(0); |
| 27 | if friction > 0 |
| 28 | && self |
| 29 | .body |
| 30 | .jelq_grip |
| 31 | .is_some_and(|grip| grip != self.body.grip) |
| 32 | { |
| 33 | friction -= FRICTION_GRIP; |
| 34 | } |
| 35 | if cold { |
| 36 | friction += FRICTION_COLD; |
| 37 | } |
| 38 | self.body.jelq_grip = Some(self.body.grip); |
| 39 | self.body.friction = (self.body.friction + friction).min(FRICTION_MAX); |
| 40 | if lube == Some(Lube::Lotion) { |
| 41 | if std::mem::take(&mut self.body.conditions.friction_burn) { |
| 42 | self.notices.push(Notice::BurnSoothed); |
| 43 | } |
| 44 | } else if self.body.friction >= FRICTION_BURN_AT |
| 45 | && !self.body.conditions.friction_burn |
| 46 | && self.chance( |
| 47 | self.decision( |
| 48 | "friction-burn", |
| 49 | "With the skin this raw, has it finally burned?", |
| 50 | ), |
| 51 | FRICTION_BURN, |
| 52 | ) |
| 53 | { |
| 54 | self.body.conditions.friction_burn = true; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | pub(super) fn commit( |
| 59 | &mut self, |
| 60 | intensity: i32, |
| 61 | lube: Option<Lube>, |
| 62 | cost: i32, |
| 63 | ) -> Result<(), Problem> { |
| 64 | self.begin_turn()?; |
| 65 | self.step_away(); |
| 66 | if self.items.kit.extender { |
| 67 | self.body.length += EXTENDER_GAIN; |
| 68 | } |
| 69 | self.body.stamina -= cost + i32::from(lube == Some(Lube::Lotion)); |
| 70 | if self.body.stamina <= 0 { |
| 71 | self.exhaust(); |
| 72 | } |
| 73 | let chill = if self.floor == Floor::ChangingRoom { |
| 74 | CHANGING_ROOM_CHILL |
| 75 | } else { |
| 76 | 0 |
| 77 | }; |
| 78 | let wobble = self.oracle.span_int( |
| 79 | self.decision( |
| 80 | "wobble", |
| 81 | "How does the count drift on this stroke, up or down?", |
| 82 | ), |
| 83 | -EQ_WOBBLE, |
| 84 | EQ_WOBBLE, |
| 85 | ); |
| 86 | self.body.eq = |
| 87 | (self.body.eq + intensity * STROKE_EQ - STROKE_EQ_COST - chill + wobble).clamp(0, 100); |
| 88 | |
| 89 | self.make_noise(intensity / 5 + i32::from(lube == Some(Lube::Vaseline))); |
| 90 | if self.floor == Floor::Nans && lube == Some(Lube::CoconutOil) { |
| 91 | self.add_suspicion(2); |
| 92 | } |
| 93 | |
| 94 | if intensity >= PULLED_MUSCLE_INTENSITY |
| 95 | && !self.body.conditions.pulled_muscle |
| 96 | && self.oracle.chance_not( |
| 97 | self.decision("pulled-muscle", "Do the muscles hold?"), |
| 98 | PULLED_MUSCLE, |
| 99 | ) |
| 100 | { |
| 101 | self.body.conditions.pulled_muscle = true; |
| 102 | self.body.stamina = self.body.stamina.min(self.max_stamina()); |
| 103 | } |
| 104 | self.settle(); |
| 105 | Ok(()) |
| 106 | } |
| 107 | |
| 108 | pub(super) fn make_noise(&mut self, noise: i32) { |
| 109 | let doubled = match self.floor { |
| 110 | Floor::Nans => true, |
| 111 | Floor::ChangingRoom => !self.holds_own_in_the_locker_room(), |
| 112 | _ => false, |
| 113 | }; |
| 114 | self.add_suspicion(if doubled { noise * 2 } else { noise }); |
| 115 | } |
| 116 | |
| 117 | pub(super) fn add_suspicion(&mut self, amount: i32) { |
| 118 | self.standing.suspicion = u8::try_from( |
| 119 | (i32::from(self.standing.suspicion) + amount).clamp(0, i32::from(SUSPICION_LIMIT)), |
| 120 | ) |
| 121 | .unwrap_or(SUSPICION_LIMIT); |
| 122 | } |
| 123 | |
| 124 | pub(super) fn settle(&mut self) { |
| 125 | self.maybe_summon_mum(); |
| 126 | if let Some(observer) = self.floor.observer() |
| 127 | && self.chance( |
| 128 | self.decision("observed", "Has someone quietly noticed what is going on?"), |
| 129 | OBSERVED, |
| 130 | ) |
| 131 | { |
| 132 | self.observed = Some(observer); |
| 133 | self.standing.shame += 1; |
| 134 | if observer == Observer::Cat |
| 135 | && self.chance( |
| 136 | self.decision("cat-takes-towel", "Has the cat taken the towel?"), |
| 137 | CAT_TAKES_TOWEL, |
| 138 | ) |
| 139 | { |
| 140 | self.here.towel_stolen = true; |
| 141 | } |
| 142 | } |
| 143 | if self.items.kit.ring |
| 144 | && self.body.eq > HOT_EQ |
| 145 | && self.chance( |
| 146 | self.decision( |
| 147 | "ring-priapism", |
| 148 | "With the ring still on and everything this warm, will it not go down?", |
| 149 | ), |
| 150 | if self.body.girth > GIRTH_THICK { |
| 151 | RING_TIGHT_PRIAPISM |
| 152 | } else { |
| 153 | RING_PRIAPISM |
| 154 | }, |
| 155 | ) |
| 156 | { |
| 157 | self.body.conditions.priapism_turns = PRIAPISM_TURNS; |
| 158 | } |
| 159 | if self.body.conditions.priapism_turns > 0 { |
| 160 | self.body.conditions.priapism_turns -= 1; |
| 161 | self.body.eq = self.body.eq.max(PRIAPISM_EQ); |
| 162 | } |
| 163 | if self.turn == BRANCH_TURN && self.online.story == Story::Unopened { |
| 164 | self.online.story = Story::Pending; |
| 165 | self.notices.push(Notice::ThreadHasReplies); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | pub(super) fn maybe_summon_mum(&mut self) { |
| 170 | let was_below = self.standing.at_turn_start < SUSPICION_LIMIT; |
| 171 | if !self.floor.has_mum() || self.household.mum.is_some() { |
| 172 | return; |
| 173 | } |
| 174 | let summoned = was_below && self.standing.suspicion == SUSPICION_LIMIT; |
| 175 | let mut visits = u32::from(self.standing.suspicion) + 1; |
| 176 | if self.here.brother_told { |
| 177 | visits *= 2; |
| 178 | } |
| 179 | if summoned |
| 180 | || self.oracle.ratio( |
| 181 | self.decision( |
| 182 | "mum-summon", |
| 183 | "With this much noise in the house, is Mum on her way up?", |
| 184 | ), |
| 185 | visits, |
| 186 | super::balance::MUM_VISIT, |
| 187 | ) |
| 188 | { |
| 189 | let errand = self.errand(summoned); |
| 190 | let nonce = self.fresh_nonce(); |
| 191 | self.household.mum = Some(MumVisit { |
| 192 | errand, |
| 193 | nonce, |
| 194 | strikes: 0, |
| 195 | knocked: false, |
| 196 | }); |
| 197 | } |
| 198 | } |
| 199 | } |