an http based roguelike rpg about jelqing
Commit 7d3fb63208bf
tarball13 files changed+82-22
@@ -60,10 +60,11 @@ impl Run {
6060
if self.chance(ZIP) {6161
return Err(self.end(Cause::Zip));6262
}63
+ let left = self.floor.name();6364
let reverted = self.here.temp_length;6465
self.next_floor(true);6566
let mut body = json!({66
- "detail": format!("You have arrived at {}.", self.floor.name()),67
+ "detail": format!("You leave {left}. You have arrived at {}.", self.floor.name()),6768
"note": self.floor.note(),6869
});6970
if reverted > 0.0 {@@ -109,14 +109,14 @@ impl Run {
109109
self.touch();110110
if self.items.door_locks > 0 {111111
self.items.door_locks -= 1;112
- self.notices.push("the flatmate tried the door");112
+ self.notices.push("your brother tried the door");113113
return Ok(());114114
}115
- self.here.flatmate_told = true;115
+ self.here.brother_told = true;116116
Err(Problem::new(117117
StatusCode::CONFLICT,118
- "flatmate",119
- "The flatmate needs the bathroom. He will mention this to people.",118
+ "brother",119
+ "Your brother needs the bathroom. He will tell Mum.",120120
))121121
}122122
Floor::ChangingRoom => {@@ -10,8 +10,23 @@ use super::{
1010
};1111
use crate::problem::Problem;1212
13
-const EXCUSES: [&str; 3] = ["changing", "homework", "pulled a muscle"];14
-const CHRISTMAS_EXCUSES: [&str; 3] = ["on a call", "wrapping presents", "folding the washing"];13
+const EXCUSES: [&str; 7] = [14
+ "changing",15
+ "homework",16
+ "pulled a muscle",17
+ "tidying",18
+ "the washing up",19
+ "fed the cat",20
+ "revising",21
+];22
+const CHRISTMAS_EXCUSES: [&str; 6] = [23
+ "on a call",24
+ "wrapping presents",25
+ "folding the washing",26
+ "hanging the lights",27
+ "helping Nan",28
+ "peeling sprouts",29
+];1530
const TRUTHS: [&str; 3] = ["the truth", "truth", "honesty"];1631
const TRUE_WHEN_PULLED: &str = "pulled a muscle";1732
@@ -206,6 +221,8 @@ impl Run {
206221
fn leave_door(&mut self) {207222
self.mum = None;208223
self.suspicion = 0;224
+ self.notices225
+ .push("Mum seems satisfied. She takes the washing with her.");209226
self.touch();210227
}211228
}@@ -83,7 +83,7 @@ impl Item {
8383
}8484
Self::IcePack => "Lowers EQ by 40. Use technique=ice.",8585
Self::Ruler => "Measures honestly. The length resource now carries a Content-Length.",86
- Self::DoorLock => "Keeps the flatmate out once.",86
+ Self::DoorLock => "Keeps your brother out once.",8787
Self::DndSign => "Works on housekeeping. Mum does not read signs.",8888
Self::Blinds => "The neighbour has a good camera. The blinds are better.",8989
Self::Pump => "A pump.",@@ -15,7 +15,7 @@ mod utility;
1515
1616
pub use body::PatchOp;1717
pub use forum::{ELDER_LAST_MODIFIED, FORUM_THREAD, forum_thread};18
-pub use items::{Item, Items};18
+pub use items::{Item, Items, SHOP};1919
pub use outcome::{Outcome, Payload};2020
pub use state::{2121
Cause, Clinic, Conditions, Elder, Ending, Floor, FloorState, Grip, MumVisit, Observer, Story,@@ -46,7 +46,7 @@ impl Floor {
4646
pub fn note(self) -> &'static str {4747
match self {4848
Self::Bedroom => "The door does not lock. The cat is present.",49
- Self::Bathroom => "The water is hot. The flatmate showers at eight.",49
+ Self::Bathroom => "The water is hot. Your brother showers at eight.",5050
Self::ChangingRoom => {5151
"It is cold. Discretion is advised. Everyone here is lying. Pump-Bro is by the lockers."5252
}@@ -136,7 +136,8 @@ pub struct FloorState {
136136
pub temp_length: f64,137137
pub teapot: bool,138138
pub towel_stolen: bool,139
- pub flatmate_told: bool,139
+ pub brother_told: bool,140
+ pub shopping: bool,140141
pub wifi_accepted: bool,141142
}142143
@@ -307,6 +307,7 @@ impl Run {
307307
self.touch();308308
self.turn += 1;309309
self.here.turns += 1;310
+ self.here.shopping = false;310311
self.realise();311312
if self.items.extender {312313
self.length += EXTENDER_GAIN;@@ -369,7 +370,10 @@ impl Run {
369370
return;370371
}371372
let summoned = was_below && self.suspicion == SUSPICION_LIMIT;372
- let visits = u32::from(self.suspicion) + 1;373
+ let mut visits = u32::from(self.suspicion) + 1;374
+ if self.here.brother_told {375
+ visits *= 2;376
+ }373377
if summoned || self.rng.random_ratio(visits, super::balance::MUM_VISIT) {374378
let errand = self.errand(summoned);375379
let nonce = self.fresh_nonce();@@ -533,13 +537,13 @@ impl Run {
533537
534538
fn gossip(&mut self, mut outcome: Outcome) -> Outcome {535539
let reportable = matches!(outcome.status.as_u16(), 200 | 202 | 206);536
- if !self.here.flatmate_told || !reportable {540
+ if !self.here.brother_told || !reportable {537541
return outcome;538542
}539
- self.here.flatmate_told = false;543
+ self.here.brother_told = false;540544
outcome.status = StatusCode::ALREADY_REPORTED;541545
if let Payload::Json(body) = &mut outcome.payload {542
- body["detail"] = json!("The flatmate has already told everyone.");546
+ body["detail"] = json!("Your brother has already told Mum.");543547
}544548
outcome545549
}@@ -33,7 +33,7 @@ impl Run {
3333
let (boost, detail) = if self.floor == Floor::Bathroom {3434
(3535
SHOWER_EQ,36
- "Warmed up. The shower is hot. The flatmate's will not be.",36
+ "Warmed up. The shower is hot. Your brother's will not be.",3737
)3838
} else if self.here.towel_stolen {3939
(TOWELLESS_EQ, "Warmed up, poorly. The cat has the towel.")@@ -11,7 +11,7 @@ use tokio::io::AsyncWriteExt;
1111
1212
use super::{AppState, ctx::Ctx, meta, middleware::Compress, resource::Resource, runs};1313
use crate::{14
- game::{Elder, FORUM_THREAD, Floor, Run, Story, balance::SUSPICION_LIMIT},14
+ game::{Elder, FORUM_THREAD, Floor, Run, SHOP, Story, balance::SUSPICION_LIMIT},1515
headers::{1616
X_CASH, X_EQ, X_FLOOR, X_FORESKIN, X_GIRTH, X_LENGTH, X_OBSERVED_BY,1717
X_SELF_REPORTED_LENGTH, X_SHAME, X_STAMINA, X_SUSPICION,@@ -156,6 +156,19 @@ fn links(run: &Run, now: Instant) -> String {
156156
run.path("")157157
)158158
}))159
+ .chain(run.here.shopping.then(|| {160
+ SHOP.iter()161
+ .filter_map(|item| {162
+ let price = item.price()?;163
+ Some(format!(164
+ "<{}>; rel=\"{REL_BASE}shop-item\"; title=\"{} for {price}\"; allow=\"POST\"",165
+ run.path(&format!("/shop/{}", item.slug())),166
+ item.slug()167
+ ))168
+ })169
+ .collect::<Vec<_>>()170
+ .join(", ")171
+ }))159172
.collect::<Vec<_>>()160173
.join(", ")161174
}@@ -450,7 +450,11 @@ fn leave_floor(run: &mut Run, _: &Ctx) -> Response {
450450
}451451
452452
fn get_shop(run: &mut Run, _: &Ctx) -> Response {453
- respond(run.shop_view().map(Json))453
+ let view = run.shop_view();454
+ if view.is_ok() {455
+ run.here.shopping = true;456
+ }457
+ respond(view.map(Json))454458
}455459
456460
fn buy(run: &mut Run, ctx: &Ctx) -> Response {@@ -25,8 +25,8 @@ const EN_US: &[(&str, &str)] = &[
2525
("folding the washing", "folding laundry"),2626
("your washing", "your laundry"),2727
("changing room", "locker room"),28
- ("flatmate", "roommate"),2928
("authorisation", "authorization"),29
+ ("revising", "studying"),3030
("neighbour", "neighbor"),3131
("cupboard", "pantry"),3232
("Christmas", "Thanksgiving"),@@ -46,7 +46,6 @@ const EN_AU: &[(&str, &str)] = &[
4646
),4747
("wrapping presents", "getting ice from the servo"),4848
("changing room", "change rooms"),49
- ("flatmate", "housemate"),5049
("cupboard", "pantry"),5150
("zip", "fly"),5251
("£", "$"),@@ -931,6 +931,18 @@ async fn a_door_lock_says_when_it_was_used() {
931931
.headers()932932
.get_all("warning")933933
.iter()934
- .any(|w| w.to_str().unwrap().contains("the flatmate tried the door"))934
+ .any(|w| w.to_str().unwrap().contains("your brother tried the door"))935935
);936936
}937
+938
+#[tokio::test]939
+async fn shopping_survives_browsing_but_not_jelqing() {940
+ let app = seeded_with(Floor::ChangingRoom, |run| run.cash = 50);941
+ send(&app, "GET", "/runs/cafebabe/shop", &[], "").await;942
+ let view = send(&app, "GET", "/runs/cafebabe", &[], "").await;943
+ assert!(header(&view, "link").contains("shop-item"));944
+ let jelq = send(&app, "JELQ", "/runs/cafebabe?technique=warmup", &[], "").await;945
+ assert_ne!(jelq.status(), StatusCode::METHOD_NOT_ALLOWED);946
+ let after = send(&app, "GET", "/runs/cafebabe", &[], "").await;947
+ assert!(!header(&after, "link").contains("shop-item"));948
+}@@ -25,6 +25,13 @@ Language <select id="locale"><option>en-GB</option><option>en-US</option><option
2525
<form id="form">2626
<h3>Method</h3>2727
<div id="methods"></div>28
+<datalist id="excuses">29
+<option value="changing"><option value="homework"><option value="pulled a muscle">30
+<option value="tidying"><option value="the washing up"><option value="fed the cat">31
+<option value="revising"><option value="on a call"><option value="wrapping presents">32
+<option value="folding the washing"><option value="hanging the lights">33
+<option value="helping Nan"><option value="peeling sprouts"><option value="the truth">34
+</datalist>2835
2936
<h3>Request</h3>3037
<table id="fields" cellpadding="2"></table>@@ -94,12 +101,13 @@ const BY_TARGET = {
94101
};95102
const GROUPS = [96103
["play", "Play"],104
+ ["shop", "Shop"],97105
["you", "You"],98106
["said", "From the last response"],99107
["reading", "Reading"],100108
["meta", "Meta"],101109
];102
-const GROUP_OF = { run: "play", floor: "play", drawer: "play", shop: "play", portal: "play", elder: "play", diagnosis: "play", length: "you", foreskin: "you", grip: "you", inventory: "you", footage: "you", techniques: "reading", thread: "reading" };110
+const GROUP_OF = { run: "play", floor: "play", drawer: "play", shop: "play", "shop-item": "shop", kitchen: "play", portal: "play", elder: "play", diagnosis: "play", length: "you", foreskin: "you", grip: "you", inventory: "you", footage: "you", techniques: "reading", thread: "reading" };103111
const NAMES = { run: "the run", thread: "the 2009 forum thread" };104112
const GLOBALS = [105113
{ key: "runs", group: "play", name: "start a new run", path: "/runs", methods: ["POST"] },@@ -289,6 +297,7 @@ function renderFields() {
289297
}290298
input.oninput = () => { values[name] = input.value; };291299
input.onchange = () => { values[name] = input.value; if (choices) renderFields(); };300
+ if (name === "authorization") input.setAttribute("list", "excuses");292301
table.append(el("tr", {}, el("td", { align: "right", valign: "top", textContent: f.label }), el("td", {}, input)));293302
}294303
const extra = el("textarea", { rows: 3, cols: 40, value: raw("extra"), placeholder: FIELDS.extra.placeholder });