atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
b85a0d4536c3b294547a682ac046d8a38beb47a3
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/game/techniques.rs

dfa38cf4cd4batagen2026-10-10 09:17 UTC1
use axum::http::StatusCode;
dfa38cf4cd4batagen2026-10-10 09:17 UTC2
use serde_json::{Value, json};
dfa38cf4cd4batagen2026-10-10 09:17 UTC3
dfa38cf4cd4batagen2026-10-10 09:17 UTC4
use crate::problem::Problem;
dfa38cf4cd4batagen2026-10-10 09:17 UTC5
dfa38cf4cd4batagen2026-10-10 09:17 UTC6
pub const LUBRICANTS: [&str; 5] = [
dfa38cf4cd4batagen2026-10-10 09:17 UTC7
    "application/baby-oil",
dfa38cf4cd4batagen2026-10-10 09:17 UTC8
    "application/coconut-oil",
dfa38cf4cd4batagen2026-10-10 09:17 UTC9
    "application/lotion",
dfa38cf4cd4batagen2026-10-10 09:17 UTC10
    "application/vaseline",
dfa38cf4cd4batagen2026-10-10 09:17 UTC11
    "text/plain; charset=spit",
dfa38cf4cd4batagen2026-10-10 09:17 UTC12
];
dfa38cf4cd4batagen2026-10-10 09:17 UTC13
dfa38cf4cd4batagen2026-10-10 09:17 UTC14
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
dfa38cf4cd4batagen2026-10-10 09:17 UTC15
pub enum Lube {
dfa38cf4cd4batagen2026-10-10 09:17 UTC16
    BabyOil,
dfa38cf4cd4batagen2026-10-10 09:17 UTC17
    CoconutOil,
dfa38cf4cd4batagen2026-10-10 09:17 UTC18
    Lotion,
dfa38cf4cd4batagen2026-10-10 09:17 UTC19
    Vaseline,
dfa38cf4cd4batagen2026-10-10 09:17 UTC20
    Spit,
dfa38cf4cd4batagen2026-10-10 09:17 UTC21
}
dfa38cf4cd4batagen2026-10-10 09:17 UTC22
dfa38cf4cd4batagen2026-10-10 09:17 UTC23
impl Lube {
dfa38cf4cd4batagen2026-10-10 09:17 UTC24
    pub fn from_content_type(content_type: Option<&str>) -> Result<Option<Self>, Problem> {
dfa38cf4cd4batagen2026-10-10 09:17 UTC25
        let Some(raw) = content_type else {
dfa38cf4cd4batagen2026-10-10 09:17 UTC26
            return Ok(None);
dfa38cf4cd4batagen2026-10-10 09:17 UTC27
        };
dfa38cf4cd4batagen2026-10-10 09:17 UTC28
        let lowered = raw.to_ascii_lowercase();
dfa38cf4cd4batagen2026-10-10 09:17 UTC29
        let mut parts = lowered.split(';').map(str::trim);
dfa38cf4cd4batagen2026-10-10 09:17 UTC30
        let essence = parts.next().unwrap_or_default();
dfa38cf4cd4batagen2026-10-10 09:17 UTC31
        let spit = parts.any(|p| p.replace(' ', "") == "charset=spit");
dfa38cf4cd4batagen2026-10-10 09:17 UTC32
        match essence {
dfa38cf4cd4batagen2026-10-10 09:17 UTC33
            "application/baby-oil" => Ok(Some(Self::BabyOil)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC34
            "application/coconut-oil" => Ok(Some(Self::CoconutOil)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC35
            "application/lotion" => Ok(Some(Self::Lotion)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC36
            "application/vaseline" => Ok(Some(Self::Vaseline)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC37
            "text/plain" if spit => Ok(Some(Self::Spit)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC38
            e if e.starts_with("image/") || e.starts_with("video/") => Err(Problem::new(
dfa38cf4cd4batagen2026-10-10 09:17 UTC39
                StatusCode::UNSUPPORTED_MEDIA_TYPE,
dfa38cf4cd4batagen2026-10-10 09:17 UTC40
                "no",
dfa38cf4cd4batagen2026-10-10 09:17 UTC41
                "No.",
dfa38cf4cd4batagen2026-10-10 09:17 UTC42
            )),
dfa38cf4cd4batagen2026-10-10 09:17 UTC43
            _ => Err(Problem::new(
dfa38cf4cd4batagen2026-10-10 09:17 UTC44
                StatusCode::UNSUPPORTED_MEDIA_TYPE,
dfa38cf4cd4batagen2026-10-10 09:17 UTC45
                "unsupported-lubricant",
dfa38cf4cd4batagen2026-10-10 09:17 UTC46
                format!("{essence} is not a lubricant."),
dfa38cf4cd4batagen2026-10-10 09:17 UTC47
            )
dfa38cf4cd4batagen2026-10-10 09:17 UTC48
            .with("lubricants", json!(LUBRICANTS))),
dfa38cf4cd4batagen2026-10-10 09:17 UTC49
        }
dfa38cf4cd4batagen2026-10-10 09:17 UTC50
    }
dfa38cf4cd4batagen2026-10-10 09:17 UTC51
782069860236atagen2026-10-10 12:44 UTC52
    pub fn modifier(self) -> i32 {
dfa38cf4cd4batagen2026-10-10 09:17 UTC53
        match self {
dfa38cf4cd4batagen2026-10-10 09:17 UTC54
            Self::BabyOil => 1,
dfa38cf4cd4batagen2026-10-10 09:17 UTC55
            Self::CoconutOil => 3,
dfa38cf4cd4batagen2026-10-10 09:17 UTC56
            Self::Lotion => 0,
dfa38cf4cd4batagen2026-10-10 09:17 UTC57
            Self::Vaseline => 2,
dfa38cf4cd4batagen2026-10-10 09:17 UTC58
            Self::Spit => -2,
dfa38cf4cd4batagen2026-10-10 09:17 UTC59
        }
dfa38cf4cd4batagen2026-10-10 09:17 UTC60
    }
dfa38cf4cd4batagen2026-10-10 09:17 UTC61
}
dfa38cf4cd4batagen2026-10-10 09:17 UTC62
782069860236atagen2026-10-10 12:44 UTC63
/// Techniques that roll for gains.
dfa38cf4cd4batagen2026-10-10 09:17 UTC64
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
782069860236atagen2026-10-10 12:44 UTC65
pub enum Training {
782069860236atagen2026-10-10 12:44 UTC66
    Wet,
782069860236atagen2026-10-10 12:44 UTC67
    Dry,
782069860236atagen2026-10-10 12:44 UTC68
    Stretch,
782069860236atagen2026-10-10 12:44 UTC69
    Uli,
782069860236atagen2026-10-10 12:44 UTC70
}
782069860236atagen2026-10-10 12:44 UTC71
782069860236atagen2026-10-10 12:44 UTC72
/// Techniques that change EQ or spend an item without rolling.
782069860236atagen2026-10-10 12:44 UTC73
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
782069860236atagen2026-10-10 12:44 UTC74
pub enum Utility {
dfa38cf4cd4batagen2026-10-10 09:17 UTC75
    Warmup,
dfa38cf4cd4batagen2026-10-10 09:17 UTC76
    Ice,
dfa38cf4cd4batagen2026-10-10 09:17 UTC77
    Edge,
dfa38cf4cd4batagen2026-10-10 09:17 UTC78
    Pump,
dfa38cf4cd4batagen2026-10-10 09:17 UTC79
    Supplement,
0fdc47364c8catagen2026-10-11 04:45 UTC80
    Meditate,
0a0bfee8329eatagen2026-10-11 05:11 UTC81
    Energy,
782069860236atagen2026-10-10 12:44 UTC82
}
782069860236atagen2026-10-10 12:44 UTC83
782069860236atagen2026-10-10 12:44 UTC84
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
782069860236atagen2026-10-10 12:44 UTC85
pub enum Technique {
782069860236atagen2026-10-10 12:44 UTC86
    Training(Training),
782069860236atagen2026-10-10 12:44 UTC87
    Utility(Utility),
dfa38cf4cd4batagen2026-10-10 09:17 UTC88
}
dfa38cf4cd4batagen2026-10-10 09:17 UTC89
dfa38cf4cd4batagen2026-10-10 09:17 UTC90
#[derive(Clone, Copy)]
782069860236atagen2026-10-10 12:44 UTC91
pub enum LubeRule {
dfa38cf4cd4batagen2026-10-10 09:17 UTC92
    Required,
dfa38cf4cd4batagen2026-10-10 09:17 UTC93
    Forbidden,
dfa38cf4cd4batagen2026-10-10 09:17 UTC94
    Optional,
dfa38cf4cd4batagen2026-10-10 09:17 UTC95
}
dfa38cf4cd4batagen2026-10-10 09:17 UTC96
58e1fb3943e6atagen2026-10-10 13:30 UTC97
impl LubeRule {
58e1fb3943e6atagen2026-10-10 13:30 UTC98
    fn name(self) -> &'static str {
58e1fb3943e6atagen2026-10-10 13:30 UTC99
        match self {
58e1fb3943e6atagen2026-10-10 13:30 UTC100
            Self::Required => "required",
58e1fb3943e6atagen2026-10-10 13:30 UTC101
            Self::Forbidden => "forbidden",
58e1fb3943e6atagen2026-10-10 13:30 UTC102
            Self::Optional => "optional",
58e1fb3943e6atagen2026-10-10 13:30 UTC103
        }
58e1fb3943e6atagen2026-10-10 13:30 UTC104
    }
58e1fb3943e6atagen2026-10-10 13:30 UTC105
}
58e1fb3943e6atagen2026-10-10 13:30 UTC106
782069860236atagen2026-10-10 12:44 UTC107
pub struct Profile {
dfa38cf4cd4batagen2026-10-10 09:17 UTC108
    pub dc: i32,
dfa38cf4cd4batagen2026-10-10 09:17 UTC109
    pub length: f64,
dfa38cf4cd4batagen2026-10-10 09:17 UTC110
    pub girth: f64,
782069860236atagen2026-10-10 12:44 UTC111
    /// Minimum and maximum foreskin the technique tolerates. A minimum above zero means it is used.
dfa38cf4cd4batagen2026-10-10 09:17 UTC112
    pub foreskin: Option<(f64, f64)>,
dfa38cf4cd4batagen2026-10-10 09:17 UTC113
    pub foreskin_growth: f64,
dfa38cf4cd4batagen2026-10-10 09:17 UTC114
    pub lube: LubeRule,
dfa38cf4cd4batagen2026-10-10 09:17 UTC115
}
dfa38cf4cd4batagen2026-10-10 09:17 UTC116
782069860236atagen2026-10-10 12:44 UTC117
impl Training {
dfa38cf4cd4batagen2026-10-10 09:17 UTC118
    pub fn name(self) -> &'static str {
dfa38cf4cd4batagen2026-10-10 09:17 UTC119
        match self {
dfa38cf4cd4batagen2026-10-10 09:17 UTC120
            Self::Wet => "wet",
dfa38cf4cd4batagen2026-10-10 09:17 UTC121
            Self::Dry => "dry",
dfa38cf4cd4batagen2026-10-10 09:17 UTC122
            Self::Stretch => "stretch",
dfa38cf4cd4batagen2026-10-10 09:17 UTC123
            Self::Uli => "uli",
dfa38cf4cd4batagen2026-10-10 09:17 UTC124
        }
dfa38cf4cd4batagen2026-10-10 09:17 UTC125
    }
dfa38cf4cd4batagen2026-10-10 09:17 UTC126
782069860236atagen2026-10-10 12:44 UTC127
    pub fn profile(self) -> Profile {
782069860236atagen2026-10-10 12:44 UTC128
        match self {
dfa38cf4cd4batagen2026-10-10 09:17 UTC129
            Self::Wet => Profile {
dfa38cf4cd4batagen2026-10-10 09:17 UTC130
                dc: 10,
dfa38cf4cd4batagen2026-10-10 09:17 UTC131
                length: 0.010,
dfa38cf4cd4batagen2026-10-10 09:17 UTC132
                girth: 0.005,
dfa38cf4cd4batagen2026-10-10 09:17 UTC133
                foreskin: None,
dfa38cf4cd4batagen2026-10-10 09:17 UTC134
                foreskin_growth: 0.0,
dfa38cf4cd4batagen2026-10-10 09:17 UTC135
                lube: LubeRule::Required,
dfa38cf4cd4batagen2026-10-10 09:17 UTC136
            },
dfa38cf4cd4batagen2026-10-10 09:17 UTC137
            Self::Dry => Profile {
dfa38cf4cd4batagen2026-10-10 09:17 UTC138
                dc: 12,
dfa38cf4cd4batagen2026-10-10 09:17 UTC139
                length: 0.008,
dfa38cf4cd4batagen2026-10-10 09:17 UTC140
                girth: 0.003,
dfa38cf4cd4batagen2026-10-10 09:17 UTC141
                foreskin: Some((0.8, 2.0)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC142
                foreskin_growth: 0.01,
dfa38cf4cd4batagen2026-10-10 09:17 UTC143
                lube: LubeRule::Forbidden,
dfa38cf4cd4batagen2026-10-10 09:17 UTC144
            },
dfa38cf4cd4batagen2026-10-10 09:17 UTC145
            Self::Stretch => Profile {
dfa38cf4cd4batagen2026-10-10 09:17 UTC146
                dc: 11,
dfa38cf4cd4batagen2026-10-10 09:17 UTC147
                length: 0.015,
dfa38cf4cd4batagen2026-10-10 09:17 UTC148
                girth: 0.0,
dfa38cf4cd4batagen2026-10-10 09:17 UTC149
                foreskin: Some((0.0, 1.8)),
dfa38cf4cd4batagen2026-10-10 09:17 UTC150
                foreskin_growth: 0.03,
dfa38cf4cd4batagen2026-10-10 09:17 UTC151
                lube: LubeRule::Optional,
dfa38cf4cd4batagen2026-10-10 09:17 UTC152
            },
dfa38cf4cd4batagen2026-10-10 09:17 UTC153
            Self::Uli => Profile {
dfa38cf4cd4batagen2026-10-10 09:17 UTC154
                dc: 15,
dfa38cf4cd4batagen2026-10-10 09:17 UTC155
                length: 0.002,
dfa38cf4cd4batagen2026-10-10 09:17 UTC156
                girth: 0.020,
dfa38cf4cd4batagen2026-10-10 09:17 UTC157
                foreskin: None,
dfa38cf4cd4batagen2026-10-10 09:17 UTC158
                foreskin_growth: 0.0,
dfa38cf4cd4batagen2026-10-10 09:17 UTC159
                lube: LubeRule::Optional,
dfa38cf4cd4batagen2026-10-10 09:17 UTC160
            },
782069860236atagen2026-10-10 12:44 UTC161
        }
782069860236atagen2026-10-10 12:44 UTC162
    }
782069860236atagen2026-10-10 12:44 UTC163
}
782069860236atagen2026-10-10 12:44 UTC164
782069860236atagen2026-10-10 12:44 UTC165
impl Utility {
782069860236atagen2026-10-10 12:44 UTC166
    pub fn name(self) -> &'static str {
782069860236atagen2026-10-10 12:44 UTC167
        match self {
782069860236atagen2026-10-10 12:44 UTC168
            Self::Warmup => "warmup",
782069860236atagen2026-10-10 12:44 UTC169
            Self::Ice => "ice",
782069860236atagen2026-10-10 12:44 UTC170
            Self::Edge => "edge",
782069860236atagen2026-10-10 12:44 UTC171
            Self::Pump => "pump",
782069860236atagen2026-10-10 12:44 UTC172
            Self::Supplement => "supplement",
0fdc47364c8catagen2026-10-11 04:45 UTC173
            Self::Meditate => "meditate",
0a0bfee8329eatagen2026-10-11 05:11 UTC174
            Self::Energy => "energy",
782069860236atagen2026-10-10 12:44 UTC175
        }
782069860236atagen2026-10-10 12:44 UTC176
    }
782069860236atagen2026-10-10 12:44 UTC177
782069860236atagen2026-10-10 12:44 UTC178
    fn effect(self) -> &'static str {
782069860236atagen2026-10-10 12:44 UTC179
        match self {
782069860236atagen2026-10-10 12:44 UTC180
            Self::Warmup => "EQ +25. Satisfies the warm-up precondition.",
5dfebcc0ac43atagen2026-10-11 05:28 UTC181
            Self::Ice => "EQ -40. Treats a friction burn. Consumes an ice pack.",
782069860236atagen2026-10-10 12:44 UTC182
            Self::Edge => "EQ +15 and a little girth. Three in a row is a loop.",
782069860236atagen2026-10-10 12:44 UTC183
            Self::Pump => "Large gains, all temporary. Requires a pump.",
782069860236atagen2026-10-10 12:44 UTC184
            Self::Supplement => "Consumes a supplement.",
5dfebcc0ac43atagen2026-10-11 05:28 UTC185
            Self::Meditate => "EQ -10, sheds 2 friction. Taught by the forum. Once per floor.",
0a0bfee8329eatagen2026-10-11 05:11 UTC186
            Self::Energy => "+3 stamina. Consumes an energy drink. Tastes like a battery.",
782069860236atagen2026-10-10 12:44 UTC187
        }
782069860236atagen2026-10-10 12:44 UTC188
    }
782069860236atagen2026-10-10 12:44 UTC189
}
782069860236atagen2026-10-10 12:44 UTC190
782069860236atagen2026-10-10 12:44 UTC191
impl Technique {
0a0bfee8329eatagen2026-10-11 05:11 UTC192
    pub const ALL: [Self; 11] = [
782069860236atagen2026-10-10 12:44 UTC193
        Self::Utility(Utility::Warmup),
782069860236atagen2026-10-10 12:44 UTC194
        Self::Utility(Utility::Ice),
782069860236atagen2026-10-10 12:44 UTC195
        Self::Utility(Utility::Edge),
782069860236atagen2026-10-10 12:44 UTC196
        Self::Utility(Utility::Pump),
782069860236atagen2026-10-10 12:44 UTC197
        Self::Utility(Utility::Supplement),
0fdc47364c8catagen2026-10-11 04:45 UTC198
        Self::Utility(Utility::Meditate),
0a0bfee8329eatagen2026-10-11 05:11 UTC199
        Self::Utility(Utility::Energy),
782069860236atagen2026-10-10 12:44 UTC200
        Self::Training(Training::Wet),
782069860236atagen2026-10-10 12:44 UTC201
        Self::Training(Training::Dry),
782069860236atagen2026-10-10 12:44 UTC202
        Self::Training(Training::Stretch),
782069860236atagen2026-10-10 12:44 UTC203
        Self::Training(Training::Uli),
782069860236atagen2026-10-10 12:44 UTC204
    ];
782069860236atagen2026-10-10 12:44 UTC205
782069860236atagen2026-10-10 12:44 UTC206
    pub fn name(self) -> &'static str {
782069860236atagen2026-10-10 12:44 UTC207
        match self {
782069860236atagen2026-10-10 12:44 UTC208
            Self::Training(t) => t.name(),
782069860236atagen2026-10-10 12:44 UTC209
            Self::Utility(u) => u.name(),
782069860236atagen2026-10-10 12:44 UTC210
        }
782069860236atagen2026-10-10 12:44 UTC211
    }
782069860236atagen2026-10-10 12:44 UTC212
782069860236atagen2026-10-10 12:44 UTC213
    pub fn parse(name: &str) -> Option<Self> {
782069860236atagen2026-10-10 12:44 UTC214
        Self::ALL.into_iter().find(|t| t.name() == name)
dfa38cf4cd4batagen2026-10-10 09:17 UTC215
    }
dfa38cf4cd4batagen2026-10-10 09:17 UTC216
58e1fb3943e6atagen2026-10-10 13:30 UTC217
    pub fn lubricant(self) -> Option<&'static str> {
58e1fb3943e6atagen2026-10-10 13:30 UTC218
        match self {
58e1fb3943e6atagen2026-10-10 13:30 UTC219
            Self::Training(training) => Some(training.profile().lube.name()),
58e1fb3943e6atagen2026-10-10 13:30 UTC220
            Self::Utility(_) => None,
58e1fb3943e6atagen2026-10-10 13:30 UTC221
        }
58e1fb3943e6atagen2026-10-10 13:30 UTC222
    }
58e1fb3943e6atagen2026-10-10 13:30 UTC223
b85a0d4536c3atagen2026-10-11 08:50 UTC224
    pub fn describe(self, uli_taught: bool) -> Value {
782069860236atagen2026-10-10 12:44 UTC225
        let training = match self {
782069860236atagen2026-10-10 12:44 UTC226
            Self::Utility(utility) => {
782069860236atagen2026-10-10 12:44 UTC227
                return json!({ "name": utility.name(), "effect": utility.effect() });
782069860236atagen2026-10-10 12:44 UTC228
            }
782069860236atagen2026-10-10 12:44 UTC229
            Self::Training(training) => training,
dfa38cf4cd4batagen2026-10-10 09:17 UTC230
        };
782069860236atagen2026-10-10 12:44 UTC231
        let profile = training.profile();
58e1fb3943e6atagen2026-10-10 13:30 UTC232
        let lubricant = profile.lube.name();
dfa38cf4cd4batagen2026-10-10 09:17 UTC233
        json!({
782069860236atagen2026-10-10 12:44 UTC234
            "name": training.name(),
782069860236atagen2026-10-10 12:44 UTC235
            "dc": profile.dc,
dfa38cf4cd4batagen2026-10-10 09:17 UTC236
            "lubricant": lubricant,
782069860236atagen2026-10-10 12:44 UTC237
            "foreskin": profile.foreskin.map(|(min, max)| json!({ "min": min, "max": max })),
b85a0d4536c3atagen2026-10-11 08:50 UTC238
            "locked": training == Training::Uli && !uli_taught,
dfa38cf4cd4batagen2026-10-10 09:17 UTC239
        })
dfa38cf4cd4batagen2026-10-10 09:17 UTC240
    }
dfa38cf4cd4batagen2026-10-10 09:17 UTC241
}