use super::*;
pub const SPECTATOR_EVENTS: [TideEvent; 7] = [
TideEvent::CrabMania,
TideEvent::GullMania,
TideEvent::SpeedUp,
TideEvent::SlowDown,
TideEvent::FreshSand,
TideEvent::CastleSwap,
TideEvent::RightClaws,
];
pub(super) const WINDOW: f32 = 3.0;
pub(super) const COOLDOWN: f32 = 25.0;
#[derive(Default)]
pub struct TideVote {
pub votes: SpectatorVotes,
pub crowd: Crowd,
pub crowd_said: f32,
pub pending_call: Option<TideEvent>,
}
impl TideVote {
pub fn leave_round(&mut self) {
self.pending_call = None;
self.votes.forget_all();
self.crowd = Crowd::default();
}
}
#[derive(Default)]
pub struct SpectatorVotes {
ballots: Vec<(usize, usize)>,
open_for: f32,
cooldown: f32,
}
impl SpectatorVotes {
pub fn cast(&mut self, voter: usize, event: u8) {
let Some(at) = SPECTATOR_EVENTS
.iter()
.position(|e| e.index() == usize::from(event))
else {
return; };
if self.cooldown > 0.0 {
return;
}
if self.open_for <= 0.0 {
self.ballots.clear();
self.open_for = WINDOW;
}
match self.ballots.iter_mut().find(|(who, _)| *who == voter) {
Some(ballot) => ballot.1 = at,
None => self.ballots.push((voter, at)),
}
}
pub fn voter_left(&mut self, peer: usize) {
self.ballots
.retain_mut(|(who, _)| crate::app::net::renumber(who, peer));
}
pub fn settle(&mut self, seconds: f32) -> Option<TideEvent> {
self.cooldown = (self.cooldown - seconds).max(0.0);
if self.open_for <= 0.0 {
return None;
}
self.open_for -= seconds;
if self.open_for > 0.0 {
return None;
}
self.open_for = 0.0;
self.cooldown = COOLDOWN;
let mut tally = [0u16; SPECTATOR_EVENTS.len()];
for (_, at) in self.ballots.drain(..) {
tally[at] = tally[at].saturating_add(1);
}
let (at, votes) = tally
.iter()
.enumerate()
.max_by_key(|(at, votes)| (**votes, std::cmp::Reverse(*at)))?;
(*votes > 0).then(|| SPECTATOR_EVENTS[at])
}
pub fn forget_open(&mut self) {
self.open_for = 0.0;
self.ballots.clear();
}
pub fn forget_all(&mut self) {
*self = SpectatorVotes::default();
}
pub fn open(&self) -> Option<f32> {
(self.open_for > 0.0).then_some(self.open_for)
}
pub fn waiting(&self) -> Option<f32> {
(self.cooldown > 0.0).then_some(self.cooldown)
}
}
#[derive(Component)]
pub struct SpectatorCardUi;
#[derive(Resource, Default)]
pub struct SpectatorCard(pub bool);
pub fn spectator_vote_input(
mut card: ListCard<SpectatorCardUi, SpectatorCard>,
mut keyboard: crate::app::keycaps::KeyboardMut,
settings: Res<crate::app::settings::GameSettings>,
watching: Watching<PickCard>,
sim: Res<crate::app::Sim>,
mut online: ResMut<Online>,
) {
if !is_spectating(&online) || !watching.free() || !sim.0.events_enabled() {
if card.is_up() {
card.shut();
}
return;
}
if !card.is_up() {
if keyboard.just('E') && !watching.other_up() {
spawn_card(card.raise(), &settings);
}
return;
}
if keyboard.keys.just_pressed(KeyCode::Escape) || keyboard.just('E') {
card.shut();
crate::app::menu_ui::spend(&mut keyboard.keys, &[KeyCode::Escape]);
return;
}
if let Some(at) = crate::app::menu_ui::number_pressed(&keyboard.keys, SPECTATOR_EVENTS.len()) {
if let Some(session) = &mut online.0 {
session.transport.send(NetMsg::SpectatorVote {
event: SPECTATOR_EVENTS[at].index() as u8,
});
}
card.shut();
}
}
pub(crate) fn spawn_card(commands: &mut Commands, settings: &crate::app::settings::GameSettings) {
let tr = settings.tr();
let rows = SPECTATOR_EVENTS.into_iter().enumerate().map(|(at, event)| {
(
format!("{} {}", at + 1, tr.events[event.index()]),
crate::app::palette::PARCHMENT,
)
});
spawn_list_card(commands, SpectatorCardUi, tr.spectator_call_title, rows);
}