an http based roguelike rpg about jelqing
Commit f3a1913c1299
tarball9 files changed+104-7
@@ -1,4 +1,6 @@
11
pub const MAX_STAMINA: i32 = 10;2
+/// At or below this, HOLD opens up and the gauge reads as dangerous.3
+pub const LOW_STAMINA: i32 = 2;24
pub const PULLED_STAMINA: i32 = 7;35
pub const EDGE_LOOP: u8 = 3;46
pub const ENERGY_STAMINA: i32 = 3;@@ -0,0 +1,59 @@
1
+use super::{2
+ Arousal, Run,3
+ balance::{FRICTION_BURN_AT, FRICTION_MAX, LOW_STAMINA, SUSPICION_LIMIT},4
+};5
+6
+/// How close a gauge is to hurting the run, so a client can flag it without knowing the balance.7
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]8
+pub enum Danger {9
+ Dangerous,10
+ Critical,11
+}12
+13
+impl Danger {14
+ pub fn name(self) -> &'static str {15
+ match self {16
+ Self::Dangerous => "dangerous",17
+ Self::Critical => "critical",18
+ }19
+ }20
+}21
+22
+impl Arousal {23
+ fn danger(self) -> Option<Danger> {24
+ match self {25
+ Self::Cold | Self::Swollen | Self::Throbbing => Some(Danger::Dangerous),26
+ Self::Straining | Self::Imminent | Self::Priapic => Some(Danger::Critical),27
+ Self::Flaccid | Self::Warm | Self::Ideal => None,28
+ }29
+ }30
+}31
+32
+impl Run {33
+ pub(super) fn dangers(&self) -> Vec<(&'static str, Danger)> {34
+ let friction = if self.body.friction >= FRICTION_MAX || self.body.conditions.friction_burn {35
+ Some(Danger::Critical)36
+ } else {37
+ (self.body.friction >= FRICTION_BURN_AT).then_some(Danger::Dangerous)38
+ };39
+ let stamina = if self.body.stamina <= 0 {40
+ Some(Danger::Critical)41
+ } else {42
+ (self.body.stamina <= LOW_STAMINA).then_some(Danger::Dangerous)43
+ };44
+ let suspicion = if self.standing.suspicion + 1 >= SUSPICION_LIMIT {45
+ Some(Danger::Critical)46
+ } else {47
+ (self.standing.suspicion + 2 >= SUSPICION_LIMIT).then_some(Danger::Dangerous)48
+ };49
+ [50
+ ("friction", friction),51
+ ("eq", self.arousal().danger()),52
+ ("stamina", stamina),53
+ ("suspicion", suspicion),54
+ ]55
+ .into_iter()56
+ .filter_map(|(gauge, danger)| Some((gauge, danger?)))57
+ .collect()58
+ }59
+}@@ -3,7 +3,7 @@ use std::time::Instant;
33
use serde_json::{Value, json};44
55
use super::{6
- Arousal, Arrival, Elder, Excuse, Floor, Grip, Item, Observer, Run, Story, Technique,6
+ Arousal, Arrival, Danger, Elder, Excuse, Floor, Grip, Item, Observer, Run, Story, Technique,77
balance::{DANGER_EQ, FLOOR_LIMIT},88
techniques::{Training, Utility},99
};@@ -27,6 +27,7 @@ pub struct Gauges {
2727
pub(crate) turn: u32,2828
pub(crate) floor_turns: u32,2929
pub(crate) floor_limit: Option<u32>,30
+ pub(crate) dangers: Vec<(&'static str, Danger)>,3031
pub(crate) observed: Option<Observer>,3132
pub(crate) mum_challenge: Option<String>,3233
pub(crate) warnings: Vec<&'static str>,@@ -86,6 +87,7 @@ impl Run {
8687
turn: self.turn,8788
floor_turns: self.here.turns,8889
floor_limit: self.floor.timed().then_some(FLOOR_LIMIT),90
+ dangers: self.dangers(),8991
observed: self.observed,9092
mum_challenge: self.mum_challenge(),9193
warnings,@@ -5,6 +5,7 @@ mod cause;
55
mod checks;66
mod clinic;77
mod credentials;8
+mod danger;89
mod dispatch;910
mod drawer;1011
mod floor;@@ -36,6 +37,7 @@ pub use body::Pending;
3637
pub use body::{Arrival, Body, Grip};3738
pub use cause::{Cause, Ending, RunStatus};3839
pub use clinic::Clinic;40
+pub use danger::Danger;3941
pub use floor::{Floor, FloorState, Observer};4042
pub use forum::{Branch, ELDER_LAST_MODIFIED, Elder, FORUM_THREAD, Online, Story, forum_thread};4143
pub use gauges::{Gauges, Shown};@@ -1,8 +1,8 @@
11
use super::{22
Notice, Outcome, Run, Story,33
balance::{4
- DANGER_EQ, FRICTION_HOLD, FRICTION_MEDITATE, HOLD_EQ, MEDITATION_EQ, REFLECTION,5
- RUPTURE_HOLD,4
+ DANGER_EQ, FRICTION_HOLD, FRICTION_MEDITATE, HOLD_EQ, LOW_STAMINA, MEDITATION_EQ,5
+ REFLECTION, RUPTURE_HOLD,66
},77
};88
use crate::problem::Problem;@@ -11,7 +11,7 @@ use serde_json::json;
1111
1212
impl Run {1313
pub fn can_hold(&self) -> bool {14
- self.hotter_than(DANGER_EQ) || self.body.stamina <= 214
+ self.hotter_than(DANGER_EQ) || self.body.stamina <= LOW_STAMINA1515
}1616
1717
pub fn hold(&mut self) -> Result<Outcome, Problem> {@@ -2,6 +2,7 @@ use axum::http::HeaderName;
22
33
pub const SERVER_TIMING: HeaderName = HeaderName::from_static("server-timing");44
pub const X_CASH: HeaderName = HeaderName::from_static("x-cash");5
+pub const X_DANGER: HeaderName = HeaderName::from_static("x-danger");56
pub const X_DIAGNOSIS: HeaderName = HeaderName::from_static("x-diagnosis");67
pub const X_EQ: HeaderName = HeaderName::from_static("x-eq");78
pub const X_FLOOR: HeaderName = HeaderName::from_static("x-floor");@@ -16,8 +16,8 @@ use crate::{
1616
balance::{FRICTION_MAX, SUSPICION_LIMIT},1717
},1818
headers::{19
- X_CASH, X_EQ, X_FLOOR, X_FLOOR_TURN, X_FORESKIN, X_FRICTION, X_GAINS_ARRIVED, X_GIRTH,20
- X_LENGTH, X_OBSERVED_BY, X_RUN_OVER, X_SELF_REPORTED_LENGTH, X_SHAME, X_STAMINA,19
+ X_CASH, X_DANGER, X_EQ, X_FLOOR, X_FLOOR_TURN, X_FORESKIN, X_FRICTION, X_GAINS_ARRIVED,20
+ X_GIRTH, X_LENGTH, X_OBSERVED_BY, X_RUN_OVER, X_SELF_REPORTED_LENGTH, X_SHAME, X_STAMINA,2121
X_SUSPICION, X_TURN,2222
},2323
locale::Locale,@@ -175,6 +175,14 @@ fn stats(gauges: &Gauges, shame: u32) -> Vec<(HeaderName, String)> {
175175
}),176176
),177177
];178
+ if !gauges.dangers.is_empty() {179
+ let dangers: Vec<String> = gauges180
+ .dangers181
+ .iter()182
+ .map(|(gauge, danger)| format!("{gauge}={}", danger.name()))183
+ .collect();184
+ stats.push((X_DANGER, dangers.join(", ")));185
+ }178186
if let Some(observer) = gauges.observed {179187
stats.push((X_OBSERVED_BY, observer.name().into()));180188
}@@ -466,3 +466,22 @@ async fn eq_is_named_rather_than_numbered() {
466466
assert_eq!(header(&view, "x-eq"), named, "{eq} with {setup:?}");467467
}468468
}469
+470
+#[tokio::test]471
+async fn gauges_near_the_edge_are_flagged() {472
+ let calm = seeded_with(Floor::Bedroom, |run| run.body.eq = 60);473
+ let view = send(&calm, "GET", "/runs/cafebabe", &[], "").await;474
+ assert!(view.headers().get("x-danger").is_none());475
+476
+ let tense = seeded_with(Floor::Bedroom, |run| {477
+ run.body.eq = 93;478
+ run.body.friction = 6;479
+ run.body.stamina = 2;480
+ run.standing.suspicion = 4;481
+ });482
+ let view = send(&tense, "GET", "/runs/cafebabe", &[], "").await;483
+ assert_eq!(484
+ header(&view, "x-danger"),485
+ "friction=dangerous, eq=critical, stamina=dangerous, suspicion=critical"486
+ );487
+}@@ -76,6 +76,7 @@ const MEASURED = ["x-length", "Length"];
7676
const GROWTH = new Set(["x-length", "x-self-reported-length", "x-girth", "x-foreskin"]);7777
const BETTER = { "x-length": 1, "x-self-reported-length": 1, "x-girth": 1, "x-foreskin": 1, "x-friction": -1, "x-stamina": 1, "x-suspicion": -1, "x-shame": -1, "x-cash": 1, "x-floor": 1 };7878
const AROUSAL = ["Flaccid", "Cold", "Warm", "Ideal", "Swollen", "Throbbing", "Straining", "Imminent"];79
+const DANGER_COLOURS = { dangerous: "#b8860b", critical: "red" };7980
let lastStats = {};8081
const QUIET = new Set(["link", "set-cookie", "server", "x-powered-by", "cache-control", "content-length", "content-type", "date"].concat(STATS.map(s => s[0]), MEASURED[0]));8182
@@ -217,6 +218,7 @@ function chooseTarget(t) {
217218
function renderStats(headers) {218219
const table = $("stats");219220
table.replaceChildren();221
+ const dangers = Object.fromEntries((headers && headers["x-danger"] || "").split(",").filter(Boolean).map(d => d.trim().split("=")));220222
const stats = STATS.map(s => s[0] === "x-self-reported-length" && headers && MEASURED[0] in headers ? MEASURED : s);221223
for (let i = 0; i < stats.length; i += 2) {222224
const row = el("tr");@@ -226,7 +228,9 @@ function renderStats(headers) {
226228
const changed = value === undefined ? null : change(name, value);227229
if (changed) cell.append(" ", changed);228230
const measured = name === MEASURED[0] && "x-self-reported-length" in lastStats;229
- row.append(el("th", { align: "left", textContent: label, style: measured ? "color: green" : "" }), cell);231
+ const danger = DANGER_COLOURS[dangers[name.slice(2)]];232
+ const colour = danger || (measured ? "green" : "");233
+ row.append(el("th", { align: "left", textContent: label, style: colour ? "color: " + colour : "" }), cell);230234
}231235
table.append(row);232236
}