diff --git a/examples/the-dungeon-puzzlers-lament/src/game/simulation/animation.rs b/examples/the-dungeon-puzzlers-lament/src/game/simulation/animation.rs index 316804f4..e6df07e6 100644 --- a/examples/the-dungeon-puzzlers-lament/src/game/simulation/animation.rs +++ b/examples/the-dungeon-puzzlers-lament/src/game/simulation/animation.rs @@ -6,8 +6,9 @@ use core::ops::{Deref, DerefMut}; use agb::{ display::object::{OamIterator, ObjectUnmanaged, SpriteLoader}, - fixnum::{num, Num, Vector2D}, + fixnum::{Num, Vector2D}, }; +use alloc::vec; use alloc::vec::Vec; use slotmap::SecondaryMap; @@ -26,11 +27,15 @@ struct AnimationEntity { attached: Option<(Item, Num)>, } +struct MovePoints { + sound_effect: Option, + points: Vec>>, +} + #[derive(Default)] struct ToPlay { - moves: Vec, + move_points: SecondaryMap, attach_progress: Vec, - fakeout: Vec, detatch: Vec, attach: Vec, change: Vec, @@ -50,11 +55,44 @@ impl ToPlay { ) { match instruction { AnimationInstruction::Move(e, p, s) => { - self.moves.push(Move(e, convert_to_real_space(p), s)); + let move_points = + self.move_points + .entry(e) + .unwrap() + .or_insert_with(|| MovePoints { + sound_effect: s, + points: map + .get(e) + .map(|x| vec![x.start_position]) + .unwrap_or_default(), + }); + move_points.points.push(convert_to_real_space(p)); + if let Some(sound_effect) = s { + move_points.sound_effect.get_or_insert(sound_effect); + } } - AnimationInstruction::FakeOutMove(e, d, p, s) => { - self.fakeout - .push(FakeOutMove(e, d, p.map(convert_to_real_space), s)) + AnimationInstruction::FakeOutMove(e, d, s) => { + let move_points = + self.move_points + .entry(e) + .unwrap() + .or_insert_with(|| MovePoints { + sound_effect: s, + points: map + .get(e) + .map(|x| vec![x.start_position]) + .unwrap_or_default(), + }); + + if let Some(sound_effect) = s { + move_points.sound_effect.get_or_insert(sound_effect); + } + + let &most_recent_position = move_points.points.last().unwrap(); + move_points + .points + .push(most_recent_position + convert_to_real_space(d.into()) / 2); + move_points.points.push(most_recent_position); } AnimationInstruction::Detatch(e, nk, s) => self.detatch.push(Detatch(e, nk, s)), AnimationInstruction::Attach(e, o, s) => { @@ -157,12 +195,6 @@ impl Map { entity.start_position = destination; } } - - fn set_entity_to_start_location(&mut self, entity: EntityKey) { - if let Some(entity) = self.map.get_mut(entity) { - entity.rendered_position = entity.start_position; - } - } } impl Deref for Map { @@ -179,6 +211,31 @@ impl DerefMut for Map { } } +fn lerp_points, Output = N> + core::ops::Add>( + points: &[N], + t: Num, +) -> N { + let number_of_points = points.len() as i32; + let slope_for_spike_fn = number_of_points - 1; + + let relevant_points_pair_idx = (t * slope_for_spike_fn).floor(); + + let spike_function_for_first = t * -slope_for_spike_fn + relevant_points_pair_idx + 1; + let spike_function_for_second = t * slope_for_spike_fn - relevant_points_pair_idx; + let first_point_idx = relevant_points_pair_idx as usize; + + let &first = points + .get(first_point_idx) + .expect("Maybe input to lerp is out of range?"); + let second = points.get(first_point_idx + 1); + + if let Some(&second) = second { + first * spike_function_for_first + second * spike_function_for_second + } else { + first + } +} + impl Animation { pub fn populate(&mut self, instruction: AnimationInstruction, sfx: &mut Sfx) { self.to_play.populate(instruction, &mut self.map, sfx); @@ -243,30 +300,16 @@ impl Animation { } pub fn update(&mut self, sfx: &mut Sfx) -> bool { - if !self.to_play.moves.is_empty() - || !self.to_play.fakeout.is_empty() - || !self.to_play.attach_progress.is_empty() - { + if !self.to_play.move_points.is_empty() || !self.to_play.attach_progress.is_empty() { if self.time >= 1.into() { // finalise animations - for m in self.to_play.moves.drain(0..) { + for m in self.to_play.move_points.drain() { let entity = m.0; - let destination = m.1; + let &destination = m.1.points.last().unwrap(); self.map.set_entity_start_location(entity, destination); } - for m in self.to_play.fakeout.drain(0..) { - let entity = m.0; - let destination = m.2; - - if let Some(destination) = destination { - self.map.set_entity_start_location(entity, destination); - } else { - self.map.set_entity_to_start_location(entity); - } - } - for m in self.to_play.attach_progress.drain(0..) { if let Some(ease) = self .map @@ -279,49 +322,11 @@ impl Animation { } } else { // play moves and fakeouts - for m in self.to_play.moves.iter_mut() { - let entity = m.0; - let destination = m.1; - - sfx.play_sound_effect(m.2.take()); + for (entity, move_points) in self.to_play.move_points.iter_mut() { + sfx.play_sound_effect(move_points.sound_effect.take()); if let Some(entity) = self.map.get_mut(entity) { - let location = entity.start_position * (Num::::new(1) - self.ease) - + destination * self.ease; - - entity.rendered_position = location; - } - } - - for m in self.to_play.fakeout.iter_mut() { - let entity = m.0; - if let Some(entity) = self.map.get_mut(entity) { - let direction = m.1; - let destination = m.2.unwrap_or(entity.start_position); - let direction = convert_to_real_space(direction.into()); - - sfx.play_sound_effect(m.3.take()); - - let go_to = destination + direction / 2; - - let mix = (self.ease * 2 - 1).abs(); - let start_position_mix = if self.ease <= num!(0.5) { - mix - } else { - 0.into() - }; - - let end_position_mix = if self.ease >= num!(0.5) { - mix - } else { - 0.into() - }; - - let intermediate_position_mix = -mix + 1; - - let location = entity.start_position * start_position_mix - + go_to * intermediate_position_mix - + destination * end_position_mix; + let location = lerp_points(&move_points.points, self.ease); entity.rendered_position = location; } @@ -363,9 +368,13 @@ impl Animation { attached: None, }, ); - self.to_play - .moves - .push(Move(new_key, destination_position, None)); + self.to_play.move_points.insert( + new_key, + MovePoints { + sound_effect: None, + points: vec![position, destination_position], + }, + ); } } } else if !self.to_play.attach.is_empty() { @@ -414,13 +423,6 @@ impl Animation { } } -struct Move(EntityKey, Vector2D>, Option); -struct FakeOutMove( - EntityKey, - Direction, - Option>>, - Option, -); struct Detatch(EntityKey, EntityKey, Option); struct Attach(EntityKey, Item, EntityKey, Option); struct AttachProgress(EntityKey, Option); @@ -431,12 +433,7 @@ struct Change(EntityKey, Item, Option); pub enum AnimationInstruction { Add(EntityKey, Item, Vector2D, Option), Move(EntityKey, Vector2D, Option), - FakeOutMove( - EntityKey, - Direction, - Option>, - Option, - ), + FakeOutMove(EntityKey, Direction, Option), Detatch(EntityKey, EntityKey, Option), Attach(EntityKey, EntityKey, Option), Change(EntityKey, Item, Option), diff --git a/examples/the-dungeon-puzzlers-lament/src/game/simulation/entity.rs b/examples/the-dungeon-puzzlers-lament/src/game/simulation/entity.rs index d4ef4f48..cf173c29 100644 --- a/examples/the-dungeon-puzzlers-lament/src/game/simulation/entity.rs +++ b/examples/the-dungeon-puzzlers-lament/src/game/simulation/entity.rs @@ -44,23 +44,6 @@ impl ActionResult { } } -fn remove_move_animation_for_entity( - animations: &mut Vec, - entity_key: EntityKey, -) { - if let Some(existing_animation) = animations.iter().position(|x| { - if let AnimationInstruction::Move(entity, _, _) = x { - *entity == entity_key - } else if let AnimationInstruction::FakeOutMove(entity, _, _, _) = x { - *entity == entity_key - } else { - false - } - }) { - animations.swap_remove(existing_animation); - } -} - struct HasMoved(bool); struct WantsToMoveAgain(bool); @@ -190,7 +173,6 @@ impl EntityMap { MoveAttemptResolution::AttemptPush => { let depth = push_depth - 1; if depth >= 0 { - remove_move_animation_for_entity(animations, other_entity_key); let (can_move_result, action_result) = self.attempt_move_in_direction( map, animations, @@ -270,7 +252,6 @@ impl EntityMap { animations.push(AnimationInstruction::FakeOutMove( entity_to_update_key, direction, - self.map.get(entity_to_update_key).map(|e| e.location), if explicit_stay_put { self.map .get(entity_to_update_key) @@ -368,7 +349,6 @@ impl EntityMap { break; } OverlapResolution::MoveAgain => { - remove_move_animation_for_entity(animations, entity_to_update_key); should_move_again = true; } OverlapResolution::Teleport => { @@ -382,7 +362,6 @@ impl EntityMap { let location_to_teleport_to = other_teleporter.location; if self.whats_at(location_to_teleport_to).count() == 1 { //ok, we can teleport - remove_move_animation_for_entity(animations, entity_to_update_key); animations.push(AnimationInstruction::Move( entity_to_update_key, location_to_teleport_to,