an http based roguelike rpg about jelqing
Commit 97c05b741d8f
tarball5 files changed+70-33
@@ -11,6 +11,7 @@ use super::{
1111
RUPTURE_IN_BAND, RUPTURE_PREMATURE, RUPTURE_WARM, SOFT_INTENSITY, SUSPICION_LIMIT,1212
WANDERER_VISIT, WINDOW,1313
},14
+ measure::round3,1415
techniques::Training,1516
travel::Exit,1617
};@@ -49,7 +50,7 @@ impl Run {
4950
self.decision("cold-degloving", "Cold and unprepared, does the skin tear?"),5051
COLD_DEGLOVING,5152
) {52
- return Err(self.end(Cause::Degloved));53
+ return Err(self.deglove());5354
}5455
}5556
if training == Training::Dry@@ -59,11 +60,20 @@ impl Run {
5960
DEGLOVING,6061
)6162
{62
- return Err(self.end(Cause::Degloved));63
+ return Err(self.deglove());6364
}6465
Ok(ControlFlow::Continue(()))6566
}6667
68
+ fn deglove(&mut self) -> Problem {69
+ let lost = self.body.foreskin.take();70
+ let problem = self.end(Cause::Degloved);71
+ match lost {72
+ Some(lost) => problem.with("foreskin", -round3(lost)),73
+ None => problem,74
+ }75
+ }76
+6777
pub(super) fn hazards(&mut self, intensity: i32) -> Interruption {6878
if !self.hotter_than(PREMATURE_EQ)6979
&& self.chance(@@ -10,7 +10,15 @@ use super::{
1010
};1111
use crate::{headers::SERVER_TIMING, problem::Problem};1212
use axum::http::{StatusCode, header};13
-use serde_json::json;13
+use serde_json::{Value, json};14
+15
+fn gains(detail: &str, length: f64, girth: f64, foreskin: Option<f64>) -> Value {16
+ let mut body = json!({ "detail": detail, "length": round3(length), "girth": round3(girth) });17
+ if let Some(foreskin) = foreskin {18
+ body["foreskin"] = json!(round3(foreskin));19
+ }20
+ body21
+}1422
1523
impl Run {1624
pub(super) fn roll(@@ -52,14 +60,15 @@ impl Run {
5260
return Err(timed(self.rupture()));5361
}5462
if natural == 20 {55
- self.gain(length * 2.0, girth * 2.0, profile.foreskin_growth);63
+ let foreskin = self.gain(length * 2.0, girth * 2.0, profile.foreskin_growth);5664
return Ok(Outcome::json(5765
StatusCode::PERMANENT_REDIRECT,58
- json!({59
- "detail": "Your length has moved permanently.",60
- "length": round3(length * 2.0),61
- "girth": round3(girth * 2.0),62
- }),66
+ gains(67
+ "Your length has moved permanently.",68
+ length * 2.0,69
+ girth * 2.0,70
+ foreskin,71
+ ),6372
)6473
.header(header::LOCATION, self.path("/length"))6574
.header(SERVER_TIMING, timing));@@ -96,24 +105,21 @@ impl Run {
96105
97106
fn land(&mut self, profile: &Profile, length: f64, girth: f64, clean: bool) -> Outcome {98107
if self.body.eq < *EQ_BAND.start() {99
- self.gain(length / 2.0, girth / 2.0, profile.foreskin_growth);108
+ let foreskin = self.gain(length / 2.0, girth / 2.0, profile.foreskin_growth);100109
Outcome::json(101110
StatusCode::PARTIAL_CONTENT,102
- json!({103
- "detail": "Partial gains were applied. EQ was below the band.",104
- "length": round3(length / 2.0),105
- "girth": round3(girth / 2.0),106
- }),111
+ gains(112
+ "Partial gains were applied. EQ was below the band.",113
+ length / 2.0,114
+ girth / 2.0,115
+ foreskin,116
+ ),107117
)108118
} else if clean {109
- self.gain(length, girth, profile.foreskin_growth);119
+ let foreskin = self.gain(length, girth, profile.foreskin_growth);110120
Outcome::json(111121
StatusCode::OK,112
- json!({113
- "detail": "Gains were applied.",114
- "length": round3(length),115
- "girth": round3(girth),116
- }),122
+ gains("Gains were applied.", length, girth, foreskin),117123
)118124
} else {119125
let eta = self.oracle.span_int(@@ -165,14 +171,14 @@ impl Run {
165171
166172
pub(super) fn critical_failure(&mut self, training: Training, profile: &Profile) -> Problem {167173
let uses_foreskin = profile.foreskin.is_some_and(|(min, _)| min > 0.0);168
- if uses_foreskin && self.body.foreskin.is_some() {169
- self.body.foreskin = None;174
+ if uses_foreskin && let Some(lost) = self.body.foreskin.take() {170175
self.standing.shame += 25;171176
return Problem::new(172177
StatusCode::GONE,173178
"foreskin-lost",174179
"The technique has removed the foreskin. It will not be returning.",175180
)181
+ .with("foreskin", -round3(lost))176182
.header(header::CONTENT_LOCATION, self.path("/foreskin"));177183
}178184
if self.hotter_than(DANGER_EQ) {@@ -362,12 +362,12 @@ impl Run {
362362
}363363
}364364
365
- pub(super) fn gain(&mut self, length: f64, girth: f64, foreskin: f64) {365
+ pub(super) fn gain(&mut self, length: f64, girth: f64, foreskin: f64) -> Option<f64> {366366
self.body.length += length;367367
self.body.girth += girth;368
- if let Some(f) = &mut self.body.foreskin {369
- *f += foreskin;370
- }368
+ let f = self.body.foreskin.as_mut().filter(|_| foreskin != 0.0)?;369
+ *f += foreskin;370
+ Some(foreskin)371371
}372372
373373
pub(super) fn rupture(&mut self) -> Problem {@@ -415,3 +415,22 @@ fn the_drawer_starts_with_at_most_one_bottle() {
415415
"{stocked}/400 started with lube"416416
);417417
}418
+419
+#[tokio::test]420
+async fn stretching_reports_the_foreskin_it_grew() {421
+ let mut landed = 0;422
+ for seed in 0..40u64 {423
+ let app = app_with(Floor::Bedroom, seed, |run| {424
+ run.body.warmed_up = true;425
+ run.body.eq = 60;426
+ run.body.foreskin = Some(1.0);427
+ });428
+ let stretch = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;429
+ if stretch.status() == StatusCode::OK {430
+ assert_eq!(header(&stretch, "x-foreskin"), "1.03");431
+ assert_eq!(json(stretch).await["foreskin"], 0.03);432
+ landed += 1;433
+ }434
+ }435
+ assert!(landed > 0, "forty stretches and none landed clean");436
+}@@ -215,12 +215,14 @@ async fn degloving_takes_one_cold_stroke_in_twenty() {
215215
run.body.foreskin = Some(1.0);216216
});217217
let cold = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;218
- if cold.status() == StatusCode::GONE219
- && json(cold).await["type"]220
- .as_str()221
- .unwrap()222
- .ends_with("degloved")223
- {218
+ if cold.status() != StatusCode::GONE {219
+ continue;220
+ }221
+ let bare = header(&cold, "x-foreskin") == "gone";222
+ let problem = json(cold).await;223
+ if problem["type"].as_str().unwrap().ends_with("degloved") {224
+ assert!(bare, "degloving takes the foreskin with it");225
+ assert_eq!(problem["foreskin"], -1.0);224226
degloved += 1;225227
}226228
}