diff --git a/src/animations.rs b/src/animations.rs index 8995834..ae21778 100644 --- a/src/animations.rs +++ b/src/animations.rs @@ -1,20 +1,24 @@ use std::collections::HashMap; use std::time::Duration; -pub type BlockOffsets = HashMap; +use crate::enums::Board; +use crate::level::{ChangeSet, Entity, FailSet}; + +pub type MoveResult = Result; +pub type BlockOffsets = HashMap; // TODO: don't yeet around a HashMap all the time -pub type AnimationFn = Box BlockOffsets>; +pub type AnimationFn = Box BlockOffsets>; +// in seconds const ANIMATION_DURATION: f32 = 1.0 / 6.0; -#[derive(Default, Serialize)] +#[derive(Default)] pub struct AnimationState { pub is_animating: bool, - pub last_move_success: bool, + pub last_move_result: Option, pub progress: f32, pub block_offsets: BlockOffsets, - #[serde(skip)] progress_function: Option, } @@ -22,13 +26,46 @@ impl AnimationState { pub fn new() -> Self { AnimationState { is_animating: false, - last_move_success: true, + last_move_result: None, progress: 0.0, block_offsets: BlockOffsets::new(), progress_function: None, } } + pub fn begin_move_transition(&mut self, result: MoveResult) { + self.last_move_result = Some(result); + self.is_animating = true; + self.progress = 0.0; + let func = |last_move_result: MoveResult, mut offsets: BlockOffsets, progress: f32| { + use std::f32::consts::PI; + match last_move_result { + // transition + Ok(change_set) => { + for (entity, direction) in change_set { + // TODO: implement ease-out? + let pair = direction.as_pair(); + let offset = (pair.0 as f32 * progress, pair.1 as f32 * progress); + println!( + "|| entity: {:?}, direction: {:?} => {:?}", + entity, direction, offset + ); + offsets.insert(entity.clone(), offset); + } + } + // vibrate all blocking pieces + Err(fail_set) => { + for index in fail_set { + let delta = 0.05 * (4.0 * PI * progress).sin() / (progress + 0.5); + offsets.insert(Entity::Block(index), (delta, delta)); + } + } + } + offsets + }; + self.progress_function = Some(Box::new(func)); + } + pub fn begin_transition(&mut self, f: AnimationFn) { self.is_animating = true; self.progress = 0.0; @@ -36,26 +73,45 @@ impl AnimationState { } pub fn make_progress(&mut self, delta: Duration) { - let progress = self.progress + delta.as_millis() as f32 / 1000.0; + let progress = self.progress + (delta.as_millis() as f32 / ANIMATION_DURATION) / 1000.0; + let block_offsets = if let Some(f) = &self.progress_function { - Some(f(self.block_offsets.clone(), progress)) + Some(f( + self.last_move_result.clone().unwrap(), + self.block_offsets.clone(), + progress, + )) } else { None }; + + // this should always work if let Some(block_offsets) = block_offsets { self.block_offsets = block_offsets; self.progress = progress; } + + if self.progress > 1.0 { + self.is_animating = false; + self.block_offsets = BlockOffsets::new(); + } } pub fn is_done(&self) -> bool { self.progress > 1.0 } - pub fn get_offset(&self, index: usize) -> (i32, i32) { + pub fn get_block_offset(&self, index: usize) -> (f32, f32) { self.block_offsets - .get(&index) + .get(&Entity::Block(index)) .cloned() - .unwrap_or_else(|| (0, 0)) + .unwrap_or_else(|| (0.0, 0.0)) + } + + pub fn get_player_offset(&self, board: Board) -> (f32, f32) { + self.block_offsets + .get(&Entity::Player(board)) + .cloned() + .unwrap_or_else(|| (0.0, 0.0)) } } diff --git a/src/game.rs b/src/game.rs index f5eb959..6c9586c 100644 --- a/src/game.rs +++ b/src/game.rs @@ -100,18 +100,29 @@ impl<'a> Game<'a> { println!("pushed: {:?}", $key); let level = self.get_current_level_mut(); let result = level.try_move($board, $direction); - println!("game result: {:?}", result); + self.animations.begin_move_transition(result); self.keymap.insert($key, false); } }; } if self.animations.is_animating { - if self.animations.last_move_success { - if self.animations.is_done() { - self.animations.make_progress(delta); + println!("animating. {:?}", self.animations.progress); + self.animations.make_progress(delta); + + // we just finished! + if !self.animations.is_animating { + // apply the changes to the entities + // this indirection is used to dodge a concurrent borrow + let change_set = if let Some(Ok(change_set)) = &self.animations.last_move_result { + Some(change_set.clone()) + } else { + None + }; + if let Some(change_set) = change_set { + let level = self.get_current_level_mut(); + level.apply_change_set(change_set.clone()); } - } else { } } else { shit!(VirtualKeyCode::W, Board::Left, PushDir::Up); @@ -125,13 +136,7 @@ impl<'a> Game<'a> { shit!(VirtualKeyCode::L, Board::Right, PushDir::Right); // failed a move - if !self.animations.last_move_success { - let func = |mut offsets: HashMap<_, _>, prog| { - offsets.insert(0, (0, 0)); - offsets - }; - self.animations.begin_transition(Box::new(func)); - } + if let Some(Err(fail_set)) = &self.animations.last_move_result {} } } diff --git a/src/level/mod.rs b/src/level/mod.rs index 7727a55..52124e0 100644 --- a/src/level/mod.rs +++ b/src/level/mod.rs @@ -96,38 +96,33 @@ impl Level { } } - pub fn try_move(&mut self, board: Board, direction: PushDir) { - let mut change_set = ChangeSet::default(); - change_set.insert(Entity::Player(board), direction); - - let result = self.player_can_move(board, direction, change_set); - - match result { - Ok(change_set) => { - println!("change_set: {:?}", change_set); - for (entity, direction) in change_set { - let direction = direction.as_pair(); - match entity { - Entity::Player(board) => { - let player = match board { - Board::Left => &mut self.player1, - Board::Right => &mut self.player2, - }; - player.position.0 += direction.0; - player.position.1 += direction.1; - } - Entity::Block(index) => { - let block = self.blocks.get_mut(index).expect("big failure"); - for segment in &mut block.segments { - segment.position.0 += direction.0; - segment.position.1 += direction.1; - } - } + pub fn apply_change_set(&mut self, change_set: ChangeSet) { + for (entity, direction) in change_set { + let direction = direction.as_pair(); + match entity { + Entity::Player(board) => { + let player = match board { + Board::Left => &mut self.player1, + Board::Right => &mut self.player2, + }; + player.position.0 += direction.0; + player.position.1 += direction.1; + } + Entity::Block(index) => { + let block = self.blocks.get_mut(index).expect("big failure"); + for segment in &mut block.segments { + segment.position.0 += direction.0; + segment.position.1 += direction.1; } } } - Err(fail_set) => {} - }; + } + } + + pub fn try_move(&mut self, board: Board, direction: PushDir) -> Result { + let mut change_set = ChangeSet::default(); + change_set.insert(Entity::Player(board), direction); + self.player_can_move(board, direction, change_set) } fn player_can_move( @@ -369,9 +364,9 @@ impl Level { offset.0 + segment.position.0 * scale, offset.1 + segment.position.1 * scale, ); - let animation_offset = animations.get_offset(i); - location.0 += animation_offset.0; - location.1 += animation_offset.1; + let animation_offset = animations.get_block_offset(i); + location.0 += (animation_offset.0 * scale as f32) as i32; + location.1 += (animation_offset.1 * scale as f32) as i32; renderer.render_segment( location, scale, @@ -383,21 +378,40 @@ impl Level { } // render player - self.render_player(renderer, &self.player1, scale, left_off); - self.render_player(renderer, &self.player2, scale, right_off); + self.render_player( + renderer, + Board::Left, + &self.player1, + scale, + animations, + left_off, + ); + self.render_player( + renderer, + Board::Right, + &self.player2, + scale, + animations, + right_off, + ); } fn render_player( &self, renderer: &mut Renderer, + board: Board, player: &Player, scale: i32, + animations: &AnimationState, offset: (i32, i32), ) { - let location = ( + let mut location = ( offset.0 + player.position.0 * scale + 4, offset.1 + player.position.1 * scale + 4, ); + let animation_offset = animations.get_player_offset(board); + location.0 += (animation_offset.0 * scale as f32) as i32; + location.1 += (animation_offset.1 * scale as f32) as i32; renderer.render_segment( location, (scale - 8),