diff options
Diffstat (limited to '')
| -rw-r--r-- | src/client/core.rs | 154 |
1 files changed, 149 insertions, 5 deletions
diff --git a/src/client/core.rs b/src/client/core.rs index e005f2a..6e9c28b 100644 --- a/src/client/core.rs +++ b/src/client/core.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | use crate::client::games::manager::GameManager; | 1 | use crate::client::games::manager::GameManager; |
| 2 | use crate::client::games::response::{parse_roulette_response, parse_spin_response}; | 2 | use crate::client::games::response::{ |
| 3 | parse_blackjack_response, parse_roulette_response, parse_spin_response, | ||
| 4 | }; | ||
| 3 | use crate::client::ui::{ | 5 | use crate::client::ui::{ |
| 4 | command_names, completion_lines, display_width, help_lines, input_cursor_column, | 6 | command_names, completion_lines, display_width, help_lines, input_cursor_column, |
| 5 | parse_bet_arg, record_history, | 7 | parse_bet_arg, record_history, |
| @@ -61,6 +63,9 @@ pub enum Effect { | |||
| 61 | enum Pending { | 63 | enum Pending { |
| 62 | Spin, | 64 | Spin, |
| 63 | RouletteSpin, | 65 | RouletteSpin, |
| 66 | BlackjackStart, | ||
| 67 | BlackjackAction, | ||
| 68 | BlackjackCancel, | ||
| 64 | Ping, | 69 | Ping, |
| 65 | Echo, | 70 | Echo, |
| 66 | Info, | 71 | Info, |
| @@ -191,6 +196,32 @@ impl ClientCore { | |||
| 191 | self.board_effects() | 196 | self.board_effects() |
| 192 | } | 197 | } |
| 193 | }, | 198 | }, |
| 199 | Some(Pending::BlackjackStart) | Some(Pending::BlackjackAction) => { | ||
| 200 | match parse_blackjack_response(reply) { | ||
| 201 | Ok(state) => { | ||
| 202 | self.balance = Some(state.balance); | ||
| 203 | self.game.apply_blackjack_state(state); | ||
| 204 | self.board_effects() | ||
| 205 | } | ||
| 206 | Err(message) => { | ||
| 207 | self.game.set_error(message); | ||
| 208 | self.board_effects() | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | Some(Pending::BlackjackCancel) => match parse_blackjack_response(reply) { | ||
| 213 | Ok(_) => { | ||
| 214 | self.game.end_game(); | ||
| 215 | vec![ | ||
| 216 | Effect::ClearRegion, | ||
| 217 | Effect::Print(vec!["Game exited.".to_string()]), | ||
| 218 | ] | ||
| 219 | } | ||
| 220 | Err(message) => { | ||
| 221 | self.game.set_error(message); | ||
| 222 | self.board_effects() | ||
| 223 | } | ||
| 224 | }, | ||
| 194 | Some(Pending::Ping) => { | 225 | Some(Pending::Ping) => { |
| 195 | if reply.trim() == "PONG" { | 226 | if reply.trim() == "PONG" { |
| 196 | print_line("Pong") | 227 | print_line("Pong") |
| @@ -225,14 +256,16 @@ impl ClientCore { | |||
| 225 | } | 256 | } |
| 226 | 257 | ||
| 227 | fn handle_game_key(&mut self, key: Key) -> Vec<Effect> { | 258 | fn handle_game_key(&mut self, key: Key) -> Vec<Effect> { |
| 228 | if self | 259 | let game_type = self |
| 229 | .game | 260 | .game |
| 230 | .get_current_state() | 261 | .get_current_state() |
| 231 | .map(|state| state.game_type.as_str()) | 262 | .map(|state| state.game_type.as_str()); |
| 232 | == Some("roulette") | 263 | if game_type == Some("roulette") { |
| 233 | { | ||
| 234 | return self.handle_roulette_key(key); | 264 | return self.handle_roulette_key(key); |
| 235 | } | 265 | } |
| 266 | if game_type == Some("blackjack") { | ||
| 267 | return self.handle_blackjack_key(key); | ||
| 268 | } | ||
| 236 | 269 | ||
| 237 | match key { | 270 | match key { |
| 238 | Key::CtrlC => vec![Effect::Quit], | 271 | Key::CtrlC => vec![Effect::Quit], |
| @@ -411,6 +444,117 @@ impl ClientCore { | |||
| 411 | .unwrap_or_default() | 444 | .unwrap_or_default() |
| 412 | } | 445 | } |
| 413 | 446 | ||
| 447 | fn handle_blackjack_key(&mut self, key: Key) -> Vec<Effect> { | ||
| 448 | let status = self | ||
| 449 | .game | ||
| 450 | .get_current_state() | ||
| 451 | .and_then(|state| state.data.get("status")) | ||
| 452 | .cloned() | ||
| 453 | .unwrap_or_else(|| "ready".to_string()); | ||
| 454 | let hand = self.game.blackjack_state(); | ||
| 455 | |||
| 456 | match key { | ||
| 457 | Key::CtrlC => vec![Effect::Quit], | ||
| 458 | Key::Char('q') => { | ||
| 459 | if let Some(hand) = hand.filter(|hand| hand.phase == "player") { | ||
| 460 | self.game.set_blackjack_status("cancelling"); | ||
| 461 | self.pending = Some(Pending::BlackjackCancel); | ||
| 462 | vec![Effect::SendLine(format!( | ||
| 463 | "BLACKJACK_ACTION {} cancel", | ||
| 464 | hand.session_id | ||
| 465 | ))] | ||
| 466 | } else { | ||
| 467 | self.game.end_game(); | ||
| 468 | vec![ | ||
| 469 | Effect::ClearRegion, | ||
| 470 | Effect::Print(vec!["Game exited.".to_string()]), | ||
| 471 | ] | ||
| 472 | } | ||
| 473 | } | ||
| 474 | Key::Char(' ') => self.start_blackjack(), | ||
| 475 | Key::Char('h') | ||
| 476 | if hand.as_ref().is_some_and(|hand| hand.phase == "player") => | ||
| 477 | { | ||
| 478 | self.blackjack_action("hit") | ||
| 479 | } | ||
| 480 | Key::Char('s') | ||
| 481 | if hand.as_ref().is_some_and(|hand| hand.phase == "player") => | ||
| 482 | { | ||
| 483 | self.blackjack_action("stand") | ||
| 484 | } | ||
| 485 | Key::Char('d') | ||
| 486 | if hand.as_ref().is_some_and(|hand| { | ||
| 487 | hand.phase == "player" | ||
| 488 | && hand.actions.iter().any(|action| action == "double") | ||
| 489 | }) => | ||
| 490 | { | ||
| 491 | self.blackjack_action("double") | ||
| 492 | } | ||
| 493 | Key::Char('+') | Key::Char('=') | ||
| 494 | if matches!(status.as_str(), "ready" | "done") => | ||
| 495 | { | ||
| 496 | let _ = self.game.adjust_bet(10); | ||
| 497 | self.board_effects() | ||
| 498 | } | ||
| 499 | Key::Char('-') if matches!(status.as_str(), "ready" | "done") => { | ||
| 500 | let _ = self.game.adjust_bet(-10); | ||
| 501 | self.board_effects() | ||
| 502 | } | ||
| 503 | _ => Vec::new(), | ||
| 504 | } | ||
| 505 | } | ||
| 506 | |||
| 507 | fn start_blackjack(&mut self) -> Vec<Effect> { | ||
| 508 | let status = self | ||
| 509 | .game | ||
| 510 | .get_current_state() | ||
| 511 | .and_then(|state| state.data.get("status")) | ||
| 512 | .cloned() | ||
| 513 | .unwrap_or_else(|| "ready".to_string()); | ||
| 514 | if !matches!(status.as_str(), "ready" | "done") { | ||
| 515 | return Vec::new(); | ||
| 516 | } | ||
| 517 | let Some(request) = self.game.spin_request() else { | ||
| 518 | return Vec::new(); | ||
| 519 | }; | ||
| 520 | if let Some(balance) = self.balance { | ||
| 521 | let bet = self | ||
| 522 | .game | ||
| 523 | .get_current_state() | ||
| 524 | .map(|state| state.bet_amount) | ||
| 525 | .unwrap_or(0); | ||
| 526 | if bet as i64 > balance { | ||
| 527 | self.game.set_error("Insufficient balance"); | ||
| 528 | return self.board_effects(); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | self.game.set_blackjack_status("dealing"); | ||
| 533 | self.pending = Some(Pending::BlackjackStart); | ||
| 534 | let mut effects = self.board_effects(); | ||
| 535 | effects.push(Effect::SendLine(request)); | ||
| 536 | effects | ||
| 537 | } | ||
| 538 | |||
| 539 | fn blackjack_action(&mut self, action: &str) -> Vec<Effect> { | ||
| 540 | let Some(hand) = self.game.blackjack_state() else { | ||
| 541 | return Vec::new(); | ||
| 542 | }; | ||
| 543 | if hand.phase != "player" { | ||
| 544 | return Vec::new(); | ||
| 545 | } | ||
| 546 | if action == "double" | ||
| 547 | && !hand.actions.iter().any(|available| available == "double") | ||
| 548 | { | ||
| 549 | return Vec::new(); | ||
| 550 | } | ||
| 551 | self.pending = Some(Pending::BlackjackAction); | ||
| 552 | vec![Effect::SendLine(format!( | ||
| 553 | "BLACKJACK_ACTION {} {}", | ||
| 554 | hand.session_id, action | ||
| 555 | ))] | ||
| 556 | } | ||
| 557 | |||
| 414 | fn submit(&mut self) -> Vec<Effect> { | 558 | fn submit(&mut self) -> Vec<Effect> { |
| 415 | let line = self.input.trim().to_string(); | 559 | let line = self.input.trim().to_string(); |
| 416 | self.input.clear(); | 560 | self.input.clear(); |
