use std::path::Path; use bevy::color::palettes::css; use bevy::prelude::*; use bevy_egui::EguiPlugin; use uuid::Uuid; use bevy_yoleck::exclusive_systems::{YoleckExclusiveSystemDirective, YoleckExclusiveSystemsQueue}; use bevy_yoleck::vpeol::{prelude::*, vpeol_read_click_on_entity}; use bevy_yoleck::{prelude::*, yoleck_exclusive_system_cancellable, yoleck_map_entity_to_uuid}; use serde::{Deserialize, Serialize}; fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins); let level = std::env::args().nth(1); if let Some(level) = level { // The egui plugin is not needed for the game itself, but GameAssets won't load without it // because it needs `EguiContexts` which cannot be `Option` because it's a custom // `SystemParam`. app.add_plugins(EguiPlugin); app.add_plugins(YoleckPluginForGame); app.add_systems( Startup, move |asset_server: Res, mut commands: Commands| { commands.spawn(YoleckLoadLevel( asset_server.load(Path::new("levels3d").join(&level)), )); }, ); app.add_plugins(Vpeol3dPluginForGame); } else { app.add_plugins(EguiPlugin); app.add_plugins(YoleckPluginForEditor); // Adding `YoleckEditorLevelsDirectoryPath` is not usually required - // `YoleckPluginForEditor` will add one with "assets/levels". Here we want to support // example2d and example3d in the same repository so we use different directories. app.insert_resource(bevy_yoleck::YoleckEditorLevelsDirectoryPath( Path::new(".").join("assets").join("levels3d"), )); app.add_plugins(Vpeol3dPluginForEditor::topdown()); app.add_plugins(VpeolSelectionCuePlugin::default()); #[cfg(target_arch = "wasm32")] app.add_systems( Startup, |asset_server: Res, mut commands: Commands| { commands.spawn(YoleckLoadLevel(asset_server.load("levels3d/example.yol"))); }, ); } app.add_systems(Startup, setup_camera); app.add_systems(Startup, setup_arena); app.add_yoleck_entity_type({ YoleckEntityType::new("Spaceship") .with::() .insert_on_init(|| IsSpaceship) .insert_on_init_during_editor(|| Vpeol3dThirdAxisWithKnob { knob_distance: 2.0, knob_scale: 0.5, }) }); app.add_systems(YoleckSchedule::Populate, populate_spaceship); app.add_yoleck_entity_type({ YoleckEntityType::new("Planet") .with_uuid() .with::() .insert_on_init(|| IsPlanet) .insert_on_init_during_editor(|| VpeolDragPlane::XY) .insert_on_init_during_editor(|| Vpeol3dThirdAxisWithKnob { knob_distance: 2.0, knob_scale: 0.5, }) }); app.add_systems(YoleckSchedule::Populate, populate_planet); app.add_yoleck_entity_type({ YoleckEntityType::new("PlanetPointer") .with::() .with::() .insert_on_init(|| SimpleSphere) .insert_on_init_during_editor(|| Vpeol3dThirdAxisWithKnob { knob_distance: 2.0, knob_scale: 0.5, }) }); app.add_systems(YoleckSchedule::Populate, populate_simple_sphere); app.add_yoleck_edit_system(edit_laser_pointer); app.add_systems(Update, draw_laser_pointers); app.add_systems( Update, (control_spaceship, hit_planets).run_if(in_state(YoleckEditorState::GameActive)), ); app.run(); } fn setup_camera(mut commands: Commands) { commands .spawn(Camera3dBundle { transform: Transform::from_xyz(0.0, 16.0, 40.0) .looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y), ..Default::default() }) .insert(VpeolCameraState::default()) .insert(Vpeol3dCameraControl::topdown()); commands.spawn(DirectionalLightBundle { directional_light: DirectionalLight { color: Color::WHITE, illuminance: 50_000.0, shadows_enabled: true, ..Default::default() }, transform: Transform::from_xyz(0.0, 100.0, 0.0).looking_to(-Vec3::Y, Vec3::Z), ..Default::default() }); } fn setup_arena( mut commands: Commands, mut mesh_assets: ResMut>, mut material_assets: ResMut>, ) { let mesh = mesh_assets.add(Mesh::from( Plane3d { normal: Dir3::Y, half_size: Vec2::new(100.0, 100.0), } .mesh(), )); let material = material_assets.add(Color::from(css::GRAY)); commands.spawn(PbrBundle { mesh, material, transform: Transform::from_xyz(0.0, -10.0, 0.0), ..Default::default() }); } #[derive(Component)] struct IsSpaceship; fn populate_spaceship( mut populate: YoleckPopulate<(), With>, asset_server: Res, ) { populate.populate(|ctx, mut cmd, ()| { cmd.insert(VpeolWillContainClickableChildren); // Spaceship model doesn't change, so there is no need to despawn and recreated it. if ctx.is_first_time() { cmd.insert(SceneBundle { scene: asset_server.load("models/spaceship.glb#Scene0"), ..Default::default() }); } }); } #[derive(Component)] struct IsPlanet; fn populate_planet( mut populate: YoleckPopulate<(), With>, asset_server: Res, ) { populate.populate(|ctx, mut cmd, ()| { cmd.insert(VpeolWillContainClickableChildren); // Planet model doesn't change, so there is no need to despawn and recreated it. if ctx.is_first_time() { cmd.insert(SceneBundle { scene: asset_server.load("models/planet.glb#Scene0"), ..Default::default() }); } }); } fn control_spaceship( mut spaceship_query: Query<&mut Transform, With>, time: Res