Display timeout in game UI
This commit is contained in:
		@@ -12,6 +12,7 @@ use tui::{Frame, Terminal};
 | 
			
		||||
 | 
			
		||||
use sea_battle_backend::data::{Coordinates, CurrentGameMapStatus, CurrentGameStatus};
 | 
			
		||||
use sea_battle_backend::human_player_ws::{ClientMessage, ServerMessage};
 | 
			
		||||
use sea_battle_backend::utils::time_utils::time;
 | 
			
		||||
use sea_battle_backend::utils::Res;
 | 
			
		||||
 | 
			
		||||
use crate::client::Client;
 | 
			
		||||
@@ -95,6 +96,7 @@ pub struct GameScreen {
 | 
			
		||||
    invite_code: Option<String>,
 | 
			
		||||
    status: GameStatus,
 | 
			
		||||
    opponent_name: Option<String>,
 | 
			
		||||
    game_last_update: u64,
 | 
			
		||||
    game: CurrentGameStatus,
 | 
			
		||||
    curr_shoot_position: Coordinates,
 | 
			
		||||
    last_opponent_fire_position: Coordinates,
 | 
			
		||||
@@ -108,6 +110,7 @@ impl GameScreen {
 | 
			
		||||
            invite_code: None,
 | 
			
		||||
            status: GameStatus::Connecting,
 | 
			
		||||
            opponent_name: None,
 | 
			
		||||
            game_last_update: 0,
 | 
			
		||||
            game: Default::default(),
 | 
			
		||||
            curr_shoot_position: Coordinates::new(0, 0),
 | 
			
		||||
            last_opponent_fire_position: Coordinates::invalid(),
 | 
			
		||||
@@ -288,11 +291,13 @@ impl GameScreen {
 | 
			
		||||
 | 
			
		||||
                    ServerMessage::OpponentMustFire { status } => {
 | 
			
		||||
                        self.status = GameStatus::OpponentMustFire;
 | 
			
		||||
                        self.game_last_update = time();
 | 
			
		||||
                        self.game = status;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ServerMessage::RequestFire { status } => {
 | 
			
		||||
                        self.status = GameStatus::MustFire;
 | 
			
		||||
                        self.game_last_update = time();
 | 
			
		||||
                        self.game = status;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@@ -303,11 +308,13 @@ impl GameScreen {
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ServerMessage::LostGame { status } => {
 | 
			
		||||
                        self.game_last_update = time();
 | 
			
		||||
                        self.game = status;
 | 
			
		||||
                        self.status = GameStatus::LostGame;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ServerMessage::WonGame { status } => {
 | 
			
		||||
                        self.game_last_update = time();
 | 
			
		||||
                        self.game = status;
 | 
			
		||||
                        self.status = GameStatus::WonGame;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -476,6 +483,17 @@ impl GameScreen {
 | 
			
		||||
            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)
 | 
			
		||||
        let player_map = self
 | 
			
		||||
            .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 max_width = max(maps_width, status_text.len() as u16).max(buttons_width);
 | 
			
		||||
        let total_height = 3 + maps_height + 3;
 | 
			
		||||
        let max_width = max(maps_width, status_text.len() as u16)
 | 
			
		||||
            .max(buttons_width)
 | 
			
		||||
            .max(timeout_str.len() as u16);
 | 
			
		||||
        let total_height = 3 + 1 + maps_height + 3;
 | 
			
		||||
 | 
			
		||||
        // Check if frame is too small
 | 
			
		||||
        if max_width > f.size().width || total_height > f.size().height {
 | 
			
		||||
@@ -531,7 +551,8 @@ impl GameScreen {
 | 
			
		||||
        let chunks = Layout::default()
 | 
			
		||||
            .direction(Direction::Vertical)
 | 
			
		||||
            .constraints([
 | 
			
		||||
                Constraint::Length(3),
 | 
			
		||||
                Constraint::Length(2),
 | 
			
		||||
                Constraint::Length(2),
 | 
			
		||||
                Constraint::Length(maps_height),
 | 
			
		||||
                Constraint::Length(3),
 | 
			
		||||
            ])
 | 
			
		||||
@@ -541,6 +562,10 @@ impl GameScreen {
 | 
			
		||||
        let paragraph = Paragraph::new(status_text.as_str());
 | 
			
		||||
        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
 | 
			
		||||
        if show_both_maps {
 | 
			
		||||
            let maps_chunks = Layout::default()
 | 
			
		||||
@@ -550,16 +575,16 @@ impl GameScreen {
 | 
			
		||||
                    Constraint::Length(3),
 | 
			
		||||
                    Constraint::Length(opponent_map_size.0),
 | 
			
		||||
                ])
 | 
			
		||||
                .split(chunks[1]);
 | 
			
		||||
                .split(chunks[2]);
 | 
			
		||||
 | 
			
		||||
            f.render_widget(player_map, maps_chunks[0]);
 | 
			
		||||
            f.render_widget(opponent_map, maps_chunks[2]);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Render a single map
 | 
			
		||||
            if self.can_fire() {
 | 
			
		||||
                f.render_widget(opponent_map, chunks[1]);
 | 
			
		||||
                f.render_widget(opponent_map, chunks[2]);
 | 
			
		||||
            } else {
 | 
			
		||||
                f.render_widget(player_map, chunks[1]);
 | 
			
		||||
                f.render_widget(player_map, chunks[2]);
 | 
			
		||||
                drop(opponent_map);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -573,7 +598,7 @@ impl GameScreen {
 | 
			
		||||
                        .map(|_| Constraint::Percentage(100 / buttons.len() as u16))
 | 
			
		||||
                        .collect::<Vec<_>>(),
 | 
			
		||||
                )
 | 
			
		||||
                .split(chunks[2]);
 | 
			
		||||
                .split(chunks[3]);
 | 
			
		||||
 | 
			
		||||
            for (idx, b) in buttons.into_iter().enumerate() {
 | 
			
		||||
                let target = centered_rect_size(
 | 
			
		||||
 
 | 
			
		||||
@@ -78,6 +78,7 @@ impl PrintableMap for PrintableCurrentGameMapStatus {
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Default)]
 | 
			
		||||
pub struct CurrentGameStatus {
 | 
			
		||||
    pub remaining_time_for_strike: Option<u64>,
 | 
			
		||||
    pub rules: GameRules,
 | 
			
		||||
    pub your_map: CurrentGameMapStatus,
 | 
			
		||||
    pub opponent_map: CurrentGameMapStatus,
 | 
			
		||||
 
 | 
			
		||||
@@ -257,6 +257,9 @@ impl Game {
 | 
			
		||||
    /// Get current game status for a specific player
 | 
			
		||||
    fn get_game_status_for_player(&self, id: usize) -> 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(),
 | 
			
		||||
            your_map: self.player_map(id).current_map_status(false),
 | 
			
		||||
            opponent_map: self
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user