Skip to main content

nil_core/world/player/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4#[cfg(test)]
5mod tests;
6
7use crate::city::City;
8use crate::error::{Error, Result};
9use crate::military::Military;
10use crate::military::maneuver::ManeuverDirection;
11use crate::player::{Player, PlayerId, PlayerManager, PlayerStatus};
12use crate::resources::maintenance::Maintenance;
13use crate::ruler::Ruler;
14use crate::world::World;
15
16impl World {
17  #[inline]
18  pub fn player_manager(&self) -> &PlayerManager {
19    &self.player_manager
20  }
21
22  #[inline]
23  pub fn player(&self, id: &PlayerId) -> Result<&Player> {
24    self.player_manager.player(id)
25  }
26
27  #[inline]
28  pub(crate) fn player_mut(&mut self, id: &PlayerId) -> Result<&mut Player> {
29    self.player_manager.player_mut(id)
30  }
31
32  pub fn players(&self) -> impl Iterator<Item = &Player> {
33    self.player_manager.players()
34  }
35
36  #[inline]
37  pub fn get_player_maintenance(&self, player: &PlayerId) -> Result<Maintenance> {
38    self.get_maintenance(player)
39  }
40
41  pub fn get_player_military(&self, player: &PlayerId) -> Result<Military> {
42    let ruler = Ruler::from(player);
43    let coords = self.continent.coords_of(player);
44    let mut military = self.military.intersection(coords)?;
45
46    // Ensures that maneuvers whose origin or destination
47    // is not among the player's coords are also included.
48    for maneuver in self.military.maneuvers() {
49      if !military.has_maneuver(maneuver.id())
50        && self
51          .military
52          .army(maneuver.army())?
53          .owner()
54          .eq(&ruler)
55      {
56        military.insert_maneuver(maneuver.clone());
57      }
58    }
59
60    military.retain_maneuvers(|maneuver| {
61      // Should not include foreign armies returning home.
62      if let ManeuverDirection::Returning = maneuver.direction() {
63        self
64          .military
65          .army(maneuver.army())
66          .is_ok_and(|army| army.is_owned_by(&ruler))
67      } else {
68        true
69      }
70    });
71
72    Ok(military)
73  }
74
75  #[inline]
76  pub fn has_player(&self, id: &PlayerId) -> bool {
77    self.player_manager.has(id)
78  }
79
80  pub fn has_any_active_player(&self) -> bool {
81    self
82      .player_manager
83      .players()
84      .any(Player::is_active)
85  }
86
87  pub fn set_player_status(&mut self, id: &PlayerId, status: PlayerStatus) -> Result<()> {
88    *self
89      .player_manager
90      .player_mut(id)?
91      .status_mut() = status;
92
93    self.emit_player(id.clone())?;
94
95    Ok(())
96  }
97
98  pub fn spawn_player(&mut self, mut player: Player) -> Result<()> {
99    let id = player.id();
100    if self.has_player(&id) {
101      Err(Error::PlayerAlreadySpawned(id))
102    } else {
103      let (coord, field) = self.find_spawn_point()?;
104      *field = City::builder(coord)
105        .name(&*id)
106        .owner(&id)
107        .build()
108        .into();
109
110      *player.status_mut() = PlayerStatus::Active;
111      self.player_manager.manage(player)?;
112
113      self.emit_public_city(coord)?;
114
115      Ok(())
116    }
117  }
118}