remotro 0.2.0

A crate allowing remote control of Balatro
Documentation
use super::{Screen, play::Play};
use crate::balatro::deck::PlayingCard;
use crate::{balatro::boosters, balatro_enum, net::Connection};
use serde::{Deserialize, Serialize};

pub struct SelectBlind<'a> {
    info: protocol::BlindInfo,
    connection: &'a mut Connection,
}

impl<'a> SelectBlind<'a> {
    pub async fn select(self) -> Result<Play<'a>, super::Error> {
        let info = self.connection.request(protocol::SelectBlind).await??;
        Ok(Play::new(info, self.connection))
    }

    pub async fn skip(self) -> Result<SkipResult<'a>, super::Error> {
        let info = self
            .connection
            .request(protocol::SkipBlind::<'a> {
                _r_marker: std::marker::PhantomData,
            })
            .await??;
        match info {
            protocol::SkipBlindResult::Select(info) => {
                Ok(SkipResult::Select(SelectBlind::new(info, self.connection)))
            }
            protocol::SkipBlindResult::Booster(info) => match info {
                protocol::SkippedBooster::Arcana(info) => {
                    Ok(SkipResult::Booster(boosters::OpenBoosterPack::Arcana(
                        boosters::OpenArcanaPack::new(info, self.connection),
                    )))
                }
                protocol::SkippedBooster::Buffoon(info) => {
                    Ok(SkipResult::Booster(boosters::OpenBoosterPack::Buffoon(
                        boosters::OpenBuffoonPack::new(info, self.connection),
                    )))
                }
                protocol::SkippedBooster::Celestial(info) => {
                    Ok(SkipResult::Booster(boosters::OpenBoosterPack::Celestial(
                        boosters::OpenCelestialPack::new(info, self.connection),
                    )))
                }
                protocol::SkippedBooster::Spectral(info) => {
                    Ok(SkipResult::Booster(boosters::OpenBoosterPack::Spectral(
                        boosters::OpenSpectralPack::new(info, self.connection),
                    )))
                }
                protocol::SkippedBooster::Standard(info) => {
                    Ok(SkipResult::Booster(boosters::OpenBoosterPack::Standard(
                        boosters::OpenStandardPack::new(info, self.connection),
                    )))
                }
            },
        }
    }

    pub fn small(&self) -> &SmallBlindChoice {
        &self.info.blinds.small
    }

    pub fn big(&self) -> &BigBlindChoice {
        &self.info.blinds.big
    }

    pub fn boss(&self) -> &BossBlindChoice {
        &self.info.blinds.boss
    }
}

impl<'a> Screen<'a> for SelectBlind<'a> {
    type Info = protocol::BlindInfo;
    fn new(info: Self::Info, connection: &'a mut Connection) -> Self {
        Self { info, connection }
    }
    fn name() -> String {
        "blind_select".to_string()
    }
}

crate::impl_hud!(SelectBlind);

