|
|
|
@ -1,5 +1,6 @@
|
|
|
|
// TODO: Rearchitected so it's Cell and Coordiantes not Cell { position { coordiantes } }
|
|
|
|
// TODO: Rearchitected so it's Cell and Coordiantes not Cell { position { coordiantes } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use bevy::picking::hover::Hovered;
|
|
|
|
use engine::*;
|
|
|
|
use engine::*;
|
|
|
|
|
|
|
|
|
|
|
|
const GREY_TILE: &str = "tileGrey_01.png";
|
|
|
|
const GREY_TILE: &str = "tileGrey_01.png";
|
|
|
|
@ -25,6 +26,7 @@ fn main() {
|
|
|
|
.init_resource::<Coordinates>()
|
|
|
|
.init_resource::<Coordinates>()
|
|
|
|
.init_state::<SimulationState>()
|
|
|
|
.init_state::<SimulationState>()
|
|
|
|
.add_message::<Lifecycle>()
|
|
|
|
.add_message::<Lifecycle>()
|
|
|
|
|
|
|
|
.add_message::<SimulationState>()
|
|
|
|
.add_systems(Startup, setup)
|
|
|
|
.add_systems(Startup, setup)
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
@ -43,6 +45,14 @@ fn main() {
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
simulation_step.run_if(input_just_pressed(KeyCode::Space)),
|
|
|
|
simulation_step.run_if(input_just_pressed(KeyCode::Space)),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
.add_systems(
|
|
|
|
|
|
|
|
Update,
|
|
|
|
|
|
|
|
simulation_step.run_if(in_state(SimulationState::Run)).run_if(cooldown_secs(1.0)),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.add_systems(
|
|
|
|
|
|
|
|
Update,
|
|
|
|
|
|
|
|
simulation_step.run_if(on_message::<SimulationState>),
|
|
|
|
|
|
|
|
)
|
|
|
|
.add_systems(Update, cell_lifecycle.run_if(on_message::<Lifecycle>))
|
|
|
|
.add_systems(Update, cell_lifecycle.run_if(on_message::<Lifecycle>))
|
|
|
|
.add_observer(on_add_ui_button)
|
|
|
|
.add_observer(on_add_ui_button)
|
|
|
|
.add_observer(on_add_coordinates)
|
|
|
|
.add_observer(on_add_coordinates)
|
|
|
|
@ -86,6 +96,7 @@ fn on_add_ui_button(
|
|
|
|
.entity(trigger.entity)
|
|
|
|
.entity(trigger.entity)
|
|
|
|
.insert((
|
|
|
|
.insert((
|
|
|
|
Button,
|
|
|
|
Button,
|
|
|
|
|
|
|
|
Hovered::default(),
|
|
|
|
BackgroundColor(GRAY.into()),
|
|
|
|
BackgroundColor(GRAY.into()),
|
|
|
|
ImageNode {
|
|
|
|
ImageNode {
|
|
|
|
image: server.load(GREY_TILE),
|
|
|
|
image: server.load(GREY_TILE),
|
|
|
|
@ -130,14 +141,33 @@ fn setup(mut commands: Commands) {
|
|
|
|
Transform::default(),
|
|
|
|
Transform::default(),
|
|
|
|
))
|
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn(UiButton::Power);
|
|
|
|
parent.spawn(UiButton::Power).observe(shutdown);
|
|
|
|
parent.spawn(UiButton::Pause);
|
|
|
|
parent.spawn(UiButton::Pause).observe(pause_sim);
|
|
|
|
parent.spawn(UiButton::Step);
|
|
|
|
parent.spawn(UiButton::Step).observe(step_sim);
|
|
|
|
parent.spawn(UiButton::Play);
|
|
|
|
parent.spawn(UiButton::Play).observe(play_sim);
|
|
|
|
parent.spawn(UiButton::ZoomOut);
|
|
|
|
parent.spawn(UiButton::ZoomOut).observe(zoom_out);
|
|
|
|
parent.spawn(UiButton::ZoomIn);
|
|
|
|
parent.spawn(UiButton::ZoomIn).observe(zoom_in);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn shutdown(_trigger: On<Pointer<Press>>, mut writer: MessageWriter<AppExit>) {
|
|
|
|
|
|
|
|
writer.write(AppExit::Success);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pause_sim(_trigger: On<Pointer<Press>>, mut next: ResMut<NextState<SimulationState>>) {
|
|
|
|
|
|
|
|
next.set(SimulationState::Pause);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn step_sim(_trigger: On<Pointer<Press>>, mut writer: MessageWriter<SimulationState>) {
|
|
|
|
|
|
|
|
writer.write(SimulationState::Step);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn play_sim(_trigger: On<Pointer<Press>>, mut next: ResMut<NextState<SimulationState>>) {
|
|
|
|
|
|
|
|
next.set(SimulationState::Run);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn zoom_out(trigger: On<Pointer<Press>>) {
|
|
|
|
|
|
|
|
todo!()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn zoom_in(trigger: On<Pointer<Press>>) {
|
|
|
|
|
|
|
|
todo!()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Spawn Coordiantes
|
|
|
|
// Spawn Coordiantes
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.spawn((Transform::default(), Visibility::Visible))
|
|
|
|
.spawn((Transform::default(), Visibility::Visible))
|
|
|
|
@ -268,17 +298,20 @@ struct Cell;
|
|
|
|
|
|
|
|
|
|
|
|
// When left mouse clicked, spawn a cell at GridPosition
|
|
|
|
// When left mouse clicked, spawn a cell at GridPosition
|
|
|
|
fn de_spawn_cells(
|
|
|
|
fn de_spawn_cells(
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
coordinates: Res<Coordinates>,
|
|
|
|
coordinates: Res<Coordinates>,
|
|
|
|
cells: Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
cells: Query<(Entity, &Coordinates), With<Cell>>,
|
|
|
|
|
|
|
|
mut lifecycle: MessageWriter<Lifecycle>,
|
|
|
|
|
|
|
|
hovered: Query<&Hovered>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if let Some(e) = cells
|
|
|
|
if hovered.iter().all(|Hovered(h)| !h) {
|
|
|
|
.iter()
|
|
|
|
if let Some(e) = cells
|
|
|
|
.find_map(|(e, c)| (*coordinates == *c).then_some(e))
|
|
|
|
.iter()
|
|
|
|
{
|
|
|
|
.find_map(|(e, c)| (*coordinates == *c).then_some(e))
|
|
|
|
commands.entity(e).despawn();
|
|
|
|
{
|
|
|
|
} else {
|
|
|
|
lifecycle.write(Lifecycle::Dead(e));
|
|
|
|
commands.spawn((coordinates.clone(), Cell));
|
|
|
|
} else {
|
|
|
|
|
|
|
|
lifecycle.write(Lifecycle::Alive(coordinates.clone()));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -292,12 +325,12 @@ fn assert_cell_uniqueness(q: Query<(Entity, &Coordinates), With<Cell>>) {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(States, Debug, Default, Hash, PartialEq, Eq, Clone)]
|
|
|
|
#[derive(States, Debug, Default, Hash, PartialEq, Eq, Clone, Message)]
|
|
|
|
enum SimulationState {
|
|
|
|
enum SimulationState {
|
|
|
|
#[default]
|
|
|
|
#[default]
|
|
|
|
Off,
|
|
|
|
Pause,
|
|
|
|
Step,
|
|
|
|
|
|
|
|
Run,
|
|
|
|
Run,
|
|
|
|
|
|
|
|
Step,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Simulate the Game of Life Rules:
|
|
|
|
/// Simulate the Game of Life Rules:
|
|
|
|
@ -407,8 +440,21 @@ fn cell_lifecycle(
|
|
|
|
commands.spawn((c.clone(), Cell));
|
|
|
|
commands.spawn((c.clone(), Cell));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Lifecycle::Dead(e) => {
|
|
|
|
Lifecycle::Dead(e) => {
|
|
|
|
// TODO: Maybe include the entity instead of the coordinates for Dead
|
|
|
|
|
|
|
|
commands.entity(*e).despawn();
|
|
|
|
commands.entity(*e).despawn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Run condition for running a system every `input` seconds
|
|
|
|
|
|
|
|
fn cooldown_secs(input: f32) -> impl FnMut(Local<Timer>, Res<Time>) -> bool + Clone
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
move |mut timer: Local<Timer>, time: Res<Time>| {
|
|
|
|
|
|
|
|
if *timer == Timer::default() {
|
|
|
|
|
|
|
|
*timer = Timer::from_seconds(input, TimerMode::Repeating);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
timer.tick(time.delta());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
timer.is_finished()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|