an http based roguelike rpg about jelqing
Commit f48daa8adf59
tarball4 files changed+62-11
@@ -2,3 +2,5 @@ pub const MAX_STAMINA: i32 = 10;
22
pub const PULLED_STAMINA: i32 = 7;33
pub const EDGE_LOOP: u8 = 3;44
pub const ENERGY_STAMINA: i32 = 3;5
+pub const REFRACTORY_STAMINA: (i32, i32) = (4, 6);6
+pub const SECOND_WIND: u32 = 20;@@ -33,15 +33,23 @@ impl Run {
3333
return Err(self.rupture());3434
}3535
if self.body.exhausted {36
- self.recover();36
+ let second_wind = self.recover();3737
self.shed(FRICTION_HOLD);3838
self.settle();39
- let detail = if self.body.conditions.priapism_turns > 0 {40
- "The count went well past ten. Stamina is back. It went down a little. Maybe. You hope."39
+ let back = if second_wind {40
+ "The count went well past ten, and something came back that had no business coming back. Stamina is full."4141
} else {42
- "The count went well past ten. Stamina is back. Everything else has gone quiet, and you are cold again."42
+ "The count went well past ten. Some of the stamina is back."4343
};44
- return Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail })));44
+ let after = if self.body.conditions.priapism_turns > 0 {45
+ "It went down a little. Maybe. You hope."46
+ } else {47
+ "Everything else has gone quiet, and you are cold again."48
+ };49
+ return Ok(Outcome::json(50
+ StatusCode::OK,51
+ json!({ "detail": format!("{back} {after}") }),52
+ ));4553
}4654
if self.chance(4755
self.decision(@@ -10,8 +10,8 @@ use super::{
1010
Body, Cause, Clinic, Ending, Floor, FloorState, Household, Item, Items, Notice, Observer,1111
Online, RunStatus, Standing, Story,1212
balance::{13
- BRANCH_TURN, FLOOR_LIMIT, MAX_STAMINA, PRIAPISM_EQ, PULLED_STAMINA, SELF_REPORT_INFLATION,14
- STARTING_BABY_OIL, STARTING_EQ, STARTING_LUBE,13
+ BRANCH_TURN, FLOOR_LIMIT, MAX_STAMINA, PRIAPISM_EQ, PULLED_STAMINA, REFRACTORY_STAMINA,14
+ SECOND_WIND, SELF_REPORT_INFLATION, STARTING_BABY_OIL, STARTING_EQ, STARTING_LUBE,1515
},1616
measure::round2,1717
oracle::{Oracle, OracleTemplate, Roll},@@ -334,11 +334,32 @@ impl Run {
334334
cause.problem().with("turn", self.turn)335335
}336336
337
- /// The refractory period is the next HOLD. It comes back with full stamina and nothing else.338
- pub(super) fn recover(&mut self) {337
+ /// The refractory period is the next HOLD. Returns whether it rolled the second wind that refills stamina outright.338
+ pub(super) fn recover(&mut self) -> bool {339339
self.body.exhausted = false;340
- self.body.stamina = self.max_stamina();340
+ let second_wind = self.chance(341
+ self.decision(342
+ "second-wind",343
+ "Does the refractory period end in a second wind?",344
+ ),345
+ SECOND_WIND,346
+ );347
+ self.body.stamina = if second_wind {348
+ self.max_stamina()349
+ } else {350
+ let (lo, hi) = REFRACTORY_STAMINA;351
+ let back = self.oracle.span_int(352
+ self.decision(353
+ "refractory",354
+ "How much stamina comes back after the refractory period?",355
+ ),356
+ lo,357
+ hi,358
+ );359
+ back.min(self.max_stamina())360
+ };341361
self.reset_eq(STARTING_EQ);362
+ second_wind342363
}343364
344365
pub(super) fn check_exhausted(&self) -> Result<(), Problem> {@@ -78,7 +78,7 @@ async fn the_refractory_period_is_the_next_hold() {
7878
assert_eq!(tired.status(), StatusCode::TOO_MANY_REQUESTS);7979
let hold = send(&app, "HOLD", "/runs/cafebabe", &[], "").await;8080
assert_eq!(hold.status(), StatusCode::OK);81
- assert_eq!(header(&hold, "x-stamina"), "10/10");81
+ assert!(["4/10", "5/10", "6/10", "10/10"].contains(&header(&hold, "x-stamina")));8282
let towel = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;8383
assert_eq!(towel.status(), StatusCode::CONFLICT, "the towel stays cold");8484
}@@ -402,3 +402,23 @@ async fn an_overstayed_jelq_spends_no_lube() {
402402
);403403
}404404
}405
+406
+#[tokio::test]407
+async fn the_refractory_period_gives_back_some_stamina_and_rarely_all() {408
+ let mut full = 0;409
+ for seed in 0..400u64 {410
+ let mut run = Run::new("cafebabe".into(), "cafe".into(), seed);411
+ run.floor = Floor::ChangingRoom;412
+ run.body.stamina = 0;413
+ run.body.exhausted = true;414
+ if run.hold().is_err() {415
+ continue;416
+ }417
+ match run.body.stamina {418
+ 4..=6 => {}419
+ 10 => full += 1,420
+ other => panic!("seed {seed} recovered {other} stamina"),421
+ }422
+ }423
+ assert!((8..=35).contains(&full), "{full} second winds in 400");424
+}