diff --git a/battlefox/src/mapmanager.rs b/battlefox/src/mapmanager.rs index 3714b89..92c0c95 100644 --- a/battlefox/src/mapmanager.rs +++ b/battlefox/src/mapmanager.rs @@ -35,6 +35,8 @@ pub struct PopState { pub pool: MapPool, /// At `min_players` or more players, activate this pool. Unless a pool with even higher `min_players` exists. pub min_players: usize, + /// The amount of map history to be excluded when generating a new vote + pub excluded_maps_count: Option, } impl PopState { @@ -128,6 +130,9 @@ impl Plugin for MapManager { // Err(MapListError::Rcon(r)) => return Err(r), Err(mle) => error!("While starting up MapManager: {:?}. MapManager is *not* starting now!", mle), } + + // Populate the current map in the history + let _ = self.current_map(&bf4).await; } async fn event(self: Arc, bf4: Arc, event: Event) -> RconResult<()> { @@ -393,6 +398,34 @@ impl MapManager { Some(hist[0]) } } + + pub fn recent_maps(&self) -> Vec { + let hist = { + let inner = self.inner.lock().unwrap(); + inner.map_history.clone() + }; + + let popstate = { + let lock = self.inner.lock().unwrap(); + lock.pop_state.clone() + }; + + hist.iter() + .take(popstate.excluded_maps_count.unwrap_or(1)) + .cloned() + .collect() + } + + pub fn is_recently_played(&self, map: &Map) -> bool { + let recent_maps = self.recent_maps(); + + for m in recent_maps.iter() { + if map.eq(&m) { + return true; + } + }; + false + } } impl std::fmt::Debug for MapManager { diff --git a/battlefox/src/mapmanager/pool.rs b/battlefox/src/mapmanager/pool.rs index 692133c..cf0cf94 100644 --- a/battlefox/src/mapmanager/pool.rs +++ b/battlefox/src/mapmanager/pool.rs @@ -204,6 +204,18 @@ impl MapPool { .collect(), } } + + /// Returns a new map pool which contains the same items, except with any with `map` removed. + pub fn without_many_vec(&self, maps: &Vec) -> Self { + Self { + pool: self + .pool + .iter() + .filter(|&mip| !maps.contains(&mip.map)) + .cloned() + .collect(), + } + } } #[cfg(test)] diff --git a/battlefox/src/mapvote.rs b/battlefox/src/mapvote.rs index f785c12..8ec149f 100644 --- a/battlefox/src/mapvote.rs +++ b/battlefox/src/mapvote.rs @@ -217,9 +217,9 @@ impl Inner { } } - fn set_up_new_vote(&mut self, n_options: usize, without: Option) { + fn set_up_new_vote(&mut self, n_options: usize, without: Option>) { let pool = if let Some(without) = without { - self.popstate.pool.without(without) + self.popstate.pool.without_many_vec(&without) } else { self.popstate.pool.clone() }; @@ -761,22 +761,31 @@ impl Mapvote { if inner.popstate.pool.contains_map(map) { // make sure this VIP hasn't exceeded their nomination limit this round. if inner.vip_n_noms(&player) < self.config.max_noms_per_vip { - // phew, that was a lot of ifs... - info!("Player {} has nominated {} (vehicles: {:?})", player.name, map.Pretty(), vehicles); - inner.vip_nom(&player, map, vehicles); - info!("The new alternatives are {:?}.", inner.alternatives); - - let announce = self.config.announce_nominator.unwrap_or(true); - if announce { - futures.push(bf4.say_lines(vec![ - format!("Our beloved VIP {} has nominated {}!", &*player, map.Pretty()), - format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()), - ], Visibility::All)); + // Check if the nominated map has recently been played + if !self.mapman.is_recently_played(&map) { + // phew, that was a lot of ifs... + info!("Player {} has nominated {} (vehicles: {:?})", player.name, map.Pretty(), vehicles); + inner.vip_nom(&player, map, vehicles); + info!("The new alternatives are {:?}.", inner.alternatives); + + let announce = self.config.announce_nominator.unwrap_or(true); + if announce { + futures.push(bf4.say_lines(vec![ + format!("Our beloved VIP {} has nominated {}!", &*player, map.Pretty()), + format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()), + ], Visibility::All)); + } + else { + futures.push(bf4.say_lines(vec![ + format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()), + ], Visibility::All)); + } } else { futures.push(bf4.say_lines(vec![ - format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()), - ], Visibility::All)); + format!("Apologies, {}, that map was just recently played.", &*player), + "Try nominating some other map".to_string(), + ], player.get().into())); } } else { futures.push(bf4.say_lines(vec![ @@ -999,7 +1008,7 @@ impl Mapvote { } async fn handle_round_over(&self, bf4: &Arc) { - let current_map = self.mapman.current_map(bf4).await; + let recent_maps = self.mapman.recent_maps(); self.broadcast_status(bf4).await; // send everyone the voting options. // let's wait like 10 seconds because people might still vote in the end screen. let _ = bf4.say(format!("Mapvote is still going for {}s! Hurry!", self.config.endscreen_votetime.as_secs()), Visibility::All).await; @@ -1019,7 +1028,7 @@ impl Mapvote { // get each player's votes, so we can simulate how the votes go later. let assignment = inner.to_assignment(); - inner.set_up_new_vote(self.config.n_options, current_map); + inner.set_up_new_vote(self.config.n_options, Some(recent_maps)); Some((profile, assignment, inner.anim_override_override.clone())) } else { None