use crate::app::round_state::Countdown;
use crate::sim::{Board, TideEvent};
use bevy::prelude::*;
pub(super) const RISE: f32 = 56.0;
pub(super) const SWELL: f32 = 5.0;
fn heaving(board: &Board, remaining: u64) -> bool {
remaining <= crate::app::hud::urgency_band(board.rules().round_length, crate::sim::SURGE_TICKS)
}
pub(super) fn climb(
board: &Board,
countdown: Option<Countdown>,
swell_clock: f32,
still: bool,
) -> (f32, f32) {
let Some(countdown) = countdown else {
return (0.0, 0.0);
};
let elapsed = countdown.elapsed();
let swell = if heaving(board, countdown.remaining) && !still {
(swell_clock * 6.0).sin() * SWELL
} else {
0.0
};
(elapsed, swell)
}
pub(super) fn mania_tint(board: &Board) -> Option<Color> {
match board.running_event()? {
(TideEvent::CrabMania, _) => Some(Color::srgba(0.35, 0.72, 0.55, 0.65)),
(TideEvent::GullMania, _) => Some(Color::srgba(0.35, 0.38, 0.48, 0.7)),
(
TideEvent::Monopoly
| TideEvent::GullAttack
| TideEvent::SpeedUp
| TideEvent::SlowDown
| TideEvent::FreshSand
| TideEvent::CastleSwap
| TideEvent::RightClaws,
_,
) => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::app::round_state::countdown;
#[test]
fn a_call_landing_in_a_mania_keeps_the_sea_tinted() {
let mut board = Board::new(9, 7, 1);
assert_eq!(mania_tint(&board), None, "calm water before anything");
board.force_tide_event(TideEvent::CrabMania, 0);
let tinted = mania_tint(&board);
assert!(tinted.is_some(), "a crab mania tints the water");
for _ in 0..30 {
board.tick_idle();
}
board.force_tide_event(TideEvent::SpeedUp, 0);
assert_eq!(
board.last_event().map(|(event, _)| event),
Some(TideEvent::SpeedUp),
"the call is the last event now"
);
assert_eq!(mania_tint(&board), tinted, "and the mania still runs");
while board.running_event().is_some() {
board.tick_idle();
}
assert_eq!(mania_tint(&board), None, "blue again once both are done");
}
fn timed(round: u32, elapsed: u32) -> Board {
let mut board = Board::new(9, 7, 1);
board.rules_mut().round_length = Some(round);
for _ in 0..elapsed {
board.tick_idle();
}
board
}
fn sea(board: &Board, swell_clock: f32, still: bool) -> (f32, f32) {
climb(board, countdown(board, false), swell_clock, still)
}
#[test]
fn a_board_with_no_clock_has_no_tide() {
let board = Board::new(9, 7, 1);
assert_eq!(sea(&board, 3.0, false), (0.0, 0.0));
}
#[test]
fn an_untimed_puzzle_tide_climbs_with_its_clock() {
let mut board = Board::new(9, 7, 1);
for _ in 0..crate::sim::PUZZLE_TICK_LIMIT - 30 {
board.tick_idle();
}
let clock = countdown(&board, true);
assert_eq!(clock.map(|c| c.remaining), Some(30), "the HUD's figure");
let (rise, swell) = climb(&board, clock, 0.3, false);
assert!(rise > 0.95, "nearly all the way at the end: {rise}");
assert_ne!(swell, 0.0, "and heaving");
}
#[test]
fn the_sea_climbs_over_the_round_and_heaves_at_the_end() {
let early = timed(3600, 30);
let (rise, swell) = sea(&early, 0.3, false);
assert!(rise < 0.05, "barely up at the start: {rise}");
assert_eq!(swell, 0.0, "calm early on");
let late = timed(3600, 3590);
let (rise, swell) = sea(&late, 0.3, false);
assert!(rise > 0.95, "nearly all the way at the end: {rise}");
assert_ne!(swell, 0.0, "and heaving");
assert_eq!(sea(&late, 0.3, true).1, 0.0, "but still when asked");
assert_eq!(
sea(&late, 0.3, false),
(rise, swell),
"held where it got to"
);
}
}