mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 08:11:33 +11:00
more advanced movement system
This commit is contained in:
parent
025e76b8bd
commit
1bcbeb056e
|
@ -6,8 +6,9 @@ use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use agb::{
|
use agb::{
|
||||||
display::object::{OamIterator, ObjectUnmanaged, SpriteLoader},
|
display::object::{OamIterator, ObjectUnmanaged, SpriteLoader},
|
||||||
fixnum::{num, Num, Vector2D},
|
fixnum::{Num, Vector2D},
|
||||||
};
|
};
|
||||||
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use slotmap::SecondaryMap;
|
use slotmap::SecondaryMap;
|
||||||
|
|
||||||
|
@ -26,11 +27,15 @@ struct AnimationEntity {
|
||||||
attached: Option<(Item, Num<i32, 10>)>,
|
attached: Option<(Item, Num<i32, 10>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MovePoints {
|
||||||
|
sound_effect: Option<SoundEffect>,
|
||||||
|
points: Vec<Vector2D<Num<i32, 10>>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ToPlay {
|
struct ToPlay {
|
||||||
moves: Vec<Move>,
|
move_points: SecondaryMap<EntityKey, MovePoints>,
|
||||||
attach_progress: Vec<AttachProgress>,
|
attach_progress: Vec<AttachProgress>,
|
||||||
fakeout: Vec<FakeOutMove>,
|
|
||||||
detatch: Vec<Detatch>,
|
detatch: Vec<Detatch>,
|
||||||
attach: Vec<Attach>,
|
attach: Vec<Attach>,
|
||||||
change: Vec<Change>,
|
change: Vec<Change>,
|
||||||
|
@ -50,11 +55,44 @@ impl ToPlay {
|
||||||
) {
|
) {
|
||||||
match instruction {
|
match instruction {
|
||||||
AnimationInstruction::Move(e, p, s) => {
|
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
|
AnimationInstruction::FakeOutMove(e, d, s) => {
|
||||||
.push(FakeOutMove(e, d, p.map(convert_to_real_space), 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::Detatch(e, nk, s) => self.detatch.push(Detatch(e, nk, s)),
|
||||||
AnimationInstruction::Attach(e, o, s) => {
|
AnimationInstruction::Attach(e, o, s) => {
|
||||||
|
@ -157,12 +195,6 @@ impl Map {
|
||||||
entity.start_position = destination;
|
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 {
|
impl Deref for Map {
|
||||||
|
@ -179,6 +211,31 @@ impl DerefMut for Map {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lerp_points<N: Copy + core::ops::Mul<Num<i32, 10>, Output = N> + core::ops::Add<Output = N>>(
|
||||||
|
points: &[N],
|
||||||
|
t: Num<i32, 10>,
|
||||||
|
) -> 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 {
|
impl Animation {
|
||||||
pub fn populate(&mut self, instruction: AnimationInstruction, sfx: &mut Sfx) {
|
pub fn populate(&mut self, instruction: AnimationInstruction, sfx: &mut Sfx) {
|
||||||
self.to_play.populate(instruction, &mut self.map, 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 {
|
pub fn update(&mut self, sfx: &mut Sfx) -> bool {
|
||||||
if !self.to_play.moves.is_empty()
|
if !self.to_play.move_points.is_empty() || !self.to_play.attach_progress.is_empty() {
|
||||||
|| !self.to_play.fakeout.is_empty()
|
|
||||||
|| !self.to_play.attach_progress.is_empty()
|
|
||||||
{
|
|
||||||
if self.time >= 1.into() {
|
if self.time >= 1.into() {
|
||||||
// finalise animations
|
// 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 entity = m.0;
|
||||||
let destination = m.1;
|
let &destination = m.1.points.last().unwrap();
|
||||||
|
|
||||||
self.map.set_entity_start_location(entity, destination);
|
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..) {
|
for m in self.to_play.attach_progress.drain(0..) {
|
||||||
if let Some(ease) = self
|
if let Some(ease) = self
|
||||||
.map
|
.map
|
||||||
|
@ -279,49 +322,11 @@ impl Animation {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// play moves and fakeouts
|
// play moves and fakeouts
|
||||||
for m in self.to_play.moves.iter_mut() {
|
for (entity, move_points) in self.to_play.move_points.iter_mut() {
|
||||||
let entity = m.0;
|
sfx.play_sound_effect(move_points.sound_effect.take());
|
||||||
let destination = m.1;
|
|
||||||
|
|
||||||
sfx.play_sound_effect(m.2.take());
|
|
||||||
|
|
||||||
if let Some(entity) = self.map.get_mut(entity) {
|
if let Some(entity) = self.map.get_mut(entity) {
|
||||||
let location = entity.start_position * (Num::<i32, 10>::new(1) - self.ease)
|
let location = lerp_points(&move_points.points, 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;
|
|
||||||
|
|
||||||
entity.rendered_position = location;
|
entity.rendered_position = location;
|
||||||
}
|
}
|
||||||
|
@ -363,9 +368,13 @@ impl Animation {
|
||||||
attached: None,
|
attached: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.to_play
|
self.to_play.move_points.insert(
|
||||||
.moves
|
new_key,
|
||||||
.push(Move(new_key, destination_position, None));
|
MovePoints {
|
||||||
|
sound_effect: None,
|
||||||
|
points: vec![position, destination_position],
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !self.to_play.attach.is_empty() {
|
} else if !self.to_play.attach.is_empty() {
|
||||||
|
@ -414,13 +423,6 @@ impl Animation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Move(EntityKey, Vector2D<Num<i32, 10>>, Option<SoundEffect>);
|
|
||||||
struct FakeOutMove(
|
|
||||||
EntityKey,
|
|
||||||
Direction,
|
|
||||||
Option<Vector2D<Num<i32, 10>>>,
|
|
||||||
Option<SoundEffect>,
|
|
||||||
);
|
|
||||||
struct Detatch(EntityKey, EntityKey, Option<SoundEffect>);
|
struct Detatch(EntityKey, EntityKey, Option<SoundEffect>);
|
||||||
struct Attach(EntityKey, Item, EntityKey, Option<SoundEffect>);
|
struct Attach(EntityKey, Item, EntityKey, Option<SoundEffect>);
|
||||||
struct AttachProgress(EntityKey, Option<SoundEffect>);
|
struct AttachProgress(EntityKey, Option<SoundEffect>);
|
||||||
|
@ -431,12 +433,7 @@ struct Change(EntityKey, Item, Option<SoundEffect>);
|
||||||
pub enum AnimationInstruction {
|
pub enum AnimationInstruction {
|
||||||
Add(EntityKey, Item, Vector2D<i32>, Option<SoundEffect>),
|
Add(EntityKey, Item, Vector2D<i32>, Option<SoundEffect>),
|
||||||
Move(EntityKey, Vector2D<i32>, Option<SoundEffect>),
|
Move(EntityKey, Vector2D<i32>, Option<SoundEffect>),
|
||||||
FakeOutMove(
|
FakeOutMove(EntityKey, Direction, Option<SoundEffect>),
|
||||||
EntityKey,
|
|
||||||
Direction,
|
|
||||||
Option<Vector2D<i32>>,
|
|
||||||
Option<SoundEffect>,
|
|
||||||
),
|
|
||||||
Detatch(EntityKey, EntityKey, Option<SoundEffect>),
|
Detatch(EntityKey, EntityKey, Option<SoundEffect>),
|
||||||
Attach(EntityKey, EntityKey, Option<SoundEffect>),
|
Attach(EntityKey, EntityKey, Option<SoundEffect>),
|
||||||
Change(EntityKey, Item, Option<SoundEffect>),
|
Change(EntityKey, Item, Option<SoundEffect>),
|
||||||
|
|
|
@ -44,23 +44,6 @@ impl ActionResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_move_animation_for_entity(
|
|
||||||
animations: &mut Vec<AnimationInstruction>,
|
|
||||||
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 HasMoved(bool);
|
||||||
struct WantsToMoveAgain(bool);
|
struct WantsToMoveAgain(bool);
|
||||||
|
|
||||||
|
@ -190,7 +173,6 @@ impl EntityMap {
|
||||||
MoveAttemptResolution::AttemptPush => {
|
MoveAttemptResolution::AttemptPush => {
|
||||||
let depth = push_depth - 1;
|
let depth = push_depth - 1;
|
||||||
if depth >= 0 {
|
if depth >= 0 {
|
||||||
remove_move_animation_for_entity(animations, other_entity_key);
|
|
||||||
let (can_move_result, action_result) = self.attempt_move_in_direction(
|
let (can_move_result, action_result) = self.attempt_move_in_direction(
|
||||||
map,
|
map,
|
||||||
animations,
|
animations,
|
||||||
|
@ -270,7 +252,6 @@ impl EntityMap {
|
||||||
animations.push(AnimationInstruction::FakeOutMove(
|
animations.push(AnimationInstruction::FakeOutMove(
|
||||||
entity_to_update_key,
|
entity_to_update_key,
|
||||||
direction,
|
direction,
|
||||||
self.map.get(entity_to_update_key).map(|e| e.location),
|
|
||||||
if explicit_stay_put {
|
if explicit_stay_put {
|
||||||
self.map
|
self.map
|
||||||
.get(entity_to_update_key)
|
.get(entity_to_update_key)
|
||||||
|
@ -368,7 +349,6 @@ impl EntityMap {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
OverlapResolution::MoveAgain => {
|
OverlapResolution::MoveAgain => {
|
||||||
remove_move_animation_for_entity(animations, entity_to_update_key);
|
|
||||||
should_move_again = true;
|
should_move_again = true;
|
||||||
}
|
}
|
||||||
OverlapResolution::Teleport => {
|
OverlapResolution::Teleport => {
|
||||||
|
@ -382,7 +362,6 @@ impl EntityMap {
|
||||||
let location_to_teleport_to = other_teleporter.location;
|
let location_to_teleport_to = other_teleporter.location;
|
||||||
if self.whats_at(location_to_teleport_to).count() == 1 {
|
if self.whats_at(location_to_teleport_to).count() == 1 {
|
||||||
//ok, we can teleport
|
//ok, we can teleport
|
||||||
remove_move_animation_for_entity(animations, entity_to_update_key);
|
|
||||||
animations.push(AnimationInstruction::Move(
|
animations.push(AnimationInstruction::Move(
|
||||||
entity_to_update_key,
|
entity_to_update_key,
|
||||||
location_to_teleport_to,
|
location_to_teleport_to,
|
||||||
|
|
Loading…
Reference in a new issue