|
|
|
|
@ -2,7 +2,6 @@
|
|
|
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
|
|
|
|
|
|
use bevy::audio::PlaybackMode;
|
|
|
|
|
use bevy::image::{ImageLoaderSettings, ImageSampler};
|
|
|
|
|
use bevy::render::view::ColorGrading;
|
|
|
|
|
use games::physics2d::*;
|
|
|
|
|
use games::*;
|
|
|
|
|
@ -36,7 +35,6 @@ fn main() {
|
|
|
|
|
init_bird.after(init_assets),
|
|
|
|
|
init_first_batches.after(init_assets),
|
|
|
|
|
init_ui,
|
|
|
|
|
init_background,
|
|
|
|
|
tweak_camera.after(create_camera_2d),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
@ -92,7 +90,7 @@ fn main() {
|
|
|
|
|
(update_tooltip, debug_trail).run_if(in_state(DebuggingState::On)),
|
|
|
|
|
// TODO: Add run_if to this system
|
|
|
|
|
update_batch_position.run_if(any_component_changed::<Batch>),
|
|
|
|
|
move_batches.run_if(on_event::<CollisionStarted>.or(on_event::<CollisionEnded>)),
|
|
|
|
|
move_batches.run_if(on_event::<CollisionStarted>),
|
|
|
|
|
manage_score.run_if(on_event::<CollisionStarted>.or(on_event::<CollisionEnded>)),
|
|
|
|
|
shimmer_button::<RewindButton>.run_if(in_state(PlayerState::Stasis)),
|
|
|
|
|
shimmer_button::<FlapButton>.run_if(in_state(PlayerState::Pause)),
|
|
|
|
|
@ -101,7 +99,6 @@ fn main() {
|
|
|
|
|
.add_observer(flap)
|
|
|
|
|
.add_observer(populate_batch)
|
|
|
|
|
.add_observer(populate_pipe)
|
|
|
|
|
.add_observer(move_pipe)
|
|
|
|
|
.add_observer(populate_ground)
|
|
|
|
|
.add_observer(populate_ceiling)
|
|
|
|
|
.add_observer(populate_hitbox)
|
|
|
|
|
@ -111,7 +108,7 @@ fn main() {
|
|
|
|
|
fn tweak_camera(camera: Single<(Entity, &mut Camera)>, mut commands: Commands) {
|
|
|
|
|
debug!("Tweaking camera");
|
|
|
|
|
let (e, mut c) = camera.into_inner();
|
|
|
|
|
c.clear_color = ClearColorConfig::Custom(SKY_BLUE.into());
|
|
|
|
|
c.clear_color = ClearColorConfig::Custom(WHITE.into());
|
|
|
|
|
commands.entity(e).insert(ColorGrading { ..default() });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -130,72 +127,17 @@ enum PlayerState {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A tape tracking the bird's state every frame
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
#[derive(Component, Default)]
|
|
|
|
|
struct Tape {
|
|
|
|
|
capacity: usize,
|
|
|
|
|
linear_velocities: VecDeque<LinearVelocity>,
|
|
|
|
|
angular_velocities: VecDeque<AngularVelocity>,
|
|
|
|
|
external_impulses: VecDeque<ExternalImpulse>,
|
|
|
|
|
positions: VecDeque<Position>,
|
|
|
|
|
rotations: VecDeque<Rotation>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Tape {
|
|
|
|
|
fn new_with_capacity(capacity: usize) -> Self {
|
|
|
|
|
Tape {
|
|
|
|
|
capacity,
|
|
|
|
|
linear_velocities: VecDeque::with_capacity(capacity),
|
|
|
|
|
angular_velocities: VecDeque::with_capacity(capacity),
|
|
|
|
|
external_impulses: VecDeque::with_capacity(capacity),
|
|
|
|
|
positions: VecDeque::with_capacity(capacity),
|
|
|
|
|
rotations: VecDeque::with_capacity(capacity),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn push(
|
|
|
|
|
&mut self,
|
|
|
|
|
lv: LinearVelocity,
|
|
|
|
|
av: AngularVelocity,
|
|
|
|
|
ei: ExternalImpulse,
|
|
|
|
|
p: Position,
|
|
|
|
|
r: Rotation,
|
|
|
|
|
) {
|
|
|
|
|
// If we are at capacity, make room
|
|
|
|
|
if self.linear_velocities.len() == self.capacity {
|
|
|
|
|
self.linear_velocities.pop_front().unwrap();
|
|
|
|
|
self.angular_velocities.pop_front().unwrap();
|
|
|
|
|
self.external_impulses.pop_front().unwrap();
|
|
|
|
|
self.positions.pop_front().unwrap();
|
|
|
|
|
self.rotations.pop_front().unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.linear_velocities.push_back(lv);
|
|
|
|
|
self.angular_velocities.push_back(av);
|
|
|
|
|
self.external_impulses.push_back(ei);
|
|
|
|
|
self.positions.push_back(p);
|
|
|
|
|
self.rotations.push_back(r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pop(
|
|
|
|
|
&mut self,
|
|
|
|
|
) -> Option<(
|
|
|
|
|
LinearVelocity,
|
|
|
|
|
AngularVelocity,
|
|
|
|
|
ExternalImpulse,
|
|
|
|
|
Position,
|
|
|
|
|
Rotation,
|
|
|
|
|
)> {
|
|
|
|
|
if self.linear_velocities.is_empty() {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
let lv = self.linear_velocities.pop_back().unwrap();
|
|
|
|
|
let av = self.angular_velocities.pop_back().unwrap();
|
|
|
|
|
let ei = self.external_impulses.pop_back().unwrap();
|
|
|
|
|
let p = self.positions.pop_back().unwrap();
|
|
|
|
|
let r = self.rotations.pop_back().unwrap();
|
|
|
|
|
Some((lv, av, ei, p, r))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
accumulated_translations: Vec<AccumulatedTranslation>,
|
|
|
|
|
linear_velocities: Vec<LinearVelocity>,
|
|
|
|
|
angular_velocities: Vec<AngularVelocity>,
|
|
|
|
|
external_angular_impulses: Vec<ExternalAngularImpulse>,
|
|
|
|
|
external_forces: Vec<ExternalForce>,
|
|
|
|
|
external_impulses: Vec<ExternalImpulse>,
|
|
|
|
|
external_torques: Vec<ExternalTorque>,
|
|
|
|
|
positions: Vec<Position>,
|
|
|
|
|
rotations: Vec<Rotation>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn init_bird(mut commands: Commands, bird_assets: Res<BirdAssets>) {
|
|
|
|
|
@ -211,9 +153,7 @@ fn init_bird(mut commands: Commands, bird_assets: Res<BirdAssets>) {
|
|
|
|
|
MaxLinearSpeed(500.0),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 60fps * 5 seconds
|
|
|
|
|
const REWIND_SECONDS: usize = 5;
|
|
|
|
|
let tape = Tape::new_with_capacity(60 * REWIND_SECONDS);
|
|
|
|
|
let tape = Tape::default();
|
|
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
name,
|
|
|
|
|
@ -300,7 +240,7 @@ fn update_batch_position(
|
|
|
|
|
mut root_changes: Query<(&mut Transform, &Batch), (Changed<Batch>, Without<ChildOf>)>,
|
|
|
|
|
) {
|
|
|
|
|
root_changes.iter_mut().for_each(|(mut t, Batch(idx))| {
|
|
|
|
|
debug!("Updating batch {:?}", idx);
|
|
|
|
|
info!("Updating batch {:?}", idx);
|
|
|
|
|
t.translation.x = 500.0 * (*idx) as f32;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -341,49 +281,18 @@ fn populate_ceiling(
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn move_pipe(
|
|
|
|
|
trigger: Trigger<OnInsert, Batch>,
|
|
|
|
|
mut pipes: Query<(&Batch, &Pipe, &mut Transform)>,
|
|
|
|
|
rand: Res<Rand>,
|
|
|
|
|
) {
|
|
|
|
|
if let Ok((Batch(id), pipe, mut pipe_t)) = pipes.get_mut(trigger.target()) {
|
|
|
|
|
*pipe_t = {
|
|
|
|
|
let offset = {
|
|
|
|
|
let val = rand.0.hash_one(id);
|
|
|
|
|
|
|
|
|
|
let option = val % 3;
|
|
|
|
|
|
|
|
|
|
match option {
|
|
|
|
|
0 => 100.0,
|
|
|
|
|
1 => 0.0,
|
|
|
|
|
2 => -100.0,
|
|
|
|
|
_ => panic!("Can only pick 1 of 3 pipe offsets"),
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match pipe {
|
|
|
|
|
Pipe::Top => {
|
|
|
|
|
Transform::from_xyz(0.0, 300.0 + offset, -2.0).with_scale(Vec3::splat(100.0))
|
|
|
|
|
}
|
|
|
|
|
Pipe::Bottom => {
|
|
|
|
|
Transform::from_xyz(0.0, -300.0 + offset, -2.0).with_scale(Vec3::splat(100.0))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Based on if this is a Top or Bottom pipe the placement changes
|
|
|
|
|
/// Otherwise this just spawns in the center of the batch.
|
|
|
|
|
fn populate_pipe(
|
|
|
|
|
trigger: Trigger<OnAdd, Pipe>,
|
|
|
|
|
pipes: Query<(&Batch, &Pipe)>,
|
|
|
|
|
pipes: Query<(&Pipe, &Batch)>,
|
|
|
|
|
pipe_assets: Res<PipeAssets>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
rand: Res<Rand>,
|
|
|
|
|
) {
|
|
|
|
|
let pipe_t = {
|
|
|
|
|
let (Batch(id), pipe) = pipes.get(trigger.target()).unwrap();
|
|
|
|
|
let (pipe, Batch(id)) = pipes.get(trigger.target()).unwrap();
|
|
|
|
|
|
|
|
|
|
let offset = {
|
|
|
|
|
let val = rand.0.hash_one(id);
|
|
|
|
|
|
|
|
|
|
@ -406,9 +315,8 @@ fn populate_pipe(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commands.entity(trigger.target()).insert((
|
|
|
|
|
pipe_t,
|
|
|
|
|
pipe_t,
|
|
|
|
|
pipe_assets.material.clone(),
|
|
|
|
|
pipe_assets.mesh.clone(),
|
|
|
|
|
RigidBody::Static,
|
|
|
|
|
@ -473,14 +381,14 @@ fn init_assets(
|
|
|
|
|
|
|
|
|
|
ground_assets.material = MeshMaterial2d(materials.add(ColorMaterial {
|
|
|
|
|
texture: Some(server.load("flappy/ground.png")),
|
|
|
|
|
color: Srgba::new(0.1, 0.1, 0.1, 1.0).into(),
|
|
|
|
|
color: BLACK.into(),
|
|
|
|
|
..default()
|
|
|
|
|
}));
|
|
|
|
|
ground_assets.mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0)));
|
|
|
|
|
|
|
|
|
|
ceiling_assets.material = MeshMaterial2d(materials.add(ColorMaterial {
|
|
|
|
|
texture: Some(server.load("flappy/ceiling.png")),
|
|
|
|
|
color: Srgba::new(0.1, 0.1, 0.1, 1.0).into(),
|
|
|
|
|
color: BLACK.into(),
|
|
|
|
|
..default()
|
|
|
|
|
}));
|
|
|
|
|
ceiling_assets.mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 0.777)));
|
|
|
|
|
@ -509,7 +417,7 @@ struct RewindSfx;
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
struct RewindButton;
|
|
|
|
|
|
|
|
|
|
fn init_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
fn init_ui(mut commands: Commands) {
|
|
|
|
|
commands
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
@ -616,6 +524,10 @@ fn init_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
})
|
|
|
|
|
.observe(hide_credits);
|
|
|
|
|
|
|
|
|
|
fn start_game(_trigger: Trigger<Pointer<Click>>, mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
next.set(PlayerState::Alive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::Center,
|
|
|
|
|
@ -638,14 +550,6 @@ fn init_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
..default()
|
|
|
|
|
})
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
let rewind_image = server.load_with_settings(
|
|
|
|
|
"flappy/rewind.png",
|
|
|
|
|
|settings: &mut ImageLoaderSettings| {
|
|
|
|
|
// Need to use nearest filtering to avoid bleeding between the slices with tiling
|
|
|
|
|
settings.sampler = ImageSampler::nearest();
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
parent
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
@ -658,34 +562,13 @@ fn init_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
BackgroundColor::default(),
|
|
|
|
|
RewindButton,
|
|
|
|
|
children![
|
|
|
|
|
(
|
|
|
|
|
ImageNode {
|
|
|
|
|
color: BLACK.into(),
|
|
|
|
|
image: rewind_image,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Node {
|
|
|
|
|
height: Val::Px(50.0),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
Text::new("Rewind! (R)"),
|
|
|
|
|
TextFont::from_font_size(30.0),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
Text::new("Rewind! (R)"),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
],
|
|
|
|
|
))
|
|
|
|
|
.observe(start_rewind)
|
|
|
|
|
.observe(end_rewind);
|
|
|
|
|
|
|
|
|
|
let play_image = server.load_with_settings(
|
|
|
|
|
"flappy/play.png",
|
|
|
|
|
|settings: &mut ImageLoaderSettings| {
|
|
|
|
|
// Need to use nearest filtering to avoid bleeding between the slices with tiling
|
|
|
|
|
settings.sampler = ImageSampler::nearest();
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
parent
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
@ -697,22 +580,9 @@ fn init_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
Button,
|
|
|
|
|
FlapButton,
|
|
|
|
|
children![
|
|
|
|
|
(
|
|
|
|
|
Text::new("Flap! (Spacebar)"),
|
|
|
|
|
TextFont::from_font_size(30.0),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
ImageNode {
|
|
|
|
|
color: BLACK.into(),
|
|
|
|
|
image: play_image,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Node {
|
|
|
|
|
height: Val::Px(50.0),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
Text::new("Flap! (Spacebar)"),
|
|
|
|
|
TextFont::from_font_size(30.0),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
],
|
|
|
|
|
))
|
|
|
|
|
.observe(flap_button);
|
|
|
|
|
@ -729,53 +599,10 @@ fn init_ui(mut commands: Commands, server: Res<AssetServer>) {
|
|
|
|
|
SyncResource::<Score>::default(),
|
|
|
|
|
Text::default(),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center),
|
|
|
|
|
)],
|
|
|
|
|
)]
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn init_background(
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
|
|
|
|
) {
|
|
|
|
|
{
|
|
|
|
|
let material = MeshMaterial2d(materials.add(ColorMaterial {
|
|
|
|
|
texture: Some(server.load("flappy/background-city.png")),
|
|
|
|
|
color: Srgba::new(0.3, 0.3, 0.3, 1.0).into(),
|
|
|
|
|
alpha_mode: AlphaMode2d::Blend,
|
|
|
|
|
..default()
|
|
|
|
|
}));
|
|
|
|
|
let mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0)));
|
|
|
|
|
{
|
|
|
|
|
let t = Transform::from_xyz(-325.0, 0.0, -32.0).with_scale(Vec3::splat(650.0));
|
|
|
|
|
commands.spawn((ParallaxDepth(4.0), mesh.clone(), material.clone(), t));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let t = Transform::from_xyz(325.0, 0.0, -32.0).with_scale(Vec3::splat(650.0));
|
|
|
|
|
commands.spawn((ParallaxDepth(4.0), mesh.clone(), material.clone(), t));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let material = MeshMaterial2d(materials.add(ColorMaterial {
|
|
|
|
|
texture: Some(server.load("flappy/background-clouds.png")),
|
|
|
|
|
color: Srgba::new(0.2, 0.2, 0.2, 1.0).into(),
|
|
|
|
|
alpha_mode: AlphaMode2d::Blend,
|
|
|
|
|
..default()
|
|
|
|
|
}));
|
|
|
|
|
let mesh = Mesh2d(meshes.add(Rectangle::new(1.0, 1.0)));
|
|
|
|
|
{
|
|
|
|
|
let t = Transform::from_xyz(-325.0, 0.0, -64.0).with_scale(Vec3::splat(650.0));
|
|
|
|
|
commands.spawn((ParallaxDepth(8.0), mesh.clone(), material.clone(), t));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
let t = Transform::from_xyz(325.0, 0.0, -64.0).with_scale(Vec3::splat(650.0));
|
|
|
|
|
commands.spawn((ParallaxDepth(8.0), mesh.clone(), material.clone(), t));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn start_rewind(_trigger: Trigger<Pointer<Pressed>>, mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
next.set(PlayerState::Rewind);
|
|
|
|
|
}
|
|
|
|
|
@ -882,9 +709,13 @@ fn record(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
mut birds: Query<
|
|
|
|
|
(
|
|
|
|
|
&AccumulatedTranslation,
|
|
|
|
|
&LinearVelocity,
|
|
|
|
|
&AngularVelocity,
|
|
|
|
|
&ExternalAngularImpulse,
|
|
|
|
|
&ExternalForce,
|
|
|
|
|
&ExternalImpulse,
|
|
|
|
|
&ExternalTorque,
|
|
|
|
|
&Position,
|
|
|
|
|
&Rotation,
|
|
|
|
|
&mut Tape,
|
|
|
|
|
@ -898,9 +729,19 @@ fn record(
|
|
|
|
|
"Only record in the alive state"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
birds.iter_mut().for_each(|(lv, av, ei, p, r, mut tape)| {
|
|
|
|
|
tape.push(*lv, *av, *ei, *p, *r);
|
|
|
|
|
});
|
|
|
|
|
birds
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.for_each(|(at, lv, av, eai, ef, ei, et, p, r, mut tape)| {
|
|
|
|
|
tape.accumulated_translations.push(*at);
|
|
|
|
|
tape.linear_velocities.push(*lv);
|
|
|
|
|
tape.angular_velocities.push(*av);
|
|
|
|
|
tape.external_angular_impulses.push(*eai);
|
|
|
|
|
tape.external_forces.push(*ef);
|
|
|
|
|
tape.external_impulses.push(*ei);
|
|
|
|
|
tape.external_torques.push(*et);
|
|
|
|
|
tape.positions.push(*p);
|
|
|
|
|
tape.rotations.push(*r);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn rewind(
|
|
|
|
|
@ -908,9 +749,13 @@ fn rewind(
|
|
|
|
|
mut next: ResMut<NextState<PlayerState>>,
|
|
|
|
|
mut birds: Query<
|
|
|
|
|
(
|
|
|
|
|
&mut AccumulatedTranslation,
|
|
|
|
|
&mut LinearVelocity,
|
|
|
|
|
&mut AngularVelocity,
|
|
|
|
|
&mut ExternalAngularImpulse,
|
|
|
|
|
&mut ExternalForce,
|
|
|
|
|
&mut ExternalImpulse,
|
|
|
|
|
&mut ExternalTorque,
|
|
|
|
|
&mut Position,
|
|
|
|
|
&mut Rotation,
|
|
|
|
|
&mut Tape,
|
|
|
|
|
@ -925,20 +770,31 @@ fn rewind(
|
|
|
|
|
"Only rewind in the rewinding state"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
birds
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.for_each(|(mut lv, mut av, mut ei, mut p, mut r, mut tape)| {
|
|
|
|
|
if let Some((new_lv, new_av, new_ei, new_p, new_r)) = tape.pop() {
|
|
|
|
|
lv.0 = new_lv.0;
|
|
|
|
|
av.0 = new_av.0;
|
|
|
|
|
ei.set_impulse(new_ei.impulse());
|
|
|
|
|
p.0 = new_p.0;
|
|
|
|
|
*r = new_r;
|
|
|
|
|
frames.0 += 1;
|
|
|
|
|
} else {
|
|
|
|
|
birds.iter_mut().for_each(
|
|
|
|
|
|(mut at, mut lv, mut av, mut eai, mut ef, mut ei, mut et, mut p, mut r, mut tape)| {
|
|
|
|
|
if tape.positions.is_empty() {
|
|
|
|
|
next.set(PlayerState::Pause);
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: Only record/restore variables that we manage!
|
|
|
|
|
at.0 = tape.accumulated_translations.pop().unwrap().0;
|
|
|
|
|
lv.0 = tape.linear_velocities.pop().unwrap().0;
|
|
|
|
|
av.0 = tape.angular_velocities.pop().unwrap().0;
|
|
|
|
|
eai.set_impulse(tape.external_angular_impulses.pop().unwrap().impulse());
|
|
|
|
|
ef.set_force(tape.external_forces.pop().unwrap().force());
|
|
|
|
|
ei.set_impulse(tape.external_impulses.pop().unwrap().impulse());
|
|
|
|
|
et.set_torque(tape.external_torques.pop().unwrap().torque());
|
|
|
|
|
p.0 = tape.positions.pop().unwrap().0;
|
|
|
|
|
*r = {
|
|
|
|
|
let curr = tape.rotations.pop().unwrap();
|
|
|
|
|
Rotation {
|
|
|
|
|
cos: curr.cos,
|
|
|
|
|
sin: curr.sin,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
frames.0 += 1;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PERF: May run more than necessary, should be event-driven on aabb intersection
|
|
|
|
|
@ -1083,16 +939,12 @@ fn manage_score(
|
|
|
|
|
/// This includes root batch entities as well as pipes and hitboxes
|
|
|
|
|
fn move_batches(
|
|
|
|
|
mut start: EventReader<CollisionStarted>,
|
|
|
|
|
mut end: EventReader<CollisionEnded>,
|
|
|
|
|
hitboxes: Query<Entity, With<Hitbox>>,
|
|
|
|
|
batches: Query<(Entity, &Batch)>,
|
|
|
|
|
state: Res<State<PlayerState>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
let s = start.read().map(|CollisionStarted(a, b)| (a, b));
|
|
|
|
|
let e = end.read().map(|CollisionEnded(a, b)| (a, b));
|
|
|
|
|
let c = s.chain(e);
|
|
|
|
|
c.for_each(|(a, b)| {
|
|
|
|
|
start.read().for_each(|CollisionStarted(a, b)| {
|
|
|
|
|
debug!("[batches] Collision {a} -> {b}");
|
|
|
|
|
|
|
|
|
|
let target = {
|
|
|
|
|
@ -1107,7 +959,7 @@ fn move_batches(
|
|
|
|
|
|
|
|
|
|
let (_, Batch(curr)) = batches.get(target).unwrap();
|
|
|
|
|
|
|
|
|
|
debug!("[batches] Current: {curr}");
|
|
|
|
|
info!("[batches] Current: {curr}");
|
|
|
|
|
let (old_batch, new_batch) = match state.get() {
|
|
|
|
|
PlayerState::Alive => (curr.saturating_sub(2), curr.saturating_add(2)),
|
|
|
|
|
PlayerState::Rewind => (curr.saturating_add(2), curr.saturating_sub(2)),
|
|
|
|
|
@ -1120,7 +972,7 @@ fn move_batches(
|
|
|
|
|
// Filter to just entities with this batch ID
|
|
|
|
|
.filter_map(|(e, Batch(id))| (*id == old_batch).then_some(e))
|
|
|
|
|
.for_each(|old| {
|
|
|
|
|
debug!("Moving batch {old_batch}({old}) -> {new_batch}");
|
|
|
|
|
info!("Moving batch {old_batch}({old}) -> {new_batch}");
|
|
|
|
|
commands.entity(old).insert(Batch(new_batch));
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|