Display timeout in game UI

This commit is contained in:
Pierre HUBERT 2022-10-17 08:21:42 +02:00
parent b4772aa88e
commit 9162c5eb24
3 changed files with 36 additions and 7 deletions

View File

@ -12,6 +12,7 @@ use tui::{Frame, Terminal};
use sea_battle_backend::data::{Coordinates, CurrentGameMapStatus, CurrentGameStatus}; use sea_battle_backend::data::{Coordinates, CurrentGameMapStatus, CurrentGameStatus};
use sea_battle_backend::human_player_ws::{ClientMessage, ServerMessage}; use sea_battle_backend::human_player_ws::{ClientMessage, ServerMessage};
use sea_battle_backend::utils::time_utils::time;
use sea_battle_backend::utils::Res; use sea_battle_backend::utils::Res;
use crate::client::Client; use crate::client::Client;
@ -95,6 +96,7 @@ pub struct GameScreen {
invite_code: Option<String>, invite_code: Option<String>,
status: GameStatus, status: GameStatus,
opponent_name: Option<String>, opponent_name: Option<String>,
game_last_update: u64,
game: CurrentGameStatus, game: CurrentGameStatus,
curr_shoot_position: Coordinates, curr_shoot_position: Coordinates,
last_opponent_fire_position: Coordinates, last_opponent_fire_position: Coordinates,
@ -108,6 +110,7 @@ impl GameScreen {
invite_code: None, invite_code: None,
status: GameStatus::Connecting, status: GameStatus::Connecting,
opponent_name: None, opponent_name: None,
game_last_update: 0,
game: Default::default(), game: Default::default(),
curr_shoot_position: Coordinates::new(0, 0), curr_shoot_position: Coordinates::new(0, 0),
last_opponent_fire_position: Coordinates::invalid(), last_opponent_fire_position: Coordinates::invalid(),
@ -288,11 +291,13 @@ impl GameScreen {
ServerMessage::OpponentMustFire { status } => { ServerMessage::OpponentMustFire { status } => {
self.status = GameStatus::OpponentMustFire; self.status = GameStatus::OpponentMustFire;
self.game_last_update = time();
self.game = status; self.game = status;
} }
ServerMessage::RequestFire { status } => { ServerMessage::RequestFire { status } => {
self.status = GameStatus::MustFire; self.status = GameStatus::MustFire;
self.game_last_update = time();
self.game = status; self.game = status;
} }
@ -303,11 +308,13 @@ impl GameScreen {
} }
ServerMessage::LostGame { status } => { ServerMessage::LostGame { status } => {
self.game_last_update = time();
self.game = status; self.game = status;
self.status = GameStatus::LostGame; self.status = GameStatus::LostGame;
} }
ServerMessage::WonGame { status } => { ServerMessage::WonGame { status } => {
self.game_last_update = time();
self.game = status; self.game = status;
self.status = GameStatus::WonGame; self.status = GameStatus::WonGame;
} }
@ -476,6 +483,17 @@ impl GameScreen {
return HashMap::default(); return HashMap::default();
} }
// Add timeout (if required)
let mut timeout_str = String::new();
if self.status == GameStatus::MustFire || self.status == GameStatus::OpponentMustFire {
if let Some(remaining) = self.game.remaining_time_for_strike {
let timeout = self.game_last_update + remaining;
if time() < timeout {
timeout_str = format!(" {} seconds left", timeout - time());
}
}
}
// Draw main ui (default play UI) // Draw main ui (default play UI)
let player_map = self let player_map = self
.player_map(&self.game.your_map, false) .player_map(&self.game.your_map, false)
@ -519,8 +537,10 @@ impl GameScreen {
let buttons_width = buttons.iter().fold(0, |a, b| a + b.estimated_size().0 + 4); let buttons_width = buttons.iter().fold(0, |a, b| a + b.estimated_size().0 + 4);
let max_width = max(maps_width, status_text.len() as u16).max(buttons_width); let max_width = max(maps_width, status_text.len() as u16)
let total_height = 3 + maps_height + 3; .max(buttons_width)
.max(timeout_str.len() as u16);
let total_height = 3 + 1 + maps_height + 3;
// Check if frame is too small // Check if frame is too small
if max_width > f.size().width || total_height > f.size().height { if max_width > f.size().width || total_height > f.size().height {
@ -531,7 +551,8 @@ impl GameScreen {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
Constraint::Length(3), Constraint::Length(2),
Constraint::Length(2),
Constraint::Length(maps_height), Constraint::Length(maps_height),
Constraint::Length(3), Constraint::Length(3),
]) ])
@ -541,6 +562,10 @@ impl GameScreen {
let paragraph = Paragraph::new(status_text.as_str()); let paragraph = Paragraph::new(status_text.as_str());
f.render_widget(paragraph, centered_text(&status_text, &chunks[0])); f.render_widget(paragraph, centered_text(&status_text, &chunks[0]));
// Render timeout
let paragraph = Paragraph::new(timeout_str.as_str());
f.render_widget(paragraph, centered_text(&timeout_str, &chunks[1]));
// Render maps // Render maps
if show_both_maps { if show_both_maps {
let maps_chunks = Layout::default() let maps_chunks = Layout::default()
@ -550,16 +575,16 @@ impl GameScreen {
Constraint::Length(3), Constraint::Length(3),
Constraint::Length(opponent_map_size.0), Constraint::Length(opponent_map_size.0),
]) ])
.split(chunks[1]); .split(chunks[2]);
f.render_widget(player_map, maps_chunks[0]); f.render_widget(player_map, maps_chunks[0]);
f.render_widget(opponent_map, maps_chunks[2]); f.render_widget(opponent_map, maps_chunks[2]);
} else { } else {
// Render a single map // Render a single map
if self.can_fire() { if self.can_fire() {
f.render_widget(opponent_map, chunks[1]); f.render_widget(opponent_map, chunks[2]);
} else { } else {
f.render_widget(player_map, chunks[1]); f.render_widget(player_map, chunks[2]);
drop(opponent_map); drop(opponent_map);
} }
} }
@ -573,7 +598,7 @@ impl GameScreen {
.map(|_| Constraint::Percentage(100 / buttons.len() as u16)) .map(|_| Constraint::Percentage(100 / buttons.len() as u16))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
) )
.split(chunks[2]); .split(chunks[3]);
for (idx, b) in buttons.into_iter().enumerate() { for (idx, b) in buttons.into_iter().enumerate() {
let target = centered_rect_size( let target = centered_rect_size(

View File

@ -78,6 +78,7 @@ impl PrintableMap for PrintableCurrentGameMapStatus {
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Default)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Default)]
pub struct CurrentGameStatus { pub struct CurrentGameStatus {
pub remaining_time_for_strike: Option<u64>,
pub rules: GameRules, pub rules: GameRules,
pub your_map: CurrentGameMapStatus, pub your_map: CurrentGameMapStatus,
pub opponent_map: CurrentGameMapStatus, pub opponent_map: CurrentGameMapStatus,

View File

@ -257,6 +257,9 @@ impl Game {
/// Get current game status for a specific player /// Get current game status for a specific player
fn get_game_status_for_player(&self, id: usize) -> CurrentGameStatus { fn get_game_status_for_player(&self, id: usize) -> CurrentGameStatus {
CurrentGameStatus { CurrentGameStatus {
remaining_time_for_strike: self.rules.strike_timeout.map(|v| {
((self.curr_strike_request_started + v) as i64 - time() as i64).max(0) as u64
}),
rules: self.rules.clone(), rules: self.rules.clone(),
your_map: self.player_map(id).current_map_status(false), your_map: self.player_map(id).current_map_status(false),
opponent_map: self opponent_map: self