an http based roguelike rpg about jelqing
Commit 6033b0db186c
tarball5 files changed+76-16
@@ -66,6 +66,7 @@ impl Run {
6666
}6767
self.ready(Technique::Utility(utility), lube)?;6868
self.check_storage()?;69
+ self.check_clock()?;6970
self.floor_enemy()?;7071
match utility {7172
Utility::Edge => self.edge(),@@ -82,6 +83,7 @@ impl Run {
8283
self.ready(Technique::Training(training), lube)?;8384
self.check_storage()?;8485
self.lube_available(lube)?;86
+ self.check_clock()?;8587
self.floor_enemy()?;8688
self.take_lube(lube)?;8789
let cold = self.cold();@@ -115,6 +117,7 @@ impl Run {
115117
self.ready(Technique::Training(b), lube)?;116118
self.check_storage()?;117119
self.lube_available(lube)?;120
+ self.check_clock()?;118121
self.floor_enemy()?;119122
self.take_lube(lube)?;120123
let cold = self.cold();@@ -133,13 +133,15 @@ impl Run {
133133
girth,134134
foreskin: profile.foreskin_growth,135135
});136
- Outcome::json(137
- StatusCode::ACCEPTED,138
- json!({139
- "detail": "Gains have been queued. They may arrive.",140
- "eta_turns": eta,141
- }),142
- )136
+ let mut body = json!({137
+ "detail": "Gains have been queued. They may arrive.",138
+ "eta_turns": eta,139
+ });140
+ let torn = std::mem::take(&mut self.body.torn);141
+ if torn > 0.0 {142
+ body["foreskin"] = json!(-round3(torn));143
+ }144
+ Outcome::json(StatusCode::ACCEPTED, body)143145
}144146
}145147
@@ -149,15 +149,13 @@ impl Run {
149149
}150150
151151
pub(super) fn begin_turn(&mut self) -> Result<(), Problem> {152
- if self.floor.timed() && self.here.turns + 1 >= FLOOR_LIMIT {153
- return Err(self.overstay());154
- }152
+ self.check_clock()?;155153
self.touch();156154
self.turn += 1;157155
self.here.turns += 1;158156
self.body.torn = 0.0;159157
self.standing.at_turn_start = self.standing.suspicion;160
- self.realise();158
+ self.realise(false);161159
if self.floor.timed() && self.here.turns + 2 == FLOOR_LIMIT {162160
self.notices.push(Notice::RunningLong(self.floor));163161
} else if self.floor.timed() && self.here.turns + 1 == FLOOR_LIMIT {@@ -359,10 +357,11 @@ impl Run {
359357
self.body.exhausted = true;360358
}361359
362
- pub(super) fn realise(&mut self) {360
+ /// Leaving a floor lets time pass, so everything still queued arrives at once.361
+ pub(super) fn realise(&mut self, everything: bool) {363362
let (due, waiting): (Vec<_>, Vec<_>) = std::mem::take(&mut self.body.pending)364363
.into_iter()365
- .partition(|p| p.due <= self.turn);364
+ .partition(|p| everything || p.due <= self.turn);366365
self.body.pending = waiting;367366
for p in due {368367
let foreskin = self.gain(p.length, p.girth, p.foreskin);@@ -1,8 +1,9 @@
11
use super::{22
Cause, Elder, Floor, FloorState, Notice, Outcome, Run,33
balance::{4
- EQ_BAND, FLOOR_ALLOWANCE, FLOOR_TURNS, FRICTION_FLOOR, LOCKER_ROOM_BAR, LOCKER_ROOM_OTHERS,5
- LOCKER_ROOM_SHAME, OVERSTAY_BUSTED, OVERSTAY_EQ, OVERSTAY_SHAME, STARTING_EQ, ZIP,4
+ EQ_BAND, FLOOR_ALLOWANCE, FLOOR_LIMIT, FLOOR_TURNS, FRICTION_FLOOR, LOCKER_ROOM_BAR,5
+ LOCKER_ROOM_OTHERS, LOCKER_ROOM_SHAME, OVERSTAY_BUSTED, OVERSTAY_EQ, OVERSTAY_SHAME,6
+ STARTING_EQ, ZIP,67
},78
measure::round2,89
};@@ -29,6 +30,7 @@ impl Run {
2930
warm_arrival: warm,3031
..FloorState::default()3132
};33
+ self.realise(true);3234
self.reset_eq(STARTING_EQ);3335
self.standing.suspicion = 0;3436
self.shed(FRICTION_FLOOR);@@ -43,8 +45,15 @@ impl Run {
4345
self.touch();4446
}4547
48
+ pub(super) fn check_clock(&mut self) -> Result<(), Problem> {49
+ if self.floor.timed() && self.here.turns + 1 >= FLOOR_LIMIT {50
+ return Err(self.overstay());51
+ }52
+ Ok(())53
+ }54
+4655
/// A floor past its limit either ends the run or moves you along, shamed and deflated.47
- pub(super) fn overstay(&mut self) -> Problem {56
+ fn overstay(&mut self) -> Problem {4857
self.touch();4958
if self.chance(5059
self.decision(@@ -355,3 +355,50 @@ async fn ice_treats_a_burn_even_through_priapism() {
355355
assert_eq!(header(&iced, "x-eq"), "80");356356
assert!(warnings(&iced).iter().all(|w| !w.contains("friction burn")));357357
}358
+359
+#[tokio::test]360
+async fn leaving_a_floor_delivers_every_queued_gain() {361
+ let app = seeded_with(Floor::ChangingRoom, |run| {362
+ run.here.turns = 6;363
+ run.body.pending.push(crate::game::Pending {364
+ due: run.turn + 4,365
+ length: 0.2,366
+ girth: 0.1,367
+ foreskin: 0.0,368
+ });369
+ });370
+ let left = send(&app, "POST", "/runs/cafebabe/floor", &[], "").await;371
+ assert_eq!(left.status(), StatusCode::SEE_OTHER);372
+ assert_eq!(373
+ header(&left, "x-gains-arrived"),374
+ "length=0.200, girth=0.100"375
+ );376
+}377
+378
+#[tokio::test]379
+async fn an_overstayed_jelq_spends_no_lube() {380
+ let app = seeded_with(Floor::ChangingRoom, |run| {381
+ run.body.eq = 60;382
+ run.here.turns = 9;383
+ run.items.lubes.baby_oil = 1;384
+ });385
+ let late = send(386
+ &app,387
+ "JELQ",388
+ "/runs/cafebabe?technique=wet",389
+ &[("content-type", "application/baby-oil")],390
+ "",391
+ )392
+ .await;393
+ assert!([StatusCode::GONE, StatusCode::REQUEST_TIMEOUT].contains(&late.status()));394
+ if late.status() == StatusCode::REQUEST_TIMEOUT {395
+ let held = json(send(&app, "GET", "/runs/cafebabe/inventory", &[], "").await).await;396
+ assert!(397
+ held["items"]398
+ .as_array()399
+ .unwrap()400
+ .iter()401
+ .any(|i| i["name"] == "baby-oil")402
+ );403
+ }404
+}