atagen

jelq

public

an http based roguelike rpg about jelqing

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

Blame: src/game/body.rs

782069860236atagen2026-10-10 12:44 UTC1
use axum::http::StatusCode;
782069860236atagen2026-10-10 12:44 UTC2
782069860236atagen2026-10-10 12:44 UTC3
use super::{
8b8e83329a1datagen2026-10-12 12:01 UTC4
    Outcome, Run,
8b8e83329a1datagen2026-10-12 12:01 UTC5
    balance::{GIRTH_RESPECTED, GIRTH_THICK, LOCKER_ROOM_BAR, MAX_STAMINA, STARTING_EQ},
8b8e83329a1datagen2026-10-12 12:01 UTC6
    credentials::etag_matches,
782069860236atagen2026-10-10 12:44 UTC7
};
782069860236atagen2026-10-10 12:44 UTC8
use crate::problem::Problem;
782069860236atagen2026-10-10 12:44 UTC9
8b8e83329a1datagen2026-10-12 12:01 UTC10
pub struct Body {
8b8e83329a1datagen2026-10-12 12:01 UTC11
    pub(crate) length: f64,
8b8e83329a1datagen2026-10-12 12:01 UTC12
    pub(crate) girth: f64,
8b8e83329a1datagen2026-10-12 12:01 UTC13
    pub(crate) foreskin: Option<f64>,
8b8e83329a1datagen2026-10-12 12:01 UTC14
    pub(crate) eq: i32,
8b8e83329a1datagen2026-10-12 12:01 UTC15
    pub(crate) stamina: i32,
8b8e83329a1datagen2026-10-12 12:01 UTC16
    pub(crate) friction: i32,
8b8e83329a1datagen2026-10-12 12:01 UTC17
    pub(crate) grip: Grip,
8b8e83329a1datagen2026-10-12 12:01 UTC18
    /// The grip of the last stroke, which decides whether this one gets the switching discount.
8b8e83329a1datagen2026-10-12 12:01 UTC19
    pub(crate) jelq_grip: Option<Grip>,
8b8e83329a1datagen2026-10-12 12:01 UTC20
    pub(crate) warmed_up: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC21
    pub(crate) exhausted: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC22
    pub(crate) put_applied: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC23
    pub(crate) conditions: Conditions,
8b8e83329a1datagen2026-10-12 12:01 UTC24
    pub(crate) pending: Vec<Pending>,
782069860236atagen2026-10-10 12:44 UTC25
}
782069860236atagen2026-10-10 12:44 UTC26
8b8e83329a1datagen2026-10-12 12:01 UTC27
impl Body {
8b8e83329a1datagen2026-10-12 12:01 UTC28
    pub fn new(length: f64, girth: f64, foreskin: f64) -> Self {
8b8e83329a1datagen2026-10-12 12:01 UTC29
        Self {
8b8e83329a1datagen2026-10-12 12:01 UTC30
            length,
8b8e83329a1datagen2026-10-12 12:01 UTC31
            girth,
8b8e83329a1datagen2026-10-12 12:01 UTC32
            foreskin: Some(foreskin),
8b8e83329a1datagen2026-10-12 12:01 UTC33
            eq: STARTING_EQ,
8b8e83329a1datagen2026-10-12 12:01 UTC34
            stamina: MAX_STAMINA,
8b8e83329a1datagen2026-10-12 12:01 UTC35
            friction: 0,
8b8e83329a1datagen2026-10-12 12:01 UTC36
            grip: Grip::Overhand,
8b8e83329a1datagen2026-10-12 12:01 UTC37
            jelq_grip: None,
8b8e83329a1datagen2026-10-12 12:01 UTC38
            warmed_up: false,
8b8e83329a1datagen2026-10-12 12:01 UTC39
            exhausted: false,
8b8e83329a1datagen2026-10-12 12:01 UTC40
            put_applied: false,
8b8e83329a1datagen2026-10-12 12:01 UTC41
            conditions: Conditions::default(),
8b8e83329a1datagen2026-10-12 12:01 UTC42
            pending: Vec::new(),
8b8e83329a1datagen2026-10-12 12:01 UTC43
        }
8b8e83329a1datagen2026-10-12 12:01 UTC44
    }
782069860236atagen2026-10-10 12:44 UTC45
}
782069860236atagen2026-10-10 12:44 UTC46
8b8e83329a1datagen2026-10-12 12:01 UTC47
#[derive(Default)]
8b8e83329a1datagen2026-10-12 12:01 UTC48
pub struct Conditions {
8b8e83329a1datagen2026-10-12 12:01 UTC49
    pub(crate) peyronie: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC50
    pub(crate) pulled_muscle: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC51
    pub(crate) priapism_turns: u8,
8b8e83329a1datagen2026-10-12 12:01 UTC52
    pub(crate) friction_burn: bool,
8b8e83329a1datagen2026-10-12 12:01 UTC53
}
8b8e83329a1datagen2026-10-12 12:01 UTC54
8b8e83329a1datagen2026-10-12 12:01 UTC55
/// Gains a `202` promised, due on a later turn.
8b8e83329a1datagen2026-10-12 12:01 UTC56
pub struct Pending {
8b8e83329a1datagen2026-10-12 12:01 UTC57
    pub(crate) due: u32,
8b8e83329a1datagen2026-10-12 12:01 UTC58
    pub(crate) length: f64,
8b8e83329a1datagen2026-10-12 12:01 UTC59
    pub(crate) girth: f64,
8b8e83329a1datagen2026-10-12 12:01 UTC60
    pub(crate) foreskin: f64,
8b8e83329a1datagen2026-10-12 12:01 UTC61
}
8b8e83329a1datagen2026-10-12 12:01 UTC62
8b8e83329a1datagen2026-10-12 12:01 UTC63
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
8b8e83329a1datagen2026-10-12 12:01 UTC64
pub enum Grip {
8b8e83329a1datagen2026-10-12 12:01 UTC65
    Overhand,
8b8e83329a1datagen2026-10-12 12:01 UTC66
    Underhand,
8b8e83329a1datagen2026-10-12 12:01 UTC67
}
8b8e83329a1datagen2026-10-12 12:01 UTC68
8b8e83329a1datagen2026-10-12 12:01 UTC69
impl Grip {
8b8e83329a1datagen2026-10-12 12:01 UTC70
    pub fn name(self) -> &'static str {
8b8e83329a1datagen2026-10-12 12:01 UTC71
        match self {
8b8e83329a1datagen2026-10-12 12:01 UTC72
            Self::Overhand => "overhand",
8b8e83329a1datagen2026-10-12 12:01 UTC73
            Self::Underhand => "underhand",
782069860236atagen2026-10-10 12:44 UTC74
        }
782069860236atagen2026-10-10 12:44 UTC75
    }
