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}