1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::path::{Path, PathBuf};

use bevy::prelude::*;
use de_types::player::{Player, PlayerRange};
use tinyvec::{array_vec, ArrayVec};

/// This resource is automatically removed when
/// [`crate::state::AppState::InGame`] is exited.
#[derive(Resource)]
pub struct GameConfig {
    map_path: PathBuf,
    multiplayer: bool,
    locals: LocalPlayers,
}

impl GameConfig {
    pub fn new<P: Into<PathBuf>>(map_path: P, multiplayer: bool, locals: LocalPlayers) -> Self {
        Self {
            map_path: map_path.into(),
            multiplayer,
            locals,
        }
    }

    pub fn map_path(&self) -> &Path {
        self.map_path.as_path()
    }

    pub fn multiplayer(&self) -> bool {
        self.multiplayer
    }

    pub fn locals(&self) -> &LocalPlayers {
        &self.locals
    }
}

/// Info about players directly controlled or simulated on this computer.
///
/// "Playable" is the player directly controlled by the user of this computer.
///
/// "Local" is either a "Playable" player or a player controlled by the AI on
/// this computer. During a multiplayer game, each AI player is simulated by
/// exactly one computer.
pub struct LocalPlayers {
    playable: Player,
    locals: ArrayVec<[Player; Player::MAX_PLAYERS]>,
}

impl LocalPlayers {
    pub fn from_max_player(playable: Player, max_player: Player) -> Self {
        Self::from_range(playable, PlayerRange::up_to(max_player))
    }

    pub fn from_range(playable: Player, locals: PlayerRange) -> Self {
        Self::new(playable, locals.collect())
    }

    pub fn from_single(playable: Player) -> Self {
        Self::new(playable, array_vec!(_ => playable))
    }

    /// # Arguments
    ///
    /// * `playable` - the player controlled locally by the user.
    ///
    /// * `locals` - other players simulated locally on this computer. It must
    ///   include `playable`.
    pub fn new(playable: Player, locals: ArrayVec<[Player; Player::MAX_PLAYERS]>) -> Self {
        assert!((*locals).contains(&playable));
        Self { playable, locals }
    }

    /// The player controlled directly by the user on this computer.
    pub fn playable(&self) -> Player {
        self.playable
    }

    pub fn locals(&self) -> &[Player] {
        self.locals.as_slice()
    }

    /// Returns true if the player is controlled directly by the user on this
    /// computer.
    pub fn is_playable(&self, player: Player) -> bool {
        self.playable == player
    }

    /// Returns true if the player is simulated by this computer.
    pub fn is_local(&self, player: Player) -> bool {
        self.locals.contains(&player)
    }
}

/// System condition which returns true during a multiplayer game.
pub fn is_multiplayer(config: Option<Res<GameConfig>>) -> bool {
    match config {
        Some(config) => config.multiplayer(),
        None => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_game_config() {
        let config = GameConfig::new(
            "/some/path",
            false,
            LocalPlayers::from_max_player(Player::Player1, Player::Player4),
        );
        assert_eq!(config.map_path().to_string_lossy(), "/some/path");
    }
}