8b8e83329a1datagen2026-10-12 12:01 UTC76
}
782069860236atagen2026-10-10 12:44 UTC77
8b8e83329a1datagen2026-10-12 12:01 UTC78
impl std::str::FromStr for Grip {
8b8e83329a1datagen2026-10-12 12:01 UTC79
    type Err = ();
8b8e83329a1datagen2026-10-12 12:01 UTC80
8b8e83329a1datagen2026-10-12 12:01 UTC81
    fn from_str(protocol: &str) -> Result<Self, ()> {
8b8e83329a1datagen2026-10-12 12:01 UTC82
        match protocol.to_ascii_lowercase().as_str() {
8b8e83329a1datagen2026-10-12 12:01 UTC83
            "overhand" => Ok(Self::Overhand),
8b8e83329a1datagen2026-10-12 12:01 UTC84
            "underhand" => Ok(Self::Underhand),
8b8e83329a1datagen2026-10-12 12:01 UTC85
            _ => Err(()),
782069860236atagen2026-10-10 12:44 UTC86
        }
782069860236atagen2026-10-10 12:44 UTC87
    }
8b8e83329a1datagen2026-10-12 12:01 UTC88
}
782069860236atagen2026-10-10 12:44 UTC89
8b8e83329a1datagen2026-10-12 12:01 UTC90
impl Run {
782069860236atagen2026-10-10 12:44 UTC91
    pub fn delete_foreskin(&mut self, if_match: Option<&str>) -> Result<Outcome, Problem> {
8b8e83329a1datagen2026-10-12 12:01 UTC92
        if self.body.foreskin.is_none() {
782069860236atagen2026-10-10 12:44 UTC93
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC94
                StatusCode::GONE,
782069860236atagen2026-10-10 12:44 UTC95
                "foreskin-gone",
782069860236atagen2026-10-10 12:44 UTC96
                "The foreskin is gone. It will not be returning.",
782069860236atagen2026-10-10 12:44 UTC97
            ));
782069860236atagen2026-10-10 12:44 UTC98
        }
