use super::*;
#[derive(Resource, Default)]
pub struct CustomBeaches(pub Vec<Beach>);
pub struct Beach {
pub level: crate::sim::Level,
pub(super) wire: Vec<u8>,
}
impl Beach {
pub(super) fn new(level: crate::sim::Level) -> Beach {
let wire = crate::lzw::compress(level.to_text().as_bytes(), 8);
Beach { level, wire }
}
pub fn too_big_to_send(&self) -> bool {
self.wire.len() > crate::transport::MAX_BEACH_BYTES
}
}
pub fn refresh_custom_beaches(mut beaches: ResMut<CustomBeaches>) {
beaches.0 = crate::app::campaign::custom_arenas(crate::app::campaign::load_custom_levels())
.into_iter()
.map(Beach::new)
.collect();
}
impl CustomBeaches {
pub fn fitting(&self, seats: u8) -> Vec<&Beach> {
self.0
.iter()
.filter(|beach| beach.level.seats() >= seats)
.collect()
}
}
pub fn beaches_note(
config: &MatchConfig,
tr: &crate::app::i18n::Tr,
beaches: &CustomBeaches,
) -> Option<String> {
let all_too_small = !beaches.0.is_empty() && beaches.fitting(config.seats).is_empty();
all_too_small.then(|| fill(tr.map_none_seats, &[("n", &config.seats.to_string())]))
}
pub fn cycle_map(config: &mut MatchConfig, turn: Turn, beaches: &CustomBeaches) {
let beaches = beaches.fitting(config.seats).len();
if config.map == MapChoice::Custom {
let next = config.custom as i64 + i64::from(turn.signum());
if (0..beaches as i64).contains(&next) {
config.custom = next as usize;
return;
}
}
config.map = config.map.cycled(turn);
if config.map == MapChoice::Custom {
if beaches == 0 {
config.map = config.map.cycled(turn);
} else {
config.custom = match turn {
Turn::Right => 0,
Turn::Left => beaches - 1,
};
}
}
}
pub fn map_label(
config: &MatchConfig,
tr: &crate::app::i18n::Tr,
beaches: &CustomBeaches,
) -> String {
if config.map == MapChoice::Custom {
return beaches
.fitting(config.seats)
.get(config.custom)
.map_or_else(
|| tr.map_names[MapChoice::GenXl.index()].to_string(),
|beach| {
let template = match beach.too_big_to_send() {
true => tr.map_custom_local,
false => tr.map_custom,
};
fill(template, &[("n", &beach.level.name)])
},
);
}
tr.map_names[config.map.index()].to_string()
}
pub fn beach_bytes(config: &MatchConfig, seats: u8, beaches: &CustomBeaches) -> Vec<u8> {
if config.map != MapChoice::Custom {
return Vec::new();
}
beaches
.fitting(config.seats)
.get(config.custom)
.filter(|beach| beach.level.seats() >= seats)
.filter(|beach| {
let sendable = !beach.too_big_to_send();
if !sendable {
warn!(
"{:?} is too big to send ({} bytes packed, {} allowed): \
the round falls back to a generated beach",
beach.level.name,
beach.wire.len(),
crate::transport::MAX_BEACH_BYTES
);
}
sendable
})
.map(|beach| beach.wire.clone())
.unwrap_or_default()
}
pub fn beach_from(bytes: &[u8]) -> Option<crate::sim::Level> {
let text = String::from_utf8(crate::lzw::decompress(bytes, 8)?).ok()?;
crate::sim::Level::parse(&text).ok()
}