dcss_api/
play.rs

1use crate::Error;
2use crate::Webtile;
3use crate::api_errors::BlockingError;
4use serde_json::json;
5
6impl Webtile {
7    /// Start an unseeded game by selecting the game_id and the character's
8    /// specifications.
9    ///
10    /// # Arguments
11    ///
12    /// * `game_id` - A string slice of the game's ID.
13    /// * `species` - A string slice for the character's species.
14    /// * `background` - A string slice for the character's background.
15    /// * `weapon` - A string slice for the character's weapon.
16    ///
17    /// # Example
18    ///
19    /// ```no_run
20    /// // Start a game on "dcss-web-trunk", for a Minotaur (b), Berserker (f), with a mace (b)
21    /// webtile.start_game("dcss-web-trunk", "b", "f", "b")?;
22    /// ```
23    pub fn start_game(
24        &mut self,
25        game_id: &str,
26        species: &str,
27        background: &str,
28        weapon: &str,
29    ) -> Result<(), Box<Error>> {
30        self.start_game_seeded(game_id, "0", false, species, background, weapon)
31    }
32
33    /// Continue a saved game by selecting it's game ID.
34    ///
35    /// # Arguments
36    ///
37    /// * `game_id` - A string slice of the game's ID.
38    ///
39    /// # Example
40    ///
41    /// ```no_run
42    /// // Continue a game on "dcss-web-trunk"
43    /// webtile.continue_game("dcss-web-trunk")?;
44    /// ```
45    pub fn continue_game(&mut self, game_id: &str) -> Result<(), Box<Error>> {
46        self.start_game_seeded(game_id, "", false, "", "", "")
47    }
48
49    /// Start an seeded game by selecting the game_id, the seed and the character's
50    /// specifications.
51    ///
52    /// # Arguments
53    ///
54    /// * `game_id` - A string slice of the game's ID.
55    /// * `seed` - A string slice of the game's seed.
56    /// * `pregenerate` - A bool on if the pregeneration option should be selected.
57    /// * `species` - A string slice for the character's species.
58    /// * `background` - A string slice for the character's background.
59    /// * `weapon` - A string slice for the character's weapon.
60    ///
61    /// # Example
62    ///
63    /// ```no_run
64    /// // Start a game on "dcss-web-trunk", for the "123" seed (pregenerated) for a
65    /// // Minotaur (b), Berserker (i), with a mace (b)
66    /// webtile.start_game_seeded("dcss-web-trunk", "123", true, "b", "f", "b")?;
67    /// ```
68    pub fn start_game_seeded(
69        &mut self,
70        game_id: &str,
71        seed: &str,
72        pregenerate: bool,
73        species: &str,
74        background: &str,
75        weapon: &str,
76    ) -> Result<(), Box<Error>> {
77        self.write_json(json!({"msg": "play", "game_id": game_id}))?;
78
79        let mut newgame_count = 0;
80        loop {
81            match self.read_until("map", None, None) {
82                Ok(_) => {
83                    // Get version of game (for API differences)
84                    if self.version.is_none() {
85                        for message in self.read_only_messages() {
86                            let message_obj = message.as_object().unwrap();
87                            if message_obj["msg"] == "version" {
88                                let text = message_obj["text"].as_str().unwrap();
89                                let long_version = text.split(" ").collect::<Vec<&str>>()[4];
90                                let version =
91                                    long_version.split(".").collect::<Vec<&str>>()[0..2].join(".");
92                                self.version = Some(version);
93                            }
94                        }
95                    }
96                    return Ok(());
97                }
98                Err(e) => match *e {
99                    Error::Blocking(BlockingError::SeedSelection) => {
100                        self.write_key("-")?;
101                        self.read_until("ui-state-sync", None, None)?;
102                        self.write_key(seed)?;
103                        if pregenerate {
104                            self.write_key("\t\t\t \r")?;
105                        } else {
106                            self.write_key("\r")?;
107                        }
108                    }
109                    Error::Blocking(BlockingError::NewGameChoice) => {
110                        match newgame_count {
111                            0 => self.write_key(species)?,
112                            1 => self.write_key(background)?,
113                            2 => self.write_key(weapon)?,
114                            _ => unreachable!(),
115                        }
116
117                        newgame_count += 1;
118                    }
119                    _ => return Err(e),
120                },
121            };
122        }
123    }
124
125    /// Get version of the DCSS game (e.g. "0.33"). Will return `None` if
126    /// the game has not been started.
127    ///
128    /// # Example
129    ///
130    /// ```no_run
131    /// webtile.game_version()?;
132    /// ```
133    pub fn game_version(&self) -> Option<String> {
134        self.version.clone()
135    }
136
137    /// Save a game by sending the `CTRL + S` command.
138    ///
139    /// # Example
140    ///
141    /// ```no_run
142    /// webtile.save_game()?;
143    /// ```
144    pub fn save_game(&mut self) -> Result<(), Box<Error>> {
145        self.write_key("key_ctrl_s")?;
146
147        self.read_until("go_lobby", None, None)?;
148
149        Ok(())
150    }
151
152    /// Quit the game (same result as dying), by sending a `CTRL + Q` and
153    /// answering `yes`.
154    ///
155    /// # Example
156    ///
157    /// ```no_run
158    /// webtile.quit_game()?;
159    /// ```
160    pub fn quit_game(&mut self) -> Result<(), Box<Error>> {
161        self.write_key("key_ctrl_q")?;
162
163        match self.read_until("input_mode", Some("mode"), Some(7)) {
164            Ok(_) => (),
165            Err(e) => match *e {
166                Error::Blocking(BlockingError::TextInput) => {
167                    if self.version == Some("0.33".to_string()) {
168                        self.write_key("quit")?;
169                    } else {
170                        self.write_key("yes")?;
171                    }
172                    self.write_key("key_enter")?;
173                    self.message_found = false; // Otherwise close_input will be skipped
174                }
175                _ => return Err(e),
176            },
177        };
178
179        match self.read_until("close_input", None, None) {
180            Ok(_) => (),
181            Err(e) => match *e {
182                Error::Blocking(BlockingError::More) => self.write_key("key_esc")?,
183                _ => return Err(e),
184            },
185        };
186
187        loop {
188            self.write_key("key_esc")?;
189            match self.read_until("go_lobby", None, None) {
190                Ok(_) => return Ok(()),
191                Err(e) => match *e {
192                    Error::Blocking(BlockingError::More) => (),
193                    _ => return Err(e),
194                },
195            };
196        }
197    }
198}