782069860236atagen2026-10-10 12:44 UTC99
        let Some(if_match) = if_match else {
782069860236atagen2026-10-10 12:44 UTC100
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC101
                StatusCode::PRECONDITION_REQUIRED,
782069860236atagen2026-10-10 12:44 UTC102
                "confirmation-required",
782069860236atagen2026-10-10 12:44 UTC103
                "The server requires you to be sure. Send If-Match.",
782069860236atagen2026-10-10 12:44 UTC104
            ));
782069860236atagen2026-10-10 12:44 UTC105
        };
782069860236atagen2026-10-10 12:44 UTC106
        if !etag_matches(if_match, &self.etag()) {
782069860236atagen2026-10-10 12:44 UTC107
            return Err(Problem::new(
782069860236atagen2026-10-10 12:44 UTC108
                StatusCode::PRECONDITION_FAILED,
782069860236atagen2026-10-10 12:44 UTC109
                "stale-foreskin",
782069860236atagen2026-10-10 12:44 UTC110
                "The foreskin has changed since you last looked at it.",
782069860236atagen2026-10-10 12:44 UTC111
            ));
782069860236atagen2026-10-10 12:44 UTC112
        }
8b8e83329a1datagen2026-10-12 12:01 UTC113
        self.body.foreskin = None;
8b8e83329a1datagen2026-10-12 12:01 UTC114
        self.standing.shame += 20;
782069860236atagen2026-10-10 12:44 UTC115
        self.touch();
782069860236atagen2026-10-10 12:44 UTC116
        Ok(Outcome::empty(StatusCode::NO_CONTENT))
782069860236atagen2026-10-10 12:44 UTC117
    }
782069860236atagen2026-10-10 12:44 UTC118
058650ec79d6atagen2026-10-12 10:49 UTC119
    pub fn set_grip(&mut self, grip: Grip) -> Result<(), Problem> {
058650ec79d6atagen2026-10-12 10:49 UTC120
        self.grip_closes(grip)?;
8b8e83329a1datagen2026-10-12 12:01 UTC121
        self.body.grip = grip;
782069860236atagen2026-10-10 12:44 UTC122
        self.touch();
058650ec79d6atagen2026-10-12 10:49 UTC123
        Ok(())
058650ec79d6atagen2026-10-12 10:49 UTC124
    }
058650ec79d6atagen2026-10-12 10:49 UTC125
058650ec79d6atagen2026-10-12 10:49 UTC126
    pub(super) fn grip_closes(&self, grip: Grip) -> Result<(), Problem> {
8b8e83329a1datagen2026-10-12 12:01 UTC127
        if grip == Grip::Underhand && self.body.girth > GIRTH_THICK {
058650ec79d6atagen2026-10-12 10:49 UTC128
            return Err(Problem::new(
058650ec79d6atagen2026-10-12 10:49 UTC129
                StatusCode::PAYLOAD_TOO_LARGE,
058650ec79d6atagen2026-10-12 10:49 UTC130
                "grip-too-small",
058650ec79d6atagen2026-10-12 10:49 UTC131
                format!(
058650ec79d6atagen2026-10-12 10:49 UTC132
                    "The underhand grip no longer closes around {:.2} inches of girth.",
8b8e83329a1datagen2026-10-12 12:01 UTC133
                    self.body.girth
058650ec79d6atagen2026-10-12 10:49 UTC134
                ),
058650ec79d6atagen2026-10-12 10:49 UTC135
            )
058650ec79d6atagen2026-10-12 10:49 UTC136
            .with("maximum", GIRTH_THICK));
058650ec79d6atagen2026-10-12 10:49 UTC137
        }
058650ec79d6atagen2026-10-12 10:49 UTC138
        Ok(())
058650ec79d6atagen2026-10-12 10:49 UTC139
    }
058650ec79d6atagen2026-10-12 10:49 UTC140
058650ec79d6atagen2026-10-12 10:49 UTC141
    /// Everyone in the changing room self-reports, so the comparison is self-report against self-report.
058650ec79d6atagen2026-10-12 10:49 UTC142
    pub fn holds_own_in_the_locker_room(&self) -> bool {
058650ec79d6atagen2026-10-12 10:49 UTC143
        self.self_reported() >= LOCKER_ROOM_BAR
058650ec79d6atagen2026-10-12 10:49 UTC144
    }
058650ec79d6atagen2026-10-12 10:49 UTC145
058650ec79d6atagen2026-10-12 10:49 UTC146
    pub fn girth_respected(&self) -> bool {
8b8e83329a1datagen2026-10-12 12:01 UTC147
        self.body.girth >= GIRTH_RESPECTED
058650ec79d6atagen2026-10-12 10:49 UTC148
    }
159f0998263batagen2026-10-12 11:01 UTC149
}