an http based roguelike rpg about jelqing
Commit bf5f8cb6f60f
tarball14 files changed+48-71
@@ -17,7 +17,6 @@ pub struct Body {
1717
pub(crate) grip: Grip,1818
/// The grip of the last stroke, which decides whether this one gets the switching discount.1919
pub(crate) jelq_grip: Option<Grip>,20
- pub(crate) warmed_up: bool,2120
pub(crate) exhausted: bool,2221
pub(crate) put_applied: bool,2322
pub(crate) conditions: Conditions,@@ -36,7 +35,6 @@ impl Body {
3635
friction: 0,3736
grip: Grip::Overhand,3837
jelq_grip: None,39
- warmed_up: false,4038
exhausted: false,4139
put_applied: false,4240
conditions: Conditions::default(),@@ -84,9 +84,10 @@ impl Run {
8484
self.lube_available(lube)?;8585
self.floor_enemy()?;8686
self.take_lube(lube)?;87
- self.chafe(lube, intensity);87
+ let cold = self.cold();88
+ self.chafe(lube, intensity, cold);8889
self.commit(intensity, lube, 1);89
- if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity)? {90
+ if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity, cold)? {9091
return Ok(interrupted);9192
}9293
if let ControlFlow::Break(interrupted) = self.hazards(intensity)? {@@ -116,11 +117,12 @@ impl Run {
116117
self.lube_available(lube)?;117118
self.floor_enemy()?;118119
self.take_lube(lube)?;119
- self.chafe(lube, intensity);120
- self.chafe(lube, intensity);120
+ let cold = self.cold();121
+ self.chafe(lube, intensity, cold);122
+ self.chafe(lube, intensity, cold);121123
self.commit(intensity, lube, 2);122124
for training in [a, b] {123
- if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity)? {125
+ if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity, cold)? {124126
return Ok(interrupted);125127
}126128
}@@ -41,8 +41,17 @@ impl Run {
4141
}4242
}4343
44
- pub(super) fn mishaps(&mut self, training: Training, intensity: i32) -> Interruption {45
- if !self.body.warmed_up {44
+ pub(super) fn cold(&self) -> bool {45
+ self.body.eq < COLD_EQ46
+ }47
+48
+ pub(super) fn mishaps(49
+ &mut self,50
+ training: Training,51
+ intensity: i32,52
+ cold: bool,53
+ ) -> Interruption {54
+ if cold {4655
if !self.notices.contains(&Notice::ColdStart) {4756
self.notices.push(Notice::ColdStart);4857
}@@ -155,13 +164,6 @@ impl Run {
155164
"The floor has ended early. Everyone involved has agreed not to discuss it.",156165
));157166
}158
- if self.body.eq < COLD_EQ {159
- return Err(Problem::new(160
- StatusCode::NOT_FOUND,161
- "cold",162
- "The resource exists. It is cold.",163
- ));164
- }165167
Ok(ControlFlow::Continue(()))166168
}167169
@@ -29,7 +29,7 @@ impl Notice {
2929
Self::ThreadHasReplies => "Somewhere, a forum thread from 2009 still has replies.",3030
Self::Reflection => "the count reached ten, and then the reflection started",3131
Self::MumSatisfied => "Mum seems satisfied. She takes the washing with her.",32
- Self::ColdStart => "Warm-up was skipped. The server has noted this.",32
+ Self::ColdStart => "Cold start. The server has noted this.",3333
}3434
}3535
}@@ -10,7 +10,7 @@ use super::{
1010
};1111
1212
impl Run {13
- pub(super) fn chafe(&mut self, lube: Option<Lube>, intensity: i32) {13
+ pub(super) fn chafe(&mut self, lube: Option<Lube>, intensity: i32, cold: bool) {1414
let (base, slip) = match lube {1515
None => (FRICTION_DRY, 0),1616
Some(Lube::Spit) => (FRICTION_SPIT, 0),@@ -31,7 +31,7 @@ impl Run {
3131
{3232
friction -= FRICTION_GRIP;3333
}34
- if !self.body.warmed_up {34
+ if cold {3535
friction += FRICTION_COLD;3636
}3737
self.body.jelq_grip = Some(self.body.grip);@@ -219,10 +219,9 @@ impl Run {
219219
}220220
}221221
s.push_str(&format!(222
- "suspicion: {}\nshame: {}\nwarmed up: {}\ngrip: {}\n",222
+ "suspicion: {}\nshame: {}\ngrip: {}\n",223223
self.standing.suspicion,224224
self.standing.shame,225
- if self.body.warmed_up { "yes" } else { "no" },226225
self.body.grip.name(),227226
));228227
let held: Vec<&str> = self.items.owned().iter().map(|item| item.slug()).collect();@@ -314,7 +313,6 @@ impl Run {
314313
"turn": self.turn,315314
"run_shame": self.standing.shame,316315
"grip": self.body.grip.name(),317
- "warmed_up": self.body.warmed_up,318316
"ranked": !self.ident.seeded,319317
"conditions": conditions,320318
})@@ -333,7 +331,6 @@ impl Run {
333331
self.body.exhausted = false;334332
self.body.stamina = self.max_stamina();335333
self.reset_eq(STARTING_EQ);336
- self.body.warmed_up = false;337334
}338335
339336
pub(super) fn check_exhausted(&self) -> Result<(), Problem> {@@ -28,7 +28,6 @@ impl Run {
2828
warm_arrival: warm,2929
..FloorState::default()3030
};31
- self.body.warmed_up = false;3231
self.reset_eq(STARTING_EQ);3332
self.standing.suspicion = 0;3433
self.shed(FRICTION_FLOOR);@@ -61,7 +61,6 @@ impl Run {
6161
self.here.kitchen.kettle_boiled = false;6262
}6363
self.here.towel_warmed = true;64
- self.body.warmed_up = true;6564
self.body.eq = (self.body.eq + boost).min(100);6665
Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail })))6766
}@@ -147,7 +146,7 @@ impl Run {
147146
}148147
149148
pub(super) fn edge(&mut self) -> Result<Outcome, Problem> {150
- self.chafe(None, 5);149
+ self.chafe(None, 5, false);151150
self.commit(5, None, 1);152151
if let ControlFlow::Break(interrupted) = self.hazards(5)? {153152
return Ok(interrupted);@@ -100,8 +100,8 @@ async fn the_displayed_length_is_accepted_as_honest() {
100100
101101
#[tokio::test]102102
async fn dry_jelqs_build_friction_and_real_lube_does_not() {103
- let app = seeded_with(Floor::Bedroom, |run| {104
- run.body.warmed_up = true;103
+ let app = seeded_with(Floor::ChangingRoom, |run| {104
+ run.body.eq = 60;105105
run.items.lubes.baby_oil = 1;106106
run.body.foreskin = Some(0.9);107107
});@@ -131,8 +131,8 @@ async fn dry_jelqs_build_friction_and_real_lube_does_not() {
131131
132132
#[tokio::test]133133
async fn switching_grips_makes_the_friction_cheaper() {134
- let app = seeded_with(Floor::Bedroom, |run| {135
- run.body.warmed_up = true;134
+ let app = seeded_with(Floor::ChangingRoom, |run| {135
+ run.body.eq = 60;136136
run.body.foreskin = Some(0.9);137137
});138138
let dry = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;@@ -185,12 +185,10 @@ async fn a_friction_burn_halves_the_gains() {
185185
let path = "/runs/cafebabe?technique=wet&intensity=5";186186
for seed in 0..60u64 {187187
let steady = app_with(Floor::Bedroom, seed, |run| {188
- run.body.warmed_up = true;189188
run.body.eq = 60;190189
run.items.lubes.baby_oil = 9;191190
});192191
let hurt_app = app_with(Floor::Bedroom, seed, |run| {193
- run.body.warmed_up = true;194192
run.body.eq = 60;195193
run.items.lubes.baby_oil = 9;196194
run.body.conditions.friction_burn = true;@@ -214,7 +212,6 @@ async fn a_friction_burn_halves_the_gains() {
214212
#[tokio::test]215213
async fn the_burn_warns_and_the_ice_treats_it() {216214
let app = seeded_with(Floor::Bedroom, |run| {217
- run.body.warmed_up = true;218215
run.body.eq = 60;219216
run.items.lubes.baby_oil = 9;220217
run.body.friction = 7;@@ -253,7 +250,6 @@ async fn friction_rises_with_vigour_and_edging_chafes() {
253250
for (intensity, friction) in [(5, "1/10"), (7, "2/10"), (10, "3/10")] {254251
let app = seeded_with(Floor::Hotel, |run| {255252
run.here.wifi_accepted = true;256
- run.body.warmed_up = true;257253
run.body.eq = 55;258254
run.body.foreskin = Some(0.5);259255
});@@ -267,7 +263,6 @@ async fn friction_rises_with_vigour_and_edging_chafes() {
267263
}268264
let app = seeded_with(Floor::Hotel, |run| {269265
run.here.wifi_accepted = true;270
- run.body.warmed_up = true;271266
run.body.eq = 55;272267
run.body.foreskin = Some(0.5);273268
});@@ -282,7 +277,6 @@ async fn the_body_changes_how_the_skin_takes_it() {
282277
{283278
let app = seeded_with(Floor::Hotel, |run| {284279
run.here.wifi_accepted = true;285
- run.body.warmed_up = true;286280
run.body.eq = 55;287281
run.body.foreskin = Some(foreskin);288282
run.body.girth = girth;@@ -309,7 +303,6 @@ async fn a_thick_girth_outgrows_the_underhand_grip() {
309303
#[tokio::test]310304
async fn a_long_run_fills_the_jeans_before_a_thick_one() {311305
let app = seeded_with(Floor::Bedroom, |run| {312
- run.body.warmed_up = true;313306
run.items.kit.extender = true;314307
run.body.girth = 4.8;315308
run.body.length = 7.5;@@ -322,7 +315,6 @@ async fn a_long_run_fills_the_jeans_before_a_thick_one() {
322315
#[tokio::test]323316
async fn a_refused_jelq_leaves_the_lube_alone() {324317
let app = seeded_with(Floor::Bedroom, |run| {325
- run.body.warmed_up = true;326318
run.items.kit.extender = true;327319
run.body.girth = 6.0;328320
run.body.length = 6.0;@@ -346,7 +338,6 @@ async fn a_refused_jelq_leaves_the_lube_alone() {
346338
#[tokio::test]347339
async fn lubes_run_out_but_spit_never_does() {348340
let app = seeded_with(Floor::Bedroom, |run| {349
- run.body.warmed_up = true;350341
run.items.lubes.baby_oil = 1;351342
});352343
let lube = [("content-type", "application/baby-oil")];@@ -384,7 +375,6 @@ async fn patching_costs_a_little_eq() {
384375
async fn a_combo_chafes_twice_and_costs_two_stamina() {385376
let app = seeded_with(Floor::Hotel, |run| {386377
run.here.wifi_accepted = true;387
- run.body.warmed_up = true;388378
run.body.eq = 55;389379
run.body.foreskin = Some(0.5);390380
});@@ -421,7 +411,6 @@ async fn stretching_reports_the_foreskin_it_grew() {
421411
let mut landed = 0;422412
for seed in 0..40u64 {423413
let app = app_with(Floor::Bedroom, seed, |run| {424
- run.body.warmed_up = true;425414
run.body.eq = 60;426415
run.body.foreskin = Some(1.0);427416
});@@ -115,7 +115,6 @@ async fn caged_drawers_are_noisy_and_eventually_give_up_the_key() {
115115
async fn an_open_tab_is_linked_and_jelq_still_answers_with_choices() {116116
let app = seeded_with(Floor::Bathroom, |run| {117117
run.online.story = Story::Pending;118
- run.body.warmed_up = true;119118
});120119
let response = send(&app, "GET", "/runs/cafebabe", &[], "").await;121120
let links = header(&response, "link");@@ -131,7 +130,6 @@ async fn either_story_choice_is_reachable_during_cooldown() {
131130
for choice in ["forum", "close-tab"] {132131
let app = seeded_with(Floor::Bathroom, |run| {133132
run.turn = 9;134
- run.body.warmed_up = true;135133
run.body.eq = 45;136134
run.body.stamina = 1;137135
run.items.stores.door_locks = 1;@@ -157,7 +155,6 @@ async fn either_story_choice_is_reachable_during_cooldown() {
157155
async fn the_thread_announces_itself_at_the_branch() {158156
let app = seeded_with(Floor::Bathroom, |run| {159157
run.turn = 8;160
- run.body.warmed_up = true;161158
run.items.lubes.baby_oil = 9;162159
});163160
send(@@ -196,7 +193,6 @@ async fn reading_the_thread_teaches_uli() {
196193
for seed in 0..100u64 {197194
let app = app_with(Floor::Bedroom, seed, |run| {198195
run.turn = BRANCH_TURN;199
- run.body.warmed_up = true;200196
run.online.story = Story::ClosedTab;201197
run.online.meditation_taught = true;202198
});@@ -235,7 +231,7 @@ async fn reading_the_thread_teaches_uli() {
235231
236232
#[tokio::test]237233
async fn the_thread_cannot_be_read_before_anyone_mentions_it() {238
- let app = seeded_with(Floor::Bedroom, |run| run.body.warmed_up = true);234
+ let app = seeded_with(Floor::Bedroom, |_| {});239235
let read = send(&app, "GET", "/runs/cafebabe/thread", &[], "").await;240236
assert_eq!(read.status(), StatusCode::NOT_FOUND);241237
let after = send(&app, "GET", "/runs/cafebabe", &[], "").await;@@ -439,7 +435,6 @@ async fn the_foreskin_window_has_hard_edges() {
439435
] {440436
let app = seeded_with(Floor::Hotel, |run| {441437
run.here.wifi_accepted = true;442
- run.body.warmed_up = true;443438
run.body.eq = 55;444439
run.body.foreskin = Some(foreskin);445440
});@@ -461,7 +456,6 @@ async fn the_forum_teaches_breathing_before_uli() {
461456
for seed in 0..100u64 {462457
let app = app_with(Floor::Bedroom, seed, |run| {463458
run.turn = BRANCH_TURN;464
- run.body.warmed_up = true;465459
run.online.story = Story::Pending;466460
});467461
let listed = json(send(&app, "GET", "/runs/cafebabe/techniques", &[], "").await).await;@@ -154,7 +154,6 @@ async fn mom_speaks_american() {
154154
#[tokio::test]155155
async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {156156
let control = seeded_with(Floor::ChangingRoom, |run| {157
- run.body.warmed_up = true;158157
run.standing.suspicion = SUSPICION_LIMIT;159158
});160159
let lube = [("content-type", "application/baby-oil")];@@ -169,7 +168,6 @@ async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {
169168
assert_eq!(caught.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);170169
171170
let app = seeded_with(Floor::ChangingRoom, |run| {172
- run.body.warmed_up = true;173171
run.standing.suspicion = SUSPICION_LIMIT;174172
run.items.lubes.baby_oil = 0;175173
});@@ -191,7 +189,6 @@ async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {
191189
async fn respectable_girth_waves_off_the_gym_staff() {192190
let staff = |girth: f64| {193191
seeded_with(Floor::ChangingRoom, move |run| {194
- run.body.warmed_up = true;195192
run.body.eq = 45;196193
run.body.girth = girth;197194
run.standing.suspicion = SUSPICION_LIMIT;@@ -31,13 +31,11 @@ async fn bodies_do_not_repeat_the_headers() {
3131
async fn jelqing_cold_goes_through_at_a_price() {3232
let friction = async |warm: bool| {3333
let app = seeded_with(Floor::Bedroom, |run| {34
- run.body.warmed_up = warm;34
+ run.body.eq = if warm { 40 } else { 20 };3535
run.body.foreskin = Some(1.0);3636
});3737
let response = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;38
- let noted = warnings(&response)39
- .iter()40
- .any(|w| w.contains("Warm-up was skipped"));38
+ let noted = warnings(&response).iter().any(|w| w.contains("Cold start"));4139
assert_eq!(noted, !warm);4240
header(&response, "x-friction").to_owned()4341
};@@ -30,7 +30,6 @@ async fn the_burn_shifts_the_rupture_tiers() {
3030
let mut gone = 0;3131
for seed in 0..400u64 {3232
let app = app_with(Floor::Bedroom, seed, |run| {33
- run.body.warmed_up = true;3433
run.body.eq = 60;3534
run.items.lubes.baby_oil = 9;3635
run.body.conditions.friction_burn = burned;@@ -64,7 +63,6 @@ async fn the_top_tier_rolls_a_single_third() {
6463
let mut early = 0;6564
for seed in 0..400u64 {6665
let app = app_with(Floor::Bedroom, seed, |run| {67
- run.body.warmed_up = true;6866
run.body.eq = 96;6967
run.items.lubes.baby_oil = 9;7068
run.items.kit.blinds = true;@@ -100,7 +98,6 @@ async fn holding_rolls_a_flat_rupture_chance() {
10098
let mut gone = 0;10199
for seed in 0..600u64 {102100
let app = app_with(Floor::Bedroom, seed, |run| {103
- run.body.warmed_up = true;104101
run.body.eq = eq;105102
run.body.stamina = stamina;106103
});@@ -178,7 +175,6 @@ async fn degloving_takes_one_hard_dry_stroke_in_twenty() {
178175
for seed in 0..1200u64 {179176
let app = app_with(Floor::Hotel, seed, |run| {180177
run.here.wifi_accepted = true;181
- run.body.warmed_up = true;182178
run.body.eq = 55;183179
run.body.foreskin = Some(1.0);184180
});@@ -211,7 +207,7 @@ async fn degloving_takes_one_cold_stroke_in_twenty() {
211207
for seed in 0..1200u64 {212208
let app = app_with(Floor::Hotel, seed, |run| {213209
run.here.wifi_accepted = true;214
- run.body.eq = 55;210
+ run.body.eq = 20;215211
run.body.foreskin = Some(1.0);216212
});217213
let cold = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;@@ -27,7 +27,6 @@ async fn meditation_respects_the_priapism_floor() {
2727
#[tokio::test]2828
async fn priapism_holds_the_floor_through_every_reset() {2929
let exit = seeded_with(Floor::Bedroom, |run| {30
- run.body.warmed_up = true;3130
run.body.eq = 90;3231
run.here.turns = 99;3332
run.body.conditions.priapism_turns = 3;@@ -70,7 +69,6 @@ async fn the_towel_warms_once_until_the_kettle_boils() {
7069
async fn the_refractory_period_is_the_next_hold() {7170
let app = seeded_with(Floor::Hotel, |run| {7271
run.here.wifi_accepted = true;73
- run.body.warmed_up = true;7472
run.here.towel_warmed = true;7573
run.body.eq = 60;7674
run.body.stamina = 0;@@ -88,7 +86,6 @@ async fn the_refractory_period_is_the_next_hold() {
8886
#[tokio::test]8987
async fn a_refused_jelq_does_not_break_the_edging_streak() {9088
let app = seeded_with(Floor::ChangingRoom, |run| {91
- run.body.warmed_up = true;9289
run.body.eq = 45;9390
run.here.edges = 2;9491
});@@ -107,14 +104,14 @@ async fn a_refused_jelq_does_not_break_the_edging_streak() {
107104
108105
#[tokio::test]109106
async fn an_interrupted_edge_keeps_no_gains() {110
- let app = seeded_with(Floor::ChangingRoom, |run| {111
- run.body.warmed_up = true;112
- run.body.eq = 10;107
+ let app = seeded_with(Floor::Hotel, |run| {108
+ run.here.wifi_accepted = true;109
+ run.body.eq = 100;113110
});114111
let before = send(&app, "GET", "/runs/cafebabe", &[], "").await;115112
let girth = header(&before, "x-girth").to_owned();116113
let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;117
- assert_eq!(edge.status(), StatusCode::NOT_FOUND);114
+ assert!(!edge.status().is_success());118115
assert_eq!(header(&edge, "x-girth"), girth);119116
}120117
@@ -126,12 +123,10 @@ async fn greed_multiplies_gains_above_the_band() {
126123
for seed in 0..120u64 {127124
let app = app_with(Floor::Bedroom, seed, |run| {128125
run.body.eq = 60;129
- run.body.warmed_up = true;130126
});131127
let in_band = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;132128
let app = app_with(Floor::Bedroom, seed, |run| {133129
run.body.eq = eq;134
- run.body.warmed_up = true;135130
});136131
let greedy = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;137132
if in_band.status() != StatusCode::OK || greedy.status() != StatusCode::OK {@@ -187,7 +182,6 @@ async fn hold_keeps_the_shop_and_the_melon_open() {
187182
async fn leaving_the_band_warms_the_next_floor() {188183
let app = seeded_with(Floor::Bedroom, |run| {189184
run.body.eq = 60;190
- run.body.warmed_up = true;191185
run.here.turns = 6;192186
run.items.lubes.baby_oil = 9;193187
});@@ -255,7 +249,6 @@ async fn queued_gains_announce_their_arrival() {
255249
#[tokio::test]256250
async fn an_energy_drink_ends_exhaustion_without_the_refractory_period() {257251
let app = seeded_with(Floor::Bedroom, |run| {258
- run.body.warmed_up = true;259252
run.body.eq = 60;260253
run.body.stamina = 0;261254
run.body.exhausted = true;@@ -274,3 +267,16 @@ async fn an_energy_drink_ends_exhaustion_without_the_refractory_period() {
274267
let jelq = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;275268
assert_ne!(jelq.status(), StatusCode::TOO_MANY_REQUESTS);276269
}270
+271
+#[tokio::test]272
+async fn edging_climbs_out_of_the_cold() {273
+ let app = seeded_with(Floor::ChangingRoom, |run| run.body.eq = 20);274
+ let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;275
+ assert_eq!(edge.status(), StatusCode::OK);276
+ assert!(warnings(&edge).iter().all(|w| !w.contains("Cold start")));277
+ assert_eq!(278
+ header(&edge, "x-friction"),279
+ "2/10",280
+ "edging pays no cold tax"281
+ );282
+}