|
|
|
|
@ -80,7 +80,9 @@ fn main() {
|
|
|
|
|
sync_resource_to_ui::<RewindFrames>.run_if(resource_changed::<RewindFrames>),
|
|
|
|
|
),
|
|
|
|
|
scoring.run_if(on_event::<CollisionEnded>),
|
|
|
|
|
manage_batches.run_if(on_event::<CollisionStarted>).run_if(in_state(PlayerState::Alive).or(in_state(PlayerState::Rewind))),
|
|
|
|
|
manage_batches
|
|
|
|
|
.run_if(on_event::<CollisionStarted>)
|
|
|
|
|
.run_if(in_state(PlayerState::Alive).or(in_state(PlayerState::Rewind))),
|
|
|
|
|
update_batch_position.run_if(any_component_changed::<Batch>),
|
|
|
|
|
update_tooltip.run_if(in_state(DebuggingState::On)),
|
|
|
|
|
),
|
|
|
|
|
@ -114,10 +116,15 @@ enum PlayerState {
|
|
|
|
|
// A tape tracking the bird's state every frame
|
|
|
|
|
#[derive(Component, Default)]
|
|
|
|
|
struct Tape {
|
|
|
|
|
translations: Vec<Vec3>,
|
|
|
|
|
rotations: Vec<Quat>,
|
|
|
|
|
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(
|
|
|
|
|
@ -176,9 +183,7 @@ struct Hitbox;
|
|
|
|
|
#[derive(Component, Clone)]
|
|
|
|
|
struct Batch(usize);
|
|
|
|
|
|
|
|
|
|
fn init_first_batches(
|
|
|
|
|
mut commands: Commands
|
|
|
|
|
) {
|
|
|
|
|
fn init_first_batches(mut commands: Commands) {
|
|
|
|
|
commands.spawn(Batch(0));
|
|
|
|
|
commands.spawn(Batch(1));
|
|
|
|
|
commands.spawn(Batch(2));
|
|
|
|
|
@ -205,7 +210,10 @@ fn populate_batch(
|
|
|
|
|
let Batch(batch_id) = batches.get(trigger.target()).unwrap();
|
|
|
|
|
commands
|
|
|
|
|
.entity(trigger.target())
|
|
|
|
|
.insert((Transform::from_xyz(500.0 * (*batch_id) as f32, 0.0, 0.0), Visibility::Inherited))
|
|
|
|
|
.insert((
|
|
|
|
|
Transform::from_xyz(500.0 * (*batch_id) as f32, 0.0, 0.0),
|
|
|
|
|
Visibility::Inherited,
|
|
|
|
|
))
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
parent.spawn(Ground(-2));
|
|
|
|
|
parent.spawn(Ground(-1));
|
|
|
|
|
@ -224,7 +232,7 @@ fn populate_batch(
|
|
|
|
|
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))|{
|
|
|
|
|
root_changes.iter_mut().for_each(|(mut t, Batch(idx))| {
|
|
|
|
|
info!("Updating batch {:?}", idx);
|
|
|
|
|
t.translation.x = 500.0 * (*idx) as f32;
|
|
|
|
|
});
|
|
|
|
|
@ -245,8 +253,9 @@ fn populate_ground(
|
|
|
|
|
ground_assets.material.clone(),
|
|
|
|
|
ground_assets.mesh.clone(),
|
|
|
|
|
Name::new("ground"),
|
|
|
|
|
RigidBody::Static, Collider::rectangle(1.0, 1.0),
|
|
|
|
|
Transform::from_xyz(100.0 * (*idx) as f32, -300.0, -1.0).with_scale(Vec3::splat(100.0))
|
|
|
|
|
RigidBody::Static,
|
|
|
|
|
Collider::rectangle(1.0, 1.0),
|
|
|
|
|
Transform::from_xyz(100.0 * (*idx) as f32, -300.0, -1.0).with_scale(Vec3::splat(100.0)),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -266,17 +275,15 @@ fn populate_pipe(
|
|
|
|
|
pipe_t,
|
|
|
|
|
pipe_assets.material.clone(),
|
|
|
|
|
pipe_assets.mesh.clone(),
|
|
|
|
|
RigidBody::Static, Collider::rectangle(1.0, 1.0),
|
|
|
|
|
RigidBody::Static,
|
|
|
|
|
Collider::rectangle(1.0, 1.0),
|
|
|
|
|
Name::new("pipe"),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The hitbox should cover the entire height of the screen so if the player
|
|
|
|
|
/// passes between the pipes it registers a point
|
|
|
|
|
fn populate_hitbox(
|
|
|
|
|
trigger: Trigger<OnAdd, Hitbox>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
fn populate_hitbox(trigger: Trigger<OnAdd, Hitbox>, mut commands: Commands) {
|
|
|
|
|
commands.entity(trigger.target()).insert((
|
|
|
|
|
RigidBody::Static,
|
|
|
|
|
Collider::rectangle(1.0, 10.0),
|
|
|
|
|
@ -502,7 +509,21 @@ fn toggle_rewind(keycode: Res<ButtonInput<KeyCode>>, mut next: ResMut<NextState<
|
|
|
|
|
|
|
|
|
|
fn record(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
mut birds: Query<(&Transform, &LinearVelocity, &AngularVelocity, &mut Tape), With<Bird>>,
|
|
|
|
|
mut birds: Query<
|
|
|
|
|
(
|
|
|
|
|
&AccumulatedTranslation,
|
|
|
|
|
&LinearVelocity,
|
|
|
|
|
&AngularVelocity,
|
|
|
|
|
&ExternalAngularImpulse,
|
|
|
|
|
&ExternalForce,
|
|
|
|
|
&ExternalImpulse,
|
|
|
|
|
&ExternalTorque,
|
|
|
|
|
&Position,
|
|
|
|
|
&Rotation,
|
|
|
|
|
&mut Tape,
|
|
|
|
|
),
|
|
|
|
|
With<Bird>,
|
|
|
|
|
>,
|
|
|
|
|
) {
|
|
|
|
|
debug_assert!(
|
|
|
|
|
matches!(state.get(), PlayerState::Alive),
|
|
|
|
|
@ -511,11 +532,16 @@ fn record(
|
|
|
|
|
|
|
|
|
|
birds
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.for_each(|(transform, linear_velocity, angular_velocity, mut tape)| {
|
|
|
|
|
tape.translations.push(transform.translation);
|
|
|
|
|
tape.rotations.push(transform.rotation);
|
|
|
|
|
tape.linear_velocities.push(*linear_velocity);
|
|
|
|
|
tape.angular_velocities.push(*angular_velocity);
|
|
|
|
|
.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);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -523,9 +549,15 @@ fn rewind(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
mut birds: Query<
|
|
|
|
|
(
|
|
|
|
|
&mut Transform,
|
|
|
|
|
&mut AngularVelocity,
|
|
|
|
|
&mut AccumulatedTranslation,
|
|
|
|
|
&mut LinearVelocity,
|
|
|
|
|
&mut AngularVelocity,
|
|
|
|
|
&mut ExternalAngularImpulse,
|
|
|
|
|
&mut ExternalForce,
|
|
|
|
|
&mut ExternalImpulse,
|
|
|
|
|
&mut ExternalTorque,
|
|
|
|
|
&mut Position,
|
|
|
|
|
&mut Rotation,
|
|
|
|
|
&mut Tape,
|
|
|
|
|
),
|
|
|
|
|
With<Bird>,
|
|
|
|
|
@ -538,22 +570,24 @@ fn rewind(
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
birds.iter_mut().for_each(
|
|
|
|
|
|(mut transform, mut angular_velocity, mut linear_velocity, mut tape)| {
|
|
|
|
|
match (tape.translations.pop(), tape.rotations.pop()) {
|
|
|
|
|
(Some(t), Some(r)) => {
|
|
|
|
|
transform.translation = t;
|
|
|
|
|
transform.rotation = r;
|
|
|
|
|
frames.0 += 1;
|
|
|
|
|
}
|
|
|
|
|
(None, None) => (),
|
|
|
|
|
_ => panic!("Translations and rotations are out of sync!"),
|
|
|
|
|
|(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() {
|
|
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
// TODO: Only need to set {angular|linear}_velocity at end of Rewind
|
|
|
|
|
if let Some(av) = tape.angular_velocities.pop() {
|
|
|
|
|
*angular_velocity = av;
|
|
|
|
|
}
|
|
|
|
|
if let Some(lv) = tape.linear_velocities.pop() {
|
|
|
|
|
*linear_velocity = lv;
|
|
|
|
|
};
|
|
|
|
|
frames.0 += 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
@ -593,7 +627,9 @@ fn pause_bird(
|
|
|
|
|
debug_assert!(!matches!(state.get(), PlayerState::Alive));
|
|
|
|
|
|
|
|
|
|
// Increment death count
|
|
|
|
|
if state.get() == &PlayerState::Stasis { deaths.0 += 1 }
|
|
|
|
|
if state.get() == &PlayerState::Stasis {
|
|
|
|
|
deaths.0 += 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug!("Setting bird to Static");
|
|
|
|
|
**bird = RigidBody::Static;
|
|
|
|
|
@ -673,32 +709,46 @@ fn manage_batches(
|
|
|
|
|
batches: Query<(Entity, &Batch)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
debug_assert!(matches!(state.get(), PlayerState::Alive) || matches!(state.get(), PlayerState::Rewind));
|
|
|
|
|
debug_assert!(
|
|
|
|
|
matches!(state.get(), PlayerState::Alive) || matches!(state.get(), PlayerState::Rewind)
|
|
|
|
|
);
|
|
|
|
|
events
|
|
|
|
|
.read()
|
|
|
|
|
// This is written in a wonky way to avoid borrow checker rules
|
|
|
|
|
// Instaed of updating the Batch in place we use Commands to get it and upsert a new batch
|
|
|
|
|
.filter_map(|CollisionStarted(a, b)| {
|
|
|
|
|
if bird.contains(*a) && let Ok((e, Batch(idx))) = batches.get(*b) && *idx > 2 {
|
|
|
|
|
if bird.contains(*a)
|
|
|
|
|
&& let Ok((e, Batch(idx))) = batches.get(*b)
|
|
|
|
|
&& *idx > 2
|
|
|
|
|
{
|
|
|
|
|
Some((e, idx))
|
|
|
|
|
} else { None }
|
|
|
|
|
}).for_each(|(_, idx)| {
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.for_each(|(_, idx)| {
|
|
|
|
|
let (old_batch_idx, new_batch_idx) = match state.get() {
|
|
|
|
|
PlayerState::Alive => (idx - 2, idx + 2),
|
|
|
|
|
PlayerState::Rewind => (idx + 2, idx - 2),
|
|
|
|
|
_ => panic!("Should not happen!"),
|
|
|
|
|
};
|
|
|
|
|
// Find all entities with the old batch and adjust them
|
|
|
|
|
batches.iter().filter_map(|(e, batch)| {
|
|
|
|
|
(batch.0 == old_batch_idx).then_some(e)
|
|
|
|
|
}).for_each(|e| {
|
|
|
|
|
batches
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|(e, batch)| (batch.0 == old_batch_idx).then_some(e))
|
|
|
|
|
.for_each(|e| {
|
|
|
|
|
commands.entity(e).insert(Batch(new_batch_idx));
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn update_tooltip(
|
|
|
|
|
mut query: Query<(&mut ToolTip, Option<&LinearVelocity>, Option<&Batch>, Entity)>,
|
|
|
|
|
mut query: Query<(
|
|
|
|
|
&mut ToolTip,
|
|
|
|
|
Option<&LinearVelocity>,
|
|
|
|
|
Option<&Batch>,
|
|
|
|
|
Entity,
|
|
|
|
|
)>,
|
|
|
|
|
) {
|
|
|
|
|
query.iter_mut().for_each(|(mut tt, lv, b, _e)| {
|
|
|
|
|
// Add Linear Velocity if present on entity
|
|
|
|
|
|