sjqmmo_lib/
lib.rs

1use std::time::Duration;
2
3use bevy::{
4    audio::PlaybackMode,
5    core_pipeline::{bloom::BloomSettings, tonemapping::Tonemapping},
6    input::keyboard,
7    pbr::{NotShadowCaster, NotShadowReceiver},
8    prelude::*,
9    render::render_resource::{Extent3d, TextureDimension, TextureFormat},
10    window::{CursorGrabMode, WindowMode},
11};
12use bevy_third_person_camera::{
13    ThirdPersonCamera, ThirdPersonCameraPlugin, ThirdPersonCameraTarget, Zoom,
14};
15const USER_GROUP: Group = Group::GROUP_4;
16const PLANE_GROUP: Group = Group::GROUP_5;
17const BOX_GROUP: Group = Group::GROUP_2;
18const EXPLODER_GROUP: Group = Group::GROUP_3;
19
20use bevy_rapier3d::prelude::*;
21use rand::Rng;
22#[derive(Component, Reflect)]
23struct Cube {
24    max_speed: f32,
25    max_acceleration: f32,
26}
27
28#[derive(Component, Reflect)]
29struct JumpControl {
30    jump: bool,
31    remaining_jumps: u8,
32    jump_speed: f32,
33}
34
35#[derive(States, Debug, Default, Hash, PartialEq, Eq, Clone, Copy)]
36enum GameState {
37    #[default]
38    Init,
39    Splash,
40}
41
42#[bevy_main]
43pub fn main() {
44    App::new()
45        .add_plugins(
46            DefaultPlugins
47                .set(ImagePlugin::default_nearest())
48                .set(WindowPlugin {
49                    primary_window: Some(Window {
50                        canvas: Some("#game".to_string()),
51                        fit_canvas_to_parent: true,
52                        mode: WindowMode::BorderlessFullscreen,
53                        ..Default::default()
54                    }),
55                    ..Default::default()
56                }),
57        )
58        .add_plugins(RapierPhysicsPlugin::<NoUserData>::default())
59        .add_state::<GameState>()
60        // .add_plugins(FrameTimeDiagnosticsPlugin)
61        // .add_plugins(LogDiagnosticsPlugin::default())
62        // .add_plugins(RapierDebugRenderPlugin::default())
63        // .add_plugins(WorldInspectorPlugin::default())
64        .register_type::<Cube>()
65        .register_type::<JumpControl>()
66        .add_plugins(ThirdPersonCameraPlugin)
67        .add_systems(Startup, startup)
68        .add_systems(
69            Update,
70            (
71                // update,
72                cube_move_keyboard,
73                cude_move_gamepad,
74                // cube_limit,
75                reset_jump,
76                cube_jump,
77                move_elevator,
78            ),
79        )
80        .add_systems(Update, touch_sensor.run_if(in_state(GameState::Init)))
81        .add_systems(
82            Update,
83            (
84                launch,
85                destroy,
86                explode,
87                assend,
88                launch_superball,
89                add_collider,
90            )
91                .run_if(in_state(GameState::Splash)),
92        )
93        .add_systems(OnEnter(GameState::Splash), splash_init)
94        .run();
95}
96
97#[derive(Component)]
98struct DestroyCountdown(f32);
99
100fn destroy(
101    mut commands: Commands,
102    mut items: Query<(Entity, &mut DestroyCountdown)>,
103    time: Res<Time>,
104) {
105    let delta = time.delta_seconds();
106    for (e, mut d) in items.iter_mut() {
107        d.0 -= delta;
108        if d.0 < 0.0 {
109            commands.entity(e).despawn();
110        }
111    }
112}
113
114#[derive(Component)]
115struct ExplodCountDown(f32);
116
117#[derive(Component)]
118struct AddColliderCountDown(Timer);
119
120#[derive(Resource)]
121struct FireWorkMeshes {
122    splash_meshes: Vec<Handle<Mesh>>,
123    meshes: Vec<Handle<Mesh>>,
124    ball_matierials: Vec<Handle<StandardMaterial>>,
125    splash_mat: Vec<Handle<StandardMaterial>>,
126}
127
128#[derive(Component, Debug, Clone)]
129enum FireWorkType {
130    White,
131    Random,
132}
133
134#[derive(Component, Debug, Clone)]
135struct FireWorkProps {
136    range: f32,
137    num: usize,
138    count_down: f32,
139    scale: f32,
140    add_collider: Option<f32>,
141    remaining_splash: Option<Box<FireWorkProps>>,
142    fire_work_type: FireWorkType,
143}
144
145#[derive(Component)]
146struct Assending {
147    speed: f32,
148}
149
150#[derive(Resource)]
151struct SplashTimer(Timer);
152
153fn add_collider(
154    mut commands: Commands,
155    mut items: Query<(Entity, &mut AddColliderCountDown)>,
156    time: Res<Time>,
157) {
158    let delta = time.delta();
159    for (e, mut d) in items.iter_mut() {
160        d.0.tick(delta);
161        if d.0.just_finished() {
162            commands.entity(e).insert((
163                Collider::cuboid(1., 1., 1.),
164                CollisionGroups::new(EXPLODER_GROUP, PLANE_GROUP),
165            ));
166        }
167    }
168}
169
170fn assend(time: Res<Time>, mut items: Query<(&mut Transform, &Assending)>) {
171    let delta = time.delta_seconds();
172    for (mut t, a) in items.iter_mut() {
173        t.translation.y += a.speed * delta;
174    }
175}
176
177fn explode(
178    mut commands: Commands,
179    time: Res<Time>,
180    mut items: Query<(&mut ExplodCountDown, Entity, &FireWorkProps, &mut Transform)>,
181    fire_work_meshs: Res<FireWorkMeshes>,
182) {
183    let delta = time.delta_seconds();
184    for (mut cd, et, fs, ts) in items.iter_mut() {
185        let FireWorkProps {
186            range,
187            num,
188            count_down,
189            scale,
190            add_collider,
191            remaining_splash,
192            fire_work_type,
193        } = fs;
194        cd.0 -= delta;
195        if cd.0 < 0. {
196            commands.entity(et).despawn();
197            let mut rng = rand::thread_rng();
198            // spawn 100 splash
199            for _i in 0..*num {
200                let init_speed = Vec3::new(
201                    rng.gen_range(-1. ..1.),
202                    rng.gen_range(-1. ..1.),
203                    rng.gen_range(-1. ..1.),
204                );
205                let mut cmd = commands.spawn((
206                    {
207                        let material = match fire_work_type {
208                            FireWorkType::White => fire_work_meshs.splash_mat[0].clone(),
209                            FireWorkType::Random => {
210                                fire_work_meshs.splash_mat[rng.gen_range(0..100)].clone()
211                            }
212                        };
213                        PbrBundle {
214                            mesh: fire_work_meshs.splash_meshes[rng.gen_range(0..100)].clone(),
215                            material,
216                            transform: Transform::from_translation(ts.translation)
217                                .with_scale(Vec3::splat(*scale)),
218                            ..Default::default()
219                        }
220                    },
221                    Velocity {
222                        linvel: init_speed * *range,
223                        ..Default::default()
224                    },
225                    NotShadowCaster,
226                    NotShadowReceiver,
227                    RigidBody::Dynamic,
228                ));
229                if let Some(time) = add_collider {
230                    cmd.insert(AddColliderCountDown(Timer::new(
231                        Duration::from_secs_f32(*time),
232                        TimerMode::Once,
233                    )));
234                }
235                if let Some(remaining_splash) = remaining_splash {
236                    // it will explode again!
237                    let remaining_splash = (**remaining_splash).clone();
238                    cmd.insert((remaining_splash, ExplodCountDown(*count_down)));
239                } else {
240                    // just destroy it!
241                    cmd.insert(DestroyCountdown(*count_down));
242                }
243            }
244            // let range = fs.1;
245        }
246    }
247}
248
249fn launch_superball(
250    mut commands: Commands,
251    time: Res<Time>,
252    mut timer: ResMut<SuperBigBallTimer>,
253    superball: Query<Entity, With<SuperBall>>,
254) {
255    timer.timer.tick(time.delta());
256    if timer.timer.just_finished() {
257        println!("launch super ball");
258        let super_ball = superball.single();
259        let mut super_ball = commands.entity(super_ball);
260        super_ball.insert((
261            ExplodCountDown(25.),
262            FireWorkProps {
263                range: 1500.,
264                num: 500,
265                count_down: 10.,
266                scale: 10.,
267                add_collider: Some(1.),
268                remaining_splash: Some(Box::new(FireWorkProps {
269                    range: 500.,
270                    num: 10,
271                    count_down: 22.,
272                    scale: 6.,
273                    add_collider: None,
274                    remaining_splash: None,
275                    fire_work_type: FireWorkType::White,
276                })),
277                fire_work_type: FireWorkType::Random,
278            }, // FireWorkSpeedRange(150., 5000., 1000, 20.),
279            Assending { speed: 150. },
280        ));
281    }
282}
283
284fn launch(
285    mut commands: Commands,
286    time: Res<Time>,
287    mut timer: ResMut<SplashTimer>,
288    fire_work_meshs: Res<FireWorkMeshes>,
289    superball_timer: Res<SuperBigBallTimer>,
290) {
291    timer.0.tick(time.delta());
292    if timer.0.just_finished() && !superball_timer.timer.finished() {
293        let mut gen = rand::thread_rng();
294        let mesh_id: usize = gen.gen_range(0..100);
295        let mat_id: usize = gen.gen_range(0..100);
296        let speed: f32 = gen.gen_range(300. ..1000.);
297        let range: f32 = gen.gen_range(300. ..2000.);
298        let cool_down: f32 = gen.gen_range(2. ..5.);
299        commands.spawn((
300            NotShadowCaster,
301            NotShadowReceiver,
302            PbrBundle {
303                mesh: fire_work_meshs.meshes[mesh_id].clone(),
304                material: fire_work_meshs.ball_matierials[mat_id].clone(),
305                transform: Transform::from_translation(Vec3::new(
306                    gen.gen_range(-200. ..200.),
307                    0.,
308                    gen.gen_range(-3500. ..-1000.),
309                )),
310                ..Default::default()
311            },
312            ExplodCountDown(cool_down),
313            // FireWorkSpeedRange(speed, range, 100, 5.),
314            FireWorkProps {
315                range,
316                num: 30,
317                count_down: 2.5,
318                scale: 1.,
319                add_collider: Some(0.3),
320                remaining_splash: Some(Box::new(FireWorkProps {
321                    range: 100.,
322                    num: 5,
323                    count_down: 1.,
324                    scale: 1.,
325                    add_collider: None,
326                    remaining_splash: None,
327                    fire_work_type: FireWorkType::White,
328                })),
329                fire_work_type: FireWorkType::Random,
330            },
331            Assending { speed },
332        ));
333    }
334}
335
336#[derive(Resource)]
337struct SuperBigBallTimer {
338    timer: Timer,
339}
340
341fn splash_init(
342    mut commands: Commands,
343    mut meshs: ResMut<Assets<Mesh>>,
344    mut materials: ResMut<Assets<StandardMaterial>>,
345    asset_server: Res<AssetServer>,
346) {
347    commands.insert_resource(SplashTimer(Timer::new(
348        Duration::from_secs(1),
349        TimerMode::Repeating,
350    )));
351    commands.insert_resource(SuperBigBallTimer {
352        timer: Timer::new(Duration::from_secs(60), TimerMode::Once),
353    });
354    commands.spawn(AudioBundle {
355        source: asset_server.load("sounds/back.ogg"),
356        settings: PlaybackSettings {
357            mode: PlaybackMode::Once,
358            ..Default::default()
359        },
360    });
361    let mut mesh_res = FireWorkMeshes {
362        splash_meshes: vec![],
363        meshes: vec![],
364        ball_matierials: vec![],
365        splash_mat: vec![],
366    };
367    // put the default value
368    mesh_res.splash_meshes.push(
369        meshs.add(
370            shape::UVSphere {
371                radius: rand::thread_rng().gen_range(2.5..5.0),
372                sectors: 16,
373                stacks: 16,
374            }
375            .into(),
376        ),
377    );
378    mesh_res.meshes.push(
379        meshs.add(
380            shape::UVSphere {
381                radius: rand::thread_rng().gen_range(2.5..25.0),
382                sectors: 16,
383                stacks: 16,
384            }
385            .into(),
386        ),
387    );
388    mesh_res
389        .ball_matierials
390        .push(materials.add(StandardMaterial {
391            emissive: Color::rgb_linear(5., 5., 5.),
392            ..Default::default()
393        }));
394    mesh_res.splash_mat.push(materials.add(StandardMaterial {
395        emissive: Color::rgb_linear(10., 10., 10.),
396        ..Default::default()
397    }));
398    let mut rng = rand::thread_rng();
399    for _i in 1..100 {
400        mesh_res.meshes.push(
401            meshs.add(
402                shape::UVSphere {
403                    radius: rand::thread_rng().gen_range(2.5..25.0),
404                    sectors: 16,
405                    stacks: 16,
406                }
407                .into(),
408            ),
409        );
410        mesh_res
411            .ball_matierials
412            .push(materials.add(StandardMaterial {
413                emissive: Color::rgb_linear(
414                    rng.gen_range(0.1..5.),
415                    rng.gen_range(0.1..5.),
416                    rng.gen_range(0.1..5.),
417                ),
418                ..Default::default()
419            }));
420        mesh_res.splash_mat.push(materials.add(StandardMaterial {
421            emissive: Color::rgb_linear(
422                rng.gen_range(0.1..15.),
423                rng.gen_range(0.1..15.),
424                rng.gen_range(0.1..15.),
425            ),
426            ..Default::default()
427        }));
428        let rect: bool = rng.gen();
429        if rect {
430            mesh_res
431                .splash_meshes
432                .push(meshs.add(shape::Cube::new(rng.gen_range(2.5..5.0)).into()));
433        } else {
434            mesh_res.splash_meshes.push(
435                meshs.add(
436                    shape::UVSphere {
437                        radius: rand::thread_rng().gen_range(2.5..5.0),
438                        sectors: 16,
439                        stacks: 16,
440                    }
441                    .into(),
442                ),
443            );
444        }
445    }
446
447    commands.insert_resource(mesh_res);
448}
449fn startup(
450    mut commands: Commands,
451    mut meshs: ResMut<Assets<Mesh>>,
452    mut materials: ResMut<Assets<StandardMaterial>>,
453    mut windows: Query<&mut Window>,
454    mut images: ResMut<Assets<Image>>,
455    assert_server: Res<AssetServer>,
456) {
457    let mut window = windows.single_mut();
458    window.cursor.visible = false;
459    window.cursor.grab_mode = CursorGrabMode::Locked;
460    spawn_world(
461        &mut commands,
462        &mut meshs,
463        &mut materials,
464        &mut images,
465        &assert_server,
466    );
467
468    // spawn a cube
469    commands.spawn((
470        PbrBundle {
471            mesh: meshs.add(Mesh::from(shape::Cube { size: 1.0 })),
472            material: materials.add(StandardMaterial {
473                emissive: Color::rgb_linear(13.99, 5.32, 2.),
474                ..Default::default()
475            }),
476            transform: Transform::from_xyz(0.0, 5.0, -2.),
477            ..Default::default()
478        },
479        Collider::cuboid(0.5, 0.5, 0.5),
480        RigidBody::Dynamic,
481        ExternalForce::default(),
482        Velocity::default(),
483        Cube {
484            max_speed: 10.,
485            max_acceleration: 10.,
486        },
487        JumpControl {
488            jump: true,
489            remaining_jumps: 2,
490            jump_speed: 10.,
491        },
492        ThirdPersonCameraTarget,
493        Name::new("Player"),
494        CollisionGroups::new(USER_GROUP, PLANE_GROUP | BOX_GROUP),
495    ));
496}
497#[derive(Resource, Deref, Clone, Copy)]
498struct ElevatorStatus {
499    /// from 0 - 100
500    current_location: f32,
501}
502
503#[derive(Component, Deref, Clone, Copy)]
504struct ElevatorId(u8);
505
506fn get_elevator_location(
507    id: ElevatorId,
508    current_status: ElevatorStatus,
509    total_elevators: u8,
510) -> Vec3 {
511    let ratio = id.0 as f32 / total_elevators as f32;
512    let mut status_id = current_status.current_location + ratio * 100.;
513    if status_id > 100. {
514        status_id -= 100.;
515    }
516    if status_id <= 49. {
517        Vec3::new(0., status_id / 49. * 49. * 5., 0.)
518    } else if status_id <= 50. {
519        Vec3::new((status_id - 49.) * 5., 49. * 5., 0.)
520    } else if status_id <= 99. {
521        Vec3::new(5., 49. * 5. - (status_id - 50.) * 5., 0.)
522    } else {
523        Vec3::new(5. - (status_id - 99.) * 5., 0., 0.)
524    }
525}
526static ELEVATOR_LOCATION: Vec3 = Vec3::new(10., -1., 0.);
527const TOTAL_ELEVATORS: u8 = 20;
528#[derive(Resource)]
529struct ElevatorSpeed(f32);
530fn move_elevator(
531    mut ele: Query<(&mut Transform, &ElevatorId)>,
532    mut current_status: ResMut<ElevatorStatus>,
533    time: Res<Time>,
534    elevator_speed: Res<ElevatorSpeed>,
535) {
536    for (mut trans, e) in ele.iter_mut() {
537        let new_position = get_elevator_location(*e, *current_status, TOTAL_ELEVATORS);
538        let new_position = new_position + ELEVATOR_LOCATION;
539        trans.translation = new_position;
540    }
541    current_status.current_location += time.delta_seconds() * elevator_speed.0;
542    if current_status.current_location > 100. {
543        current_status.current_location -= 100.;
544    }
545}
546
547fn spawn_world(
548    commands: &mut Commands,
549    meshs: &mut ResMut<Assets<Mesh>>,
550    materials: &mut ResMut<Assets<StandardMaterial>>,
551    images: &mut ResMut<Assets<Image>>,
552    asset_server: &Res<AssetServer>,
553) {
554    let debug_material = materials.add(StandardMaterial {
555        base_color_texture: Some(images.add(uv_debug_texture())),
556        ..Default::default()
557    });
558    let pink_emit = materials.add(StandardMaterial {
559        emissive: Color::rgb_linear(10., 0.8, 5.8),
560        ..Default::default()
561    });
562    commands.spawn(PointLightBundle {
563        point_light: PointLight {
564            color: Color::WHITE,
565            intensity: 100000.,
566            range: 200.,
567            shadows_enabled: true,
568            ..Default::default()
569        },
570        transform: Transform::from_translation(Vec3::new(0., 20., -10.)),
571        ..Default::default()
572    });
573    spawn_basic(commands, meshs, debug_material.clone());
574
575    let light_material = materials.add(StandardMaterial {
576        emissive: Color::rgb_linear(2.99, 15.32, 2.),
577        ..Default::default()
578    });
579    let light_cube = meshs.add(Mesh::from(shape::Cube { size: 10.0 }));
580
581    spawn_light(commands, &light_cube, &light_material, meshs, pink_emit);
582
583    spawn_elevator(commands, meshs, materials, asset_server);
584    // spawn_elevator(commands, meshs, materials, );
585}
586
587fn spawn_elevator(
588    commands: &mut Commands<'_, '_>,
589    meshs: &mut ResMut<'_, Assets<Mesh>>,
590    materials: &mut ResMut<'_, Assets<StandardMaterial>>,
591    assert_server: &Res<AssetServer>,
592) {
593    // load the image
594    let image = assert_server.load("images/bibi.png");
595    let front_big_wall_matierial = materials.add(StandardMaterial {
596        base_color_texture: Some(image.clone()),
597        ..Default::default()
598    });
599    // the bibi_wall
600    commands.spawn((
601        PbrBundle {
602            material: front_big_wall_matierial.clone(),
603            mesh: meshs.add(shape::Box::new(100., 40. * 5., 1.).into()),
604            transform: Transform::from_translation(
605                Vec3::new(0., 40. * 5. / 2., -100.) + ELEVATOR_LOCATION,
606            )
607            .looking_to(Vec3::Z, Vec3::Y),
608            ..Default::default()
609        },
610        Collider::cuboid(100. / 2.0, 40.0 * 5.0 / 2.0, 0.5),
611        CollisionGroups::new(BOX_GROUP, Group::ALL),
612    ));
613    commands.spawn(PointLightBundle {
614        point_light: PointLight {
615            color: Color::WHITE,
616            intensity: 100000.,
617            range: 200.,
618            ..Default::default()
619        },
620        transform: Transform::from_translation(
621            Vec3::new(0., 40. * 5. / 2., -48.) + ELEVATOR_LOCATION,
622        ),
623        ..Default::default()
624    });
625    // spawn the elevator
626    commands.insert_resource(ElevatorStatus {
627        current_location: 0.0,
628    });
629    commands.insert_resource(ElevatorSpeed(1.));
630
631    let elevator_mesh = meshs.add(shape::Box::new(3., 1., 20.).into());
632    let elevator_matirerial = materials.add(StandardMaterial {
633        emissive: Color::rgb_linear(0.2, 5., 15.),
634        ..Default::default()
635    });
636    // spawn a wall
637    let wall_matireial = materials.add(Color::rgba(0.7, 0.6, 0.1, 0.1).into());
638    let side_wall_mesh = meshs.add(shape::Box::new(1., 49. * 5., 20.).into());
639    let front_wall_mesh = meshs.add(shape::Box::new(3. + 5. + 3., 49. * 5., 1.).into());
640    // left
641    commands.spawn((
642        PbrBundle {
643            material: wall_matireial.clone(),
644            mesh: side_wall_mesh.clone(),
645            transform: Transform::from_translation(
646                Vec3::new(-1.5 - 0.5, 49. * 5. / 2. + 2., 0.) + ELEVATOR_LOCATION,
647            ),
648            ..Default::default()
649        },
650        Collider::cuboid(0.5, 49.0 * 5.0 / 2.0, 20.0 / 2.0),
651        CollisionGroups::new(BOX_GROUP, Group::ALL),
652    ));
653    // right
654    commands.spawn((
655        PbrBundle {
656            material: wall_matireial.clone(),
657            mesh: side_wall_mesh.clone(),
658            transform: Transform::from_translation(
659                Vec3::new(5. + 1.5 + 0.5, 49. * 5. / 2. + 2., 0.) + ELEVATOR_LOCATION,
660            ),
661            ..Default::default()
662        },
663        Collider::cuboid(0.5, 49.0 * 5.0 / 2.0, 20.0 / 2.0),
664        CollisionGroups::new(BOX_GROUP, Group::ALL),
665    ));
666    // middle
667    commands.spawn((
668        PbrBundle {
669            material: wall_matireial.clone(),
670            mesh: side_wall_mesh.clone(),
671            transform: Transform::from_translation(
672                Vec3::new(2.5, 49. * 5. / 2., 0.) + ELEVATOR_LOCATION,
673            ),
674            ..Default::default()
675        },
676        Collider::cuboid(0.5, 49.0 * 5.0 / 2.0, 20.0 / 2.0),
677        CollisionGroups::new(BOX_GROUP, Group::ALL),
678    ));
679    // back
680    commands.spawn((
681        PbrBundle {
682            material: wall_matireial.clone(),
683            mesh: front_wall_mesh.clone(),
684            transform: Transform::from_translation(
685                Vec3::new(2.5, 49. * 5. / 2., 10. + 0.5) + ELEVATOR_LOCATION,
686            ),
687            ..Default::default()
688        },
689        Collider::cuboid((3.0 + 5.0 + 3.0) / 2.0, 49.0 * 5.0 / 2.0, 0.5),
690        CollisionGroups::new(BOX_GROUP, Group::ALL),
691    ));
692    // front
693    commands.spawn((
694        PbrBundle {
695            material: wall_matireial.clone(),
696            mesh: front_wall_mesh.clone(),
697            transform: Transform::from_translation(
698                Vec3::new(2.5, 49. * 5. / 2., -10. - 0.5) + ELEVATOR_LOCATION,
699            ),
700            ..Default::default()
701        },
702        Collider::cuboid((3.0 + 5.0 + 3.0) / 2.0, 49.0 * 5.0 / 2.0, 0.5),
703        CollisionGroups::new(BOX_GROUP, Group::ALL),
704    ));
705
706    // spawn the top plane
707    commands.spawn((
708        PbrBundle {
709            material: materials.add(Color::WHITE.with_a(0.3).into()),
710            mesh: meshs.add(shape::Plane::from_size(200.).into()),
711            transform: Transform::from_translation(
712                Vec3::new(0.0, 49.0 * 5.0, 0.) + ELEVATOR_LOCATION,
713            ),
714            ..Default::default()
715        },
716        Collider::cuboid(100., 0.01, 100.),
717        CollisionGroups::new(BOX_GROUP, Group::ALL),
718    ));
719    commands.spawn(PointLightBundle {
720        point_light: PointLight {
721            color: Color::WHITE,
722            intensity: 100000.,
723            range: 200.,
724            shadows_enabled: true,
725            ..Default::default()
726        },
727        transform: Transform::from_translation(
728            Vec3::new(0., 49. * 5. + 10., 0.) + ELEVATOR_LOCATION,
729        ),
730        ..Default::default()
731    });
732    // spawn the gate
733    commands.spawn((
734        PbrBundle {
735            material: materials.add(Color::PINK.with_a(0.2).into()),
736            mesh: meshs.add(shape::Box::new(200., 0.5, 1.).into()),
737            transform: Transform::from_translation(
738                Vec3::new(0.0, 49.0 * 5.0 + 0.25, -100.) + ELEVATOR_LOCATION,
739            ),
740            ..Default::default()
741        },
742        Collider::cuboid(100., 0.25, 0.5),
743        CollisionGroups::new(BOX_GROUP, Group::ALL),
744    ));
745
746    for i in 0..TOTAL_ELEVATORS {
747        let location = get_elevator_location(
748            ElevatorId(i),
749            ElevatorStatus {
750                current_location: 0.,
751            },
752            TOTAL_ELEVATORS,
753        );
754        commands.spawn((
755            PbrBundle {
756                mesh: elevator_mesh.clone(),
757                material: elevator_matirerial.clone(),
758                transform: Transform::from_translation(location + ELEVATOR_LOCATION),
759                ..Default::default()
760            },
761            RigidBody::KinematicPositionBased,
762            Collider::cuboid(3. / 2., 1. / 2., 20. / 2.),
763            ElevatorId(i),
764            CollisionGroups::new(BOX_GROUP, Group::ALL),
765        ));
766    }
767
768    // spawn a sensor
769    commands.spawn((
770        Sensor,
771        Collider::cylinder(0.1, 1.),
772        PbrBundle {
773            mesh: meshs.add(
774                shape::Cylinder {
775                    radius: 1.,
776                    height: 0.2,
777                    ..Default::default()
778                }
779                .into(),
780            ),
781            material: materials.add(Color::RED.into()),
782            transform: Transform::from_translation(
783                Vec3::new(-15., 49. * 5. + 0.05, -15.) + ELEVATOR_LOCATION,
784            ),
785            ..Default::default()
786        },
787        CollisionGroups::new(BOX_GROUP, Group::ALL),
788    ));
789}
790
791fn touch_sensor(
792    mut commands: Commands,
793    sensor: Query<Entity, With<Sensor>>,
794    cube: Query<Entity, With<Cube>>,
795    rapier_context: Res<RapierContext>,
796
797    mut next_state: ResMut<NextState<GameState>>,
798) {
799    let s = sensor.single();
800    let cube = cube.single();
801    if rapier_context.intersection_pair(cube, s) == Some(true) {
802        println!("touch sensor");
803        commands.entity(s).despawn();
804        next_state.set(GameState::Splash);
805    }
806}
807#[derive(Component)]
808struct SuperBall;
809fn spawn_light(
810    commands: &mut Commands<'_, '_>,
811    light_cube: &Handle<Mesh>,
812    light_material: &Handle<StandardMaterial>,
813    meshs: &mut ResMut<'_, Assets<Mesh>>,
814    pink_emit: Handle<StandardMaterial>,
815) {
816    // spawn 100 light cube and a light ball
817    for i in 0..100 {
818        commands.spawn(PbrBundle {
819            mesh: light_cube.clone(),
820            material: light_material.clone(),
821            transform: Transform::from_translation(Vec3::new(i as f32 * 10. - 500., 20., -4490.)),
822            ..Default::default()
823        });
824    }
825    commands.spawn((PbrBundle {
826        mesh: meshs.add(
827            shape::Icosphere {
828                radius: 2.,
829                subdivisions: 4,
830            }
831            .try_into()
832            .unwrap(),
833        ),
834        material: light_material.clone(),
835        transform: Transform::from_translation(Vec3::new(0., 10., -4495.)),
836        ..Default::default()
837    },));
838    commands.spawn(PointLightBundle {
839        point_light: PointLight {
840            color: Color::PINK,
841            intensity: 100000.,
842            range: 200.,
843            ..Default::default()
844        },
845        transform: Transform::from_translation(Vec3::new(0., 10., -4495.)),
846        ..Default::default()
847    });
848
849    commands.spawn((
850        PbrBundle {
851            mesh: meshs.add(
852                shape::Icosphere {
853                    radius: 200.,
854                    subdivisions: 4,
855                }
856                .try_into()
857                .unwrap(),
858            ),
859            material: pink_emit.clone(),
860            transform: Transform::from_translation(Vec3::new(0., 300., -4295.)),
861            ..Default::default()
862        },
863        SuperBall,
864    ));
865    commands.spawn(PointLightBundle {
866        point_light: PointLight {
867            color: Color::PINK,
868            intensity: 100000.,
869            range: 500.,
870            ..Default::default()
871        },
872        transform: Transform::from_translation(Vec3::new(0., 300., -4295.)),
873        ..Default::default()
874    });
875    // // spawn 100 light cubes
876    // for i in 0..10 {
877    //     for j in 0..10 {
878    //         commands.spawn(PbrBundle {
879    //             mesh: light_cube.clone(),
880    //             material: light_material.clone(),
881    //             transform: Transform::from_translation(Vec3::new(
882    //                 i as f32 * 100. - 500.,
883    //                 j as f32 * 100.,
884    //                 -1000.,
885    //             )),
886    //             ..Default::default()
887    //         });
888    //     }
889    // }
890}
891
892fn spawn_basic(
893    commands: &mut Commands<'_, '_>,
894    meshs: &mut ResMut<'_, Assets<Mesh>>,
895    debug_material: Handle<StandardMaterial>,
896) {
897    // spawn a plane
898    commands.spawn((
899        PbrBundle {
900            mesh: meshs.add(Mesh::from(shape::Plane {
901                size: 10000.0,
902                subdivisions: 0,
903            })),
904            material: debug_material.clone(),
905            ..Default::default()
906        },
907        Collider::halfspace(Vec3::Y).unwrap(),
908        CollisionGroups::new(PLANE_GROUP, Group::ALL),
909    ));
910
911    // spawn a camera
912    commands.spawn((
913        Camera3dBundle {
914            transform: Transform::from_xyz(0.0, 5.0, 10.0).looking_to(-Vec3::Z, Vec3::Y),
915            camera: Camera {
916                hdr: true,
917                ..Default::default()
918            },
919            tonemapping: Tonemapping::TonyMcMapface,
920            ..Default::default()
921        },
922        BloomSettings::default(),
923        ThirdPersonCamera {
924            cursor_lock_active: true,
925            cursor_lock_toggle_enabled: true,
926            cursor_lock_key: keyboard::KeyCode::L,
927            zoom_enabled: true,
928            zoom: Zoom::new(8., 10.),
929            ..Default::default()
930        },
931    ));
932
933    // spawn a light
934    commands.insert_resource(AmbientLight {
935        color: Color::WHITE,
936        brightness: 0.4,
937    });
938
939    // spawn some fixed cubes
940    let positions = [
941        Vec3::new(-10., 2., -30.),
942        Vec3::new(10., 3., -20.),
943        Vec3::new(0., 4., -10.),
944        Vec3::new(0., 4., -5000.),
945    ];
946    let sizes = [1., 2., 3., 1000.];
947    for (mut p, s) in positions.into_iter().zip(sizes) {
948        p.y = s / 2.;
949        commands.spawn((
950            PbrBundle {
951                mesh: meshs.add(Mesh::from(shape::Cube { size: s })),
952                material: debug_material.clone(),
953                transform: Transform::from_translation(p),
954                ..Default::default()
955            },
956            Collider::cuboid(s / 2., s / 2., s / 2.),
957            CollisionGroups::new(BOX_GROUP, Group::ALL),
958        ));
959    }
960}
961
962fn cude_move_gamepad(
963    gamepads: Res<Gamepads>,
964    button_inputs: Res<Input<GamepadButton>>,
965    axes: Res<Axis<GamepadAxis>>,
966    mut cube: Query<
967        (&mut Velocity, &mut Transform, &mut JumpControl, &Cube),
968        Without<ThirdPersonCamera>,
969    >,
970    camera: Query<&Transform, With<ThirdPersonCamera>>,
971) {
972    let (mut velocity, mut transform, mut jump_contorl, cube) = cube.single_mut();
973    let camera = camera.single();
974
975    for game_pad in gamepads.iter() {
976        if button_inputs.just_pressed(GamepadButton::new(game_pad, GamepadButtonType::North)) {
977            transform.translation = Vec3::new(0.0, 5.0, 0.0);
978        }
979        if button_inputs.just_pressed(GamepadButton::new(game_pad, GamepadButtonType::West)) {
980            velocity.linvel.x = 0.;
981            velocity.linvel.z = 0.;
982        }
983        if button_inputs.just_pressed(GamepadButton::new(game_pad, GamepadButtonType::South))
984            && jump_contorl.remaining_jumps > 0
985        {
986            // jump
987            velocity.linvel.y = jump_contorl.jump_speed;
988            jump_contorl.remaining_jumps -= 1;
989        }
990        let left_stick_x = axes.get(GamepadAxis::new(game_pad, GamepadAxisType::LeftStickX));
991        let left_stick_y = axes.get(GamepadAxis::new(game_pad, GamepadAxisType::LeftStickY));
992        match (left_stick_x, left_stick_y) {
993            (Some(x), Some(y)) => {
994                if x.abs() > 0.1 || y.abs() > 0.1 {
995                    let max_speed = cube.max_speed;
996                    let mut forward_direction = camera.forward();
997                    forward_direction.y = 0.;
998                    forward_direction = forward_direction.normalize();
999                    let mut right_direction = camera.right();
1000                    right_direction.y = 0.;
1001                    right_direction = right_direction.normalize();
1002
1003                    let speed = y * forward_direction + x * right_direction;
1004                    let speed = speed * max_speed;
1005                    let old_y = velocity.linvel.y;
1006                    velocity.linvel = speed;
1007                    velocity.linvel.y = old_y;
1008                }
1009            }
1010            _ => {}
1011        };
1012    }
1013}
1014
1015fn cube_move_keyboard(
1016    keyboard_input: Res<Input<keyboard::KeyCode>>,
1017    mut cube: Query<(&mut Transform, &mut Velocity, &Cube), Without<ThirdPersonCamera>>,
1018    camera: Query<&Transform, With<ThirdPersonCamera>>,
1019) {
1020    let (mut transform, mut velocity, cube) = cube.single_mut();
1021    if keyboard_input.just_pressed(keyboard::KeyCode::R) {
1022        transform.translation = Vec3::new(0.0, 5.0 * 50., 0.0);
1023    }
1024    if keyboard_input.just_pressed(KeyCode::ShiftLeft) {
1025        velocity.linvel.x = 0.;
1026        velocity.linvel.z = 0.;
1027    }
1028    let camera = camera.single();
1029    let mut direction = Vec3::ZERO;
1030    let mut forward_direction = camera.forward();
1031    forward_direction.y = 0.;
1032    forward_direction = forward_direction.normalize();
1033    let mut right_direction = camera.right();
1034    right_direction.y = 0.;
1035    right_direction = right_direction.normalize();
1036    let mut moved = false;
1037
1038    if keyboard_input.pressed(keyboard::KeyCode::W) {
1039        direction += forward_direction;
1040        moved = true;
1041    }
1042    if keyboard_input.pressed(keyboard::KeyCode::S) {
1043        direction -= forward_direction;
1044        moved = true;
1045    }
1046    if keyboard_input.pressed(keyboard::KeyCode::A) {
1047        direction -= right_direction;
1048        moved = true;
1049    }
1050    if keyboard_input.pressed(keyboard::KeyCode::D) {
1051        direction += right_direction;
1052        moved = true;
1053    }
1054    if moved {
1055        let old_y = velocity.linvel.y;
1056        velocity.linvel = direction * cube.max_speed;
1057        velocity.linvel.y = old_y;
1058    }
1059}
1060fn cube_jump(
1061    keyboard_input: Res<Input<keyboard::KeyCode>>,
1062    mut cube: Query<(&mut Velocity, &mut JumpControl)>,
1063) {
1064    let (mut v, mut jump) = cube.single_mut();
1065    if keyboard_input.just_pressed(keyboard::KeyCode::Space)
1066        && jump.jump
1067        && jump.remaining_jumps > 0
1068    {
1069        v.linvel.y = jump.jump_speed;
1070        jump.remaining_jumps -= 1;
1071    }
1072}
1073
1074fn reset_jump(
1075    mut cube: Query<(&mut JumpControl, Entity), With<Cube>>,
1076    rapier_context: Res<RapierContext>,
1077) {
1078    let (mut cube, e) = cube.single_mut();
1079
1080    let contacts = rapier_context.contact_pairs_with(e);
1081    for _contact in contacts {
1082        if _contact.has_any_active_contacts() {
1083            cube.remaining_jumps = 2;
1084            return;
1085        }
1086    }
1087}
1088/// Creates a colorful test pattern
1089fn uv_debug_texture() -> Image {
1090    const TEXTURE_SIZE: usize = 8;
1091
1092    let mut palette: [u8; 32] = [
1093        255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
1094        198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
1095    ];
1096
1097    let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
1098    for y in 0..TEXTURE_SIZE {
1099        let offset = TEXTURE_SIZE * y * 4;
1100        texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
1101        palette.rotate_right(4);
1102    }
1103
1104    Image::new_fill(
1105        Extent3d {
1106            width: TEXTURE_SIZE as u32,
1107            height: TEXTURE_SIZE as u32,
1108            depth_or_array_layers: 1,
1109        },
1110        TextureDimension::D2,
1111        &texture_data,
1112        TextureFormat::Rgba8UnormSrgb,
1113    )
1114}