use bevy::prelude::*;
use jackdaw_api::pie::PlayState;
use jackdaw_api::prelude::*;
use jackdaw_jsn::SceneJsnAst;
#[derive(Resource, Default)]
pub struct PrePlayScene {
snapshot: Option<SceneJsnAst>,
}
#[derive(Component, Clone, Copy, Debug, PartialEq, Eq)]
pub enum PieButton {
Play,
Pause,
Stop,
}
#[derive(Component, Clone, Copy, Debug, Default)]
pub struct GameSpawned;
pub struct PiePlugin;
impl Plugin for PiePlugin {
fn build(&self, app: &mut App) {
app.init_state::<PlayState>()
.init_resource::<PrePlayScene>()
.add_observer(wire_pie_button)
.add_observer(tag_game_spawned);
}
}
pub(crate) fn add_to_extension(ctx: &mut ExtensionContext) {
ctx.register_operator::<PiePlayOp>()
.register_operator::<PiePauseOp>()
.register_operator::<PieStopOp>();
}
fn play_is_stopped_or_paused(state: Res<State<PlayState>>) -> bool {
!matches!(state.get(), PlayState::Playing)
}
fn play_is_playing(state: Res<State<PlayState>>) -> bool {
*state.get() == PlayState::Playing
}
fn play_is_running(state: Res<State<PlayState>>) -> bool {
*state.get() != PlayState::Stopped
}
#[operator(
id = "pie.play",
label = "Play",
description = "Start the game running in the editor.",
is_available = play_is_stopped_or_paused
)]
pub(crate) fn pie_play(_: In<OperatorParameters>, mut commands: Commands) -> OperatorResult {
commands.queue(handle_play);
OperatorResult::Finished
}
#[operator(
id = "pie.pause",
label = "Pause",
description = "Pause the running game.",
is_available = play_is_playing
)]
pub(crate) fn pie_pause(_: In<OperatorParameters>, mut commands: Commands) -> OperatorResult {
commands.queue(handle_pause);
OperatorResult::Finished
}
#[operator(
id = "pie.stop",
label = "Stop",
description = "Stop the running game and restore the scene.",
is_available = play_is_running
)]
pub(crate) fn pie_stop(_: In<OperatorParameters>, mut commands: Commands) -> OperatorResult {
commands.queue(handle_stop);
OperatorResult::Finished
}
fn tag_game_spawned(
trigger: On<Add, Transform>,
state: Res<State<PlayState>>,
already_tagged: Query<(), With<GameSpawned>>,
mut commands: Commands,
) {
if *state.get() != PlayState::Playing {
return;
}
let entity = trigger.event_target();
if already_tagged.get(entity).is_ok() {
return;
}
commands.entity(entity).insert(GameSpawned);
}
fn wire_pie_button(
trigger: On<Add, PieButton>,
buttons: Query<&PieButton>,
mut commands: Commands,
) {
let entity = trigger.event_target();
let Ok(kind) = buttons.get(entity).copied() else {
return;
};
let op_id = match kind {
PieButton::Play => PiePlayOp::ID,
PieButton::Pause => PiePauseOp::ID,
PieButton::Stop => PieStopOp::ID,
};
commands
.entity(entity)
.observe(move |_: On<Pointer<Click>>, mut commands: Commands| {
commands
.operator(op_id)
.settings(CallOperatorSettings {
execution_context: ExecutionContext::Invoke,
creates_history_entry: false,
})
.call();
});
}
pub fn handle_play(world: &mut World) {
let current = world.resource::<State<PlayState>>().get().clone();
match current {
PlayState::Stopped => {
let snapshot = world.resource::<SceneJsnAst>().clone();
world.resource_mut::<PrePlayScene>().snapshot = Some(snapshot);
world
.resource_mut::<NextState<PlayState>>()
.set(PlayState::Playing);
info!("PIE: Play (fresh start, scene snapshot captured)");
}
PlayState::Paused => {
world
.resource_mut::<NextState<PlayState>>()
.set(PlayState::Playing);
info!("PIE: Play (resumed)");
}
PlayState::Playing => {}
}
}
pub fn handle_pause(world: &mut World) {
if *world.resource::<State<PlayState>>().get() == PlayState::Playing {
world
.resource_mut::<NextState<PlayState>>()
.set(PlayState::Paused);
info!("PIE: Pause");
}
}
pub fn handle_stop(world: &mut World) {
let current = world.resource::<State<PlayState>>().get().clone();
if current == PlayState::Stopped {
return;
}
if let Some(snapshot) = world.resource_mut::<PrePlayScene>().snapshot.take() {
crate::scene_io::apply_ast_to_world(world, &snapshot);
info!("PIE: Stop (scene restored from snapshot)");
} else {
info!("PIE: Stop (no snapshot to restore)");
}
world
.resource_mut::<NextState<PlayState>>()
.set(PlayState::Stopped);
}