use super::*;
pub const PICKS_OPEN_FOR: u32 = 30 * crate::sim::TICKS_PER_SECOND;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Picks {
pub counts: [u8; crate::sim::MAX_PLAYERS],
pub open: u8,
}
impl Picks {
pub fn any(&self) -> bool {
self.counts.iter().any(|&n| n > 0)
}
pub fn line(&self, name: impl Fn(u8) -> String) -> String {
let mut called: Vec<(u8, u8)> = (0..crate::sim::MAX_PLAYERS as u8)
.map(|seat| (seat, self.counts[usize::from(seat)]))
.filter(|&(_, n)| n > 0)
.collect();
called.sort_by_key(|&(seat, n)| (std::cmp::Reverse(n), seat));
called
.into_iter()
.map(|(seat, n)| format!("{} ×{n}", name(seat)))
.collect::<Vec<_>>()
.join(" · ")
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Calls {
pub right: u32,
pub made: u32,
}
#[derive(Component)]
pub struct PickCardUi;
#[derive(Resource, Default)]
pub struct PickCard(pub bool);
pub fn spectator_pick_input(
mut card: ListCard<PickCardUi, PickCard>,
keyboard: crate::app::keycaps::KeyboardMut,
time: Res<Time>,
settings: Res<crate::app::settings::GameSettings>,
watching: Watching<SpectatorCard>,
mut online: ResMut<Online>,
mut resend: Local<f32>,
) {
let crate::app::keycaps::KeyboardMut { mut keys, caps } = keyboard;
let open = online.0.as_ref().is_some_and(|s| s.stands.picks.open > 0);
if !is_spectating(&online) || !watching.free() || watching.other_up() || !open {
if card.is_up() {
card.shut();
}
return;
}
let Some(session) = &mut online.0 else {
return;
};
if crate::app::lobby::once_a_second(&mut resend, time.delta_secs())
&& let Some(seat) = session.stands.my_pick
{
session.transport.send(NetMsg::SpectatorPick { seat });
}
if !card.is_up() {
if caps.just_pressed(&keys, 'P') {
spawn_pick_card(card.raise(), &settings, session);
}
return;
}
if keys.just_pressed(KeyCode::Escape) || caps.just_pressed(&keys, 'P') {
card.shut();
crate::app::menu_ui::spend(&mut keys, &[KeyCode::Escape]);
return;
}
if let Some(seat) = crate::app::menu_ui::number_pressed(&keys, usize::from(session.seats)) {
let seat = seat as u8;
session.stands.my_pick = Some(seat);
session.transport.send(NetMsg::SpectatorPick { seat });
card.shut();
}
}
fn spawn_pick_card(
commands: &mut Commands,
settings: &crate::app::settings::GameSettings,
session: &crate::app::net::OnlineSession,
) {
let tr = settings.tr();
let rows = (0..session.seats).map(|seat| {
let name = crate::app::name_or_label(&session.names, tr, seat);
(
format!("{} {name}", seat + 1),
crate::app::palette::player_color(seat),
)
});
spawn_list_card(commands, PickCardUi, tr.spectator_pick_title, rows);
}
pub fn score_the_call(
sim: Res<crate::app::Sim>,
settings: Res<crate::app::settings::GameSettings>,
mut online: ResMut<Online>,
) {
let Some(seats) = online.0.as_ref().map(|s| s.seats) else {
return;
};
let mode = crate::app::teams::in_play(&settings, &online, seats);
let winners = crate::app::side_panels::leading_seats(sim.0.scores(), seats, mode);
let Some(session) = &mut online.0 else {
return;
};
let Some(seat) = session
.stands
.my_pick
.filter(|_| session.session.watching())
else {
return;
};
let right = winners[usize::from(seat)];
session.stands.calls.made += 1;
session.stands.calls.right += u32::from(right);
session.stands.last_call = Some((seat, right));
}
pub(super) fn say_the_calls(
session: &mut crate::app::net::OnlineSession,
tr: &crate::app::i18n::Tr,
again: bool,
) {
let picks = match session.stands.picks_said {
true => session.stands.picks,
false => session.crowd_picks_now(),
};
if picks != session.stands.picks || again {
session.stands.picks = picks;
session.transport.send(NetMsg::CrowdPicks {
picks: picks.counts,
open: picks.open,
});
}
if picks.open == 0 && !session.stands.picks_said {
session.stands.picks_said = true;
if picks.any() {
let names = &session.names;
let label = |seat: u8| crate::app::name_or_label(names, tr, seat);
let line = crate::app::i18n::fill(tr.crowd_picked, &[("l", &picks.line(label))]);
session.transport.send(NetMsg::chat("", &line));
session.heard.push((String::new(), line));
}
}
}