#![forbid(missing_docs, unsafe_code)]
#![warn(
clippy::all,
clippy::nursery,
clippy::pedantic,
nonstandard_style,
rustdoc::broken_intra_doc_links
)]
#![allow(clippy::default_trait_access, clippy::module_name_repetitions)]
use bevy::{log, prelude::*, time::common_conditions::on_timer};
use std::{marker::PhantomData, time::Duration};
mod components;
mod resources;
mod systems;
use systems::cells::{handle_cells, handle_new_cells};
use crate::systems::cells::handle_removed_cells;
pub use components::*;
pub use resources::*;
#[cfg(feature = "2D")]
pub type GameOfLife2dPlugin = CellularAutomatonPlugin<components::MooreCell2d, ConwayCellState>;
#[cfg(feature = "3D")]
pub type GameOfLife3dPlugin = CellularAutomatonPlugin<components::MooreCell3d, ConwayCell4555State>;
#[cfg(feature = "2D")]
pub type ImmigrationGame2dPlugin =
CellularAutomatonPlugin<components::MooreCell2d, ImmigrationCellState>;
#[cfg(feature = "3D")]
pub type ImmigrationGame3dPlugin =
CellularAutomatonPlugin<components::MooreCell3d, ImmigrationCellState>;
#[cfg(feature = "2D")]
pub type RainbowGame2dPlugin = CellularAutomatonPlugin<components::MooreCell2d, RainbowCellState>;
#[cfg(feature = "3D")]
pub type RainbowGame3dPlugin = CellularAutomatonPlugin<components::MooreCell3d, RainbowCellState>;
#[cfg(feature = "2D")]
pub type WireWorld2dPlugin =
CellularAutomatonPlugin<components::MooreCell2d, components::WireWorldCellState>;
#[cfg(feature = "3D")]
pub type WireWorld3dPlugin =
CellularAutomatonPlugin<components::MooreCell3d, components::WireWorldCellState>;
#[cfg(feature = "2D")]
pub type CyclicColors2dPlugin<const N: usize> =
CellularAutomatonPlugin<components::MooreCell2d, CyclicColorCellState<N>>;
#[cfg(feature = "3D")]
pub type CyclicColors3dPlugin<const N: usize> =
CellularAutomatonPlugin<components::MooreCell3d, CyclicColorCellState<N>>;
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemSet)]
pub enum LifeSystemSet {
NewCells,
RemovedCells,
CellUpdate,
}
pub struct CellularAutomatonPlugin<C, S> {
pub tick_time_step: Option<f64>,
pub use_cell_map: bool,
pub phantom_c: PhantomData<C>,
pub phantom_s: PhantomData<S>,
}
impl<C: Cell, S: CellState> Plugin for CellularAutomatonPlugin<C, S> {
fn build(&self, app: &mut App) {
if self.use_cell_map {
app.insert_resource(CellMap::<C>::default());
app.add_systems(
PostUpdate,
(
handle_new_cells::<C>.in_set(LifeSystemSet::NewCells),
handle_removed_cells::<C>.in_set(LifeSystemSet::RemovedCells),
),
);
}
if let Some(time_step) = self.tick_time_step {
let duration = Duration::from_secs_f64(time_step);
app.add_systems(
Update,
handle_cells::<C, S>
.run_if(on_timer(duration))
.in_set(LifeSystemSet::CellUpdate),
);
} else {
app.add_systems(
Update,
handle_cells::<C, S>.in_set(LifeSystemSet::CellUpdate),
);
}
#[cfg(feature = "auto-coloring")]
{
#[cfg(feature = "2D")]
{
app.add_systems(Update, systems::coloring::color_sprites::<S>);
}
#[cfg(feature = "3D")]
{
log::warn!("No auto coloring is available for 3D materials");
}
}
log::info!("Loaded cellular automaton plugin");
}
}
impl<C, S> CellularAutomatonPlugin<C, S> {
#[must_use]
#[inline]
pub const fn new() -> Self {
Self {
tick_time_step: None,
use_cell_map: false,
phantom_c: PhantomData,
phantom_s: PhantomData,
}
}
#[must_use]
#[inline]
pub const fn with_time_step(mut self, tick_time_step: f64) -> Self {
self.tick_time_step = Some(tick_time_step);
self
}
#[must_use]
#[inline]
pub const fn with_cell_map(mut self) -> Self {
self.use_cell_map = true;
self
}
}
impl<C, S> Default for CellularAutomatonPlugin<C, S> {
fn default() -> Self {
Self::new()
}
}