#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum CurrentBlind {
    Small { chips: u32 },
    Big { chips: u32 },
    Boss { kind: Boss, chips: u32 },
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SmallBlindChoice {
    pub state: BlindState,
    pub chips: f64,
    pub tag: Tag,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BigBlindChoice {
    pub state: BlindState,
    pub chips: f64,
    pub tag: Tag,
}

balatro_enum!(Tag {
    Uncommon = "tag_uncommon",
    Rare = "tag_rare",
    Negative = "tag_negative",
    Foil = "tag_foil",
    Holographic = "tag_holo",
    Polychrome = "tag_polychrome",
    Investment = "tag_investment",
    Voucher = "tag_voucher",
    Boss = "tag_boss",
    Standard = "tag_standard",
    Charm = "tag_charm",
    Meteor = "tag_meteor",
    Buffoon = "tag_buffoon",
    Handy = "tag_handy",
    Ethereal = "tag_ethereal",
    Coupon = "tag_coupon",
    Double = "tag_double",
    Juggle = "tag_juggle",
    D6 = "tag_d_six",
    TopUp = "tag_top_up",
    Skip = "tag_skip",
    Orbital = "tag_orbital",
    Economy = "tag_economy",
    Garbage = "tag_garbage",
});

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BossBlindChoice {
    pub kind: Boss,
    pub state: BlindState,
    pub chips: f64,
}
balatro_enum!(Boss {
    TheOx = "bl_ox",
    TheHook = "bl_hook",
    TheMouth = "bl_mouth",
    TheFish = "bl_fish",
    TheClub = "bl_club",
    TheManacle = "bl_manacle",
    TheTooth = "bl_tooth",
    TheWall = "bl_wall",
    TheHouse = "bl_house",
    TheMark = "bl_mark",
    TheWheel = "bl_wheel",
    TheArm = "bl_arm",
    ThePsychic = "bl_psychic",
    TheGoad = "bl_goad",
    TheWater = "bl_water",
    TheEye = "bl_eye",
    ThePlant = "bl_plant",
    TheNeedle = "bl_needle",
    TheHead = "bl_head",
    TheWindow = "bl_window",
    TheSerpent = "bl_serpent",
    ThePillar = "bl_pillar",
    TheFlint = "bl_flint",
    CeruleanBell = "bl_final_bell",
    VerdantLeaf = "bl_final_leaf",
    VioletVessel = "bl_final_vessel",
    AmberAcorn = "bl_final_acorn",
    CrimsonHeart = "bl_final_heart",
});

#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
#[serde(rename_all = "PascalCase")]
pub enum BlindState {
    Current,
    Select,
    Skipped,
    Upcoming,
    Defeated,
}

pub enum SkipResult<'a> {
    Select(SelectBlind<'a>),
    Booster(boosters::OpenBoosterPack<'a, protocol::SkipBlindResult<'a>>),
}

pub(crate) mod protocol {
    use crate::{
        balatro::{Screen, boosters, hud::protocol::HudInfo, play::protocol::PlayInfo},
        net::protocol::{Packet, Request, Response},
    };
    use serde::{Deserialize, Serialize};

    use super::{BigBlindChoice, BossBlindChoice, SmallBlindChoice};

    #[derive(Serialize, Deserialize)]
    pub struct BlindInfo {
        pub hud: HudInfo,
        pub blinds: BlindChoices,
    }

    impl Response for BlindInfo {}

    impl Packet for BlindInfo {
        fn kind() -> String {
            "blind_select/info".to_string()
        }
    }

    #[derive(Serialize, Deserialize)]
    pub struct BlindChoices {
        pub small: SmallBlindChoice,
        pub big: BigBlindChoice,
        pub boss: BossBlindChoice,
    }

    #[derive(Serialize)]
    pub struct SelectBlind;

    impl Request for SelectBlind {
        type Expect = Result<PlayInfo, String>;
    }

    impl Packet for SelectBlind {
        fn kind() -> String {
            "blind_select/select".to_string()
        }
    }

    #[derive(Deserialize)]
    pub enum SkipBlindResult<'a> {
        Select(BlindInfo),
        Booster(SkippedBooster<'a>),
    }

    impl<'a> Response for SkipBlindResult<'a> {}

    impl<'a> Packet for SkipBlindResult<'a> {
        fn kind() -> String {
            "blind_select/skip_result".to_string()
        }
    }

    #[derive(Deserialize)]
    pub enum SkippedBooster<'a> {
        Arcana(<boosters::OpenArcanaPack<'a, SkipBlindResult<'a>> as Screen<'a>>::Info),
        Buffoon(<boosters::OpenBuffoonPack<'a, SkipBlindResult<'a>> as Screen<'a>>::Info),
        Celestial(<boosters::OpenCelestialPack<'a, SkipBlindResult<'a>> as Screen<'a>>::Info),
        Spectral(<boosters::OpenSpectralPack<'a, SkipBlindResult<'a>> as Screen<'a>>::Info),
        Standard(<boosters::OpenStandardPack<'a, SkipBlindResult<'a>> as Screen<'a>>::Info),
    }

    #[derive(Serialize)]
    pub struct SkipBlind<'a> {
        pub _r_marker: std::marker::PhantomData<&'a ()>,
    }

    impl<'a> Request for SkipBlind<'a> {
        type Expect = Result<SkipBlindResult<'a>, String>;
    }

    impl<'a> Packet for SkipBlind<'a> {
        fn kind() -> String {
            "blind_select/skip".to_string()
        }
    }
}