atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0

Commit 72cdd1267600

tarball

atagen <boss@atagen.co>2026-10-14 11:16 UTC

3 files changed+51-7
game: give each shop purchase a receipt, and own up to the self-report when a ruler is bought

A bought ruler swapped X-Self-Reported-Length for X-Length behind a generic 'Purchased. No refunds.', so the one purchase that rewrites a stat never said so. The drawer and forum rulers already announce themselves.
Modified file: src/game/items.rs+21 additions
@@ -138,6 +138,27 @@ impl Item {
138138 }
139139 }
140140
141+ pub fn receipt(self) -> &'static str {
142+ match self {
143+ Self::PumpPro => "The box says professional. The pump inside is the same pump.",
144+ Self::Extender => "It is fitted. It will be noticed when you sit down.",
145+ Self::Ring => "It is snug. Pump-Bro still does not discuss the risks.",
146+ Self::DoorLock => "One door lock, good for one door, once.",
147+ Self::DndSign => "The sign is on the handle. Housekeeping can read.",
148+ Self::Blinds => "The blinds are down. Across the street, a lens is lowered.",
149+ Self::Ruler => "Pump-Bro looks away while you use it.",
150+ Self::IcePack => {
151+ "It came out of the chest freezer behind the counter. It is very cold."
152+ }
153+ Self::CageKey => "The cage is open. Pump-Bro does not ask, and neither should you.",
154+ Self::BabyOil => "Pump-Bro buys it in bulk. He does not have a baby.",
155+ Self::CoconutOil => "It is solid. It will not stay that way.",
156+ Self::Lotion => "Unscented, allegedly.",
157+ Self::Vaseline => "Everyone at the counter saw.",
158+ _ => "",
159+ }
160+ }
161+
141162 pub fn price(self) -> Option<u32> {
142163 match self {
143164 Self::Pump => Some(10),
Modified file: src/game/shop.rs+17 additions−7 deletions
@@ -1,3 +1,5 @@
1+use std::fmt::Write as _;
2+
13 use axum::http::{StatusCode, header};
24 use serde_json::{Value, json};
35
@@ -96,14 +98,22 @@ impl Run {
9698 "The supplement is still shipping. You have been charged. It may arrive.",
9799 ));
98100 }
99- Ok(Outcome::json(
100- StatusCode::CREATED,
101- json!({ "detail": "Purchased. No refunds." }),
101+ let mut detail = format!("Purchased. No refunds. {}", item.receipt())
102+ .trim_end()
103+ .to_owned();
104+ if item == Item::Ruler {
105+ let _ = write!(
106+ detail,
107+ " You now know. The length you had been reporting was {:.1} inches generous.",
108+ self.body.inflation
109+ );
110+ }
111+ Ok(
112+ Outcome::json(StatusCode::CREATED, json!({ "detail": detail })).header(
113+ header::LOCATION,
114+ self.path(&format!("/inventory/{}", item.slug())),
115+ ),
102116 )
103- .header(
104- header::LOCATION,
105- self.path(&format!("/inventory/{}", item.slug())),
106- ))
107117 }
108118
109119 pub fn drop_item(&mut self, item: Item) -> Result<Outcome, Problem> {
Modified file: src/tests/floors.rs+13 additions
@@ -525,3 +525,16 @@ async fn the_forum_teaches_breathing_before_uli() {
525525 "{breathing}/100 learned to breathe on the branch floor"
526526 );
527527 }
528+
529+#[tokio::test]
530+async fn a_bought_ruler_owns_up_to_the_self_report() {
531+ let app = seeded_with(Floor::ChangingRoom, |run| {
532+ run.standing.cash = 100;
533+ run.body.inflation = 1.2;
534+ });
535+ let bought = send(&app, "POST", "/runs/cafebabe/shop/ruler", &[], "").await;
536+ assert_eq!(bought.status(), StatusCode::CREATED);
537+ assert!(bought.headers().contains_key("x-length"));
538+ let detail = json(bought).await["detail"].as_str().unwrap().to_owned();
539+ assert!(detail.contains("1.2 inches generous"), "{detail}");
540+}