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.version == Some("0.34".to_string())
169 {
170 self.write_key("quit")?;
171 } else {
172 self.write_key("yes")?;
173 }
174 self.write_key("key_enter")?;
175 self.message_found = false; // Otherwise close_input will be skipped
176 }
177 _ => return Err(e),
178 },
179 };
180
181 match self.read_until("close_input", None, None) {
182 Ok(_) => (),
183 Err(e) => match *e {
184 Error::Blocking(BlockingError::More) => self.write_key("key_esc")?,
185 _ => return Err(e),
186 },
187 };
188
189 loop {
190 self.write_key("key_esc")?;
191 match self.read_until("go_lobby", None, None) {
192 Ok(_) => return Ok(()),
193 Err(e) => match *e {
194 Error::Blocking(BlockingError::More) => (),
195 _ => return Err(e),
196 },
197 };
198 }
199 }
200}