an http based roguelike rpg about jelqing
| 1 | use std::{ |
| 2 | ops::ControlFlow, |
| 3 | time::{Duration, SystemTime}, |
| 4 | }; |
| 5 | |
| 6 | use axum::http::{StatusCode, header}; |
| 7 | use serde_json::json; |
| 8 | |
| 9 | use super::{ |
| 10 | Floor, Outcome, Run, |
| 11 | balance::{ |
| 12 | EDGE_EQ, EDGE_GIRTH, EDGE_LOOP, ENERGY_STAMINA, HOTEL_STRESS, ICE_EQ, PRIAPISM_EQ, |
| 13 | PRIAPISM_TURNS, PUMP_GAIN, SHOWER_EQ, SUPPLEMENT_WORKS, TOWELLESS_EQ, WARMUP_EQ, |
| 14 | }, |
| 15 | }; |
| 16 | use crate::problem::Problem; |
| 17 | |
| 18 | const WEEK: Duration = Duration::from_secs(7 * 24 * 60 * 60); |
| 19 | |
| 20 | impl Run { |
| 21 | pub(super) fn warm_up(&mut self) -> Result<Outcome, Problem> { |
| 22 | if self.here.towel_warmed && !self.here.kitchen.kettle_boiled { |
| 23 | return Err(Problem::new( |
| 24 | StatusCode::CONFLICT, |
| 25 | "towel-cold", |
| 26 | "The towel has gone cold. It warms once per floor, unless someone boils the kettle.", |
| 27 | )); |
| 28 | } |
| 29 | if self.floor == Floor::Hotel |
| 30 | && self.chance( |
| 31 | self.decision( |
| 32 | "hotel-stress", |
| 33 | "Has the hotel's plumbing given up for the week?", |
| 34 | ), |
| 35 | HOTEL_STRESS, |
| 36 | ) |
| 37 | { |
| 38 | return Err(Problem::new( |
| 39 | StatusCode::SERVICE_UNAVAILABLE, |
| 40 | "service-unavailable", |
| 41 | "Service is unavailable. The server estimates next week. The server is being pessimistic.", |
| 42 | ) |
| 43 | .header( |
| 44 | header::RETRY_AFTER, |
| 45 | httpdate::fmt_http_date(SystemTime::now() + WEEK), |
| 46 | )); |
| 47 | } |
| 48 | let (boost, detail) = if self.floor == Floor::Bathroom { |
| 49 | ( |
| 50 | SHOWER_EQ, |
| 51 | "Warmed up. The shower is hot. Your brother's will not be.", |
| 52 | ) |
| 53 | } else if self.here.towel_stolen { |
| 54 | (TOWELLESS_EQ, "Warmed up, poorly. The cat has the towel.") |
| 55 | } else { |
| 56 | (WARMUP_EQ, "Warmed up. The towel has cooled.") |
| 57 | }; |
| 58 | self.touch(); |
| 59 | self.step_away(); |
| 60 | if self.here.towel_warmed { |
| 61 | self.here.kitchen.kettle_boiled = false; |
| 62 | } |
| 63 | self.here.towel_warmed = true; |
| 64 | self.body.eq = (self.body.eq + boost).min(100); |
| 65 | Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail }))) |
| 66 | } |
| 67 | |
| 68 | pub(super) fn ice(&mut self) -> Result<Outcome, Problem> { |
| 69 | if self.items.stores.ice_packs == 0 { |
| 70 | return Err(Problem::new( |
| 71 | StatusCode::FAILED_DEPENDENCY, |
| 72 | "no-ice", |
| 73 | "The ice technique depends on an ice pack. There are none.", |
| 74 | )); |
| 75 | } |
| 76 | self.touch(); |
| 77 | self.step_away(); |
| 78 | self.items.stores.ice_packs -= 1; |
| 79 | if self.body.conditions.priapism_turns > 0 { |
| 80 | return Ok(Outcome::json( |
| 81 | StatusCode::OK, |
| 82 | json!({ "detail": "The ice has no effect. The condition persists. The ice pack is gone." }), |
| 83 | )); |
| 84 | } |
| 85 | self.body.eq = (self.body.eq - ICE_EQ).max(0); |
| 86 | let detail = if self.body.conditions.friction_burn { |
| 87 | self.body.conditions.friction_burn = false; |
| 88 | "The friction burn is treated. EQ has been reduced. The ice pack is gone." |
| 89 | } else { |
| 90 | "EQ has been reduced. The ice pack is gone." |
| 91 | }; |
| 92 | Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail }))) |
| 93 | } |
| 94 | |
| 95 | pub(super) fn energy(&mut self) -> Result<Outcome, Problem> { |
| 96 | if self.items.stores.energy_drinks == 0 { |
| 97 | return Err(Problem::new( |
| 98 | StatusCode::FAILED_DEPENDENCY, |
| 99 | "no-energy-drink", |
| 100 | "The energy technique depends on an energy drink.", |
| 101 | )); |
| 102 | } |
| 103 | self.items.stores.energy_drinks -= 1; |
| 104 | self.step_away(); |
| 105 | self.body.exhausted = false; |
| 106 | self.body.stamina = (self.body.stamina + ENERGY_STAMINA).min(self.max_stamina()); |
| 107 | self.touch(); |
| 108 | Ok(Outcome::json( |
| 109 | StatusCode::OK, |
| 110 | json!({ "detail": "The energy arrives before the regret. Stamina +3." }), |
| 111 | )) |
| 112 | } |
| 113 | |
| 114 | pub(super) fn supplement(&mut self) -> Result<Outcome, Problem> { |
| 115 | if self.items.stores.supplements == 0 { |
| 116 | return Err(Problem::new( |
| 117 | StatusCode::FAILED_DEPENDENCY, |
| 118 | "no-supplement", |
| 119 | "The supplement technique depends on a supplement.", |
| 120 | )); |
| 121 | } |
| 122 | self.items.stores.supplements -= 1; |
| 123 | self.step_away(); |
| 124 | self.touch(); |
| 125 | if self.chance( |
| 126 | self.decision( |
| 127 | "supplement", |
| 128 | "Does the supplement from Pump-Bro actually work?", |
| 129 | ), |
| 130 | SUPPLEMENT_WORKS, |
| 131 | ) { |
| 132 | self.body.stamina = self.max_stamina(); |
| 133 | self.body.conditions.priapism_turns = PRIAPISM_TURNS; |
| 134 | self.body.eq = self.body.eq.max(PRIAPISM_EQ); |
| 135 | return Ok(Outcome::json( |
| 136 | StatusCode::OK, |
| 137 | json!({ "detail": "The upstream returned a valid response. It is still returning it. Stamina is fully restored." }), |
| 138 | )); |
| 139 | } |
| 140 | Err(Problem::new( |
| 141 | StatusCode::BAD_GATEWAY, |
| 142 | "bad-gateway", |
| 143 | "The upstream returned an invalid response.", |
| 144 | ) |
| 145 | .with("upstream", "Pump-Bro")) |
| 146 | } |
| 147 | |
| 148 | pub(super) fn edge(&mut self) -> Result<Outcome, Problem> { |
| 149 | self.chafe(None, 5, false); |
| 150 | self.commit(5, None, 1); |
| 151 | if let ControlFlow::Break(interrupted) = self.hazards(5)? { |
| 152 | return Ok(interrupted); |
| 153 | } |
| 154 | self.here.edges += 1; |
| 155 | self.body.eq = (self.body.eq + EDGE_EQ).min(100); |
| 156 | self.body.girth += EDGE_GIRTH; |
| 157 | if self.here.edges >= EDGE_LOOP { |
| 158 | self.here.edges = 0; |
| 159 | self.exhaust(); |
| 160 | return Err(Problem::new( |
| 161 | StatusCode::LOOP_DETECTED, |
| 162 | "edging-loop", |
| 163 | "Edging has looped. The server detected an infinite loop and terminated it.", |
| 164 | )); |
| 165 | } |
| 166 | Ok(Outcome::json( |
| 167 | StatusCode::OK, |
| 168 | json!({ "detail": "Edged. EQ is rising. Nothing else is.", "edges": self.here.edges }), |
| 169 | )) |
| 170 | } |
| 171 | |
| 172 | pub(super) fn pump(&mut self) -> Result<Outcome, Problem> { |
| 173 | self.commit(4, None, 1); |
| 174 | if let ControlFlow::Break(interrupted) = self.hazards(4)? { |
| 175 | return Ok(interrupted); |
| 176 | } |
| 177 | self.here.temp_length += PUMP_GAIN; |
| 178 | Ok(Outcome::json( |
| 179 | StatusCode::TEMPORARY_REDIRECT, |
| 180 | json!({ "detail": "Gains applied. They are temporary.", "length": PUMP_GAIN }), |
| 181 | ) |
| 182 | .header(header::LOCATION, self.path("/length"))) |
| 183 | } |
| 184 | } |