diff --git a/assets/img/character/diveNormal.aseprite b/assets/img/character/diveNormal.aseprite new file mode 100644 index 0000000..49cf5d8 Binary files /dev/null and b/assets/img/character/diveNormal.aseprite differ diff --git a/assets/img/character/diveNormal.json b/assets/img/character/diveNormal.json new file mode 100644 index 0000000..0687c6a --- /dev/null +++ b/assets/img/character/diveNormal.json @@ -0,0 +1,82 @@ +{ "frames": { + "Sprite-0005 0.": { + "frame": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 1.": { + "frame": { "x": 11, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 2.": { + "frame": { "x": 22, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 3.": { + "frame": { "x": 33, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 4.": { + "frame": { "x": 44, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 5.": { + "frame": { "x": 55, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 6.": { + "frame": { "x": 66, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "Sprite-0005 7.": { + "frame": { "x": 77, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "diveNormal.png", + "format": "RGBA8888", + "size": { "w": 88, "h": 21 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/character/diveNormal.png b/assets/img/character/diveNormal.png new file mode 100644 index 0000000..acf7ee8 Binary files /dev/null and b/assets/img/character/diveNormal.png differ diff --git a/assets/img/character/diveStroke.aseprite b/assets/img/character/diveStroke.aseprite new file mode 100644 index 0000000..c923093 Binary files /dev/null and b/assets/img/character/diveStroke.aseprite differ diff --git a/assets/img/character/diveStroke.json b/assets/img/character/diveStroke.json new file mode 100644 index 0000000..0717420 --- /dev/null +++ b/assets/img/character/diveStroke.json @@ -0,0 +1,186 @@ +{ "frames": { + "diveStroke 0.aseprite": { + "frame": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 1.aseprite": { + "frame": { "x": 17, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 2.aseprite": { + "frame": { "x": 34, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 3.aseprite": { + "frame": { "x": 51, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 4.aseprite": { + "frame": { "x": 68, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 5.aseprite": { + "frame": { "x": 85, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 6.aseprite": { + "frame": { "x": 102, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 7.aseprite": { + "frame": { "x": 119, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 8.aseprite": { + "frame": { "x": 136, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 9.aseprite": { + "frame": { "x": 153, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 10.aseprite": { + "frame": { "x": 170, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 11.aseprite": { + "frame": { "x": 187, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 12.aseprite": { + "frame": { "x": 204, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 13.aseprite": { + "frame": { "x": 221, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 14.aseprite": { + "frame": { "x": 238, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 15.aseprite": { + "frame": { "x": 255, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 16.aseprite": { + "frame": { "x": 272, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 17.aseprite": { + "frame": { "x": 289, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 18.aseprite": { + "frame": { "x": 306, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 19.aseprite": { + "frame": { "x": 323, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + }, + "diveStroke 20.aseprite": { + "frame": { "x": 340, "y": 0, "w": 17, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 17, "h": 21 }, + "sourceSize": { "w": 17, "h": 21 }, + "duration": 75 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "diveStroke.png", + "format": "RGBA8888", + "size": { "w": 357, "h": 21 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/character/diveStroke.png b/assets/img/character/diveStroke.png new file mode 100644 index 0000000..b9bcf67 Binary files /dev/null and b/assets/img/character/diveStroke.png differ diff --git a/assets/img/character/diveStrokeCharge.aseprite b/assets/img/character/diveStrokeCharge.aseprite new file mode 100644 index 0000000..49cf5d8 Binary files /dev/null and b/assets/img/character/diveStrokeCharge.aseprite differ diff --git a/assets/img/character/diveStrokeCharge.json b/assets/img/character/diveStrokeCharge.json new file mode 100644 index 0000000..383ff0f --- /dev/null +++ b/assets/img/character/diveStrokeCharge.json @@ -0,0 +1,50 @@ +{ "frames": { + "diveStroke 0.aseprite": { + "frame": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "diveStroke 1.aseprite": { + "frame": { "x": 11, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "diveStroke 2.aseprite": { + "frame": { "x": 22, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + }, + "diveStroke 3.aseprite": { + "frame": { "x": 33, "y": 0, "w": 11, "h": 21 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 11, "h": 21 }, + "sourceSize": { "w": 11, "h": 21 }, + "duration": 100 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "diveStrokeCharge.png", + "format": "RGBA8888", + "size": { "w": 44, "h": 21 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/character/diveStrokeCharge.png b/assets/img/character/diveStrokeCharge.png new file mode 100644 index 0000000..dbb8c77 Binary files /dev/null and b/assets/img/character/diveStrokeCharge.png differ diff --git a/assets/img/character/walk1.png b/assets/img/character/walk1.png new file mode 100644 index 0000000..1b338fe Binary files /dev/null and b/assets/img/character/walk1.png differ diff --git a/assets/img/tiles/tileset.png b/assets/img/tiles/tileset.png new file mode 100644 index 0000000..e48eea1 Binary files /dev/null and b/assets/img/tiles/tileset.png differ diff --git a/src/lib/wrappers/animation.rs b/src/lib/wrappers/animation.rs index 3632dee..053cf2b 100644 --- a/src/lib/wrappers/animation.rs +++ b/src/lib/wrappers/animation.rs @@ -1,9 +1,4 @@ -use raylib::{ - core::color::Color, - math::{Rectangle, Vector2}, - prelude::{RaylibDraw, RaylibDrawHandle}, - texture::Texture2D, -}; +use raylib::{core::color::Color, math::{Rectangle, Vector2}, prelude::{RaylibDraw, RaylibDrawHandle, RaylibMode2D}, texture::Texture2D}; /// A wrapper around an animation spritesheet pub struct FrameAnimationWrapper { @@ -29,7 +24,9 @@ impl FrameAnimationWrapper { /// Start the animation pub fn start(&mut self, handle: &RaylibDrawHandle) { - self.start_time_seconds = handle.get_time(); + if self.start_time_seconds == 0.0 { + self.start_time_seconds = handle.get_time(); + } } /// Stop (and reset) the animation @@ -48,16 +45,17 @@ impl FrameAnimationWrapper { } /// Draw the next frame to the screen at `position` - pub fn draw(&mut self, handle: &mut RaylibDrawHandle, position: Vector2) { + pub fn draw(&mut self, handle: &mut RaylibMode2D, position: Vector2, rotation: f32) { let frame_id = self.get_current_frame_id(handle); - self.draw_frame(handle, position, frame_id); + self.draw_frame(handle, position, rotation, frame_id); } /// Draw a specified frame to the screen at `position` pub fn draw_frame( &mut self, - handle: &mut RaylibDrawHandle, + handle: &mut RaylibMode2D, position: Vector2, + rotation: f32, frame_number: u32, ) { // Determine the col number @@ -75,8 +73,20 @@ impl FrameAnimationWrapper { width: self.size.x, height: self.size.y, }; + let frame_dest = Rectangle { + x: position.x, + y: position.y, + width: self.size.x, + height: self.size.y, + }; + + // Rotation origin + let origin = Vector2 { + x: self.size.x / 2.0, + y: self.size.y / 2.0 + }; // Render - handle.draw_texture_rec(&mut self.sprite_sheet, frame_box, position, Color::WHITE); + handle.draw_texture_pro(&mut self.sprite_sheet, frame_box, frame_dest, origin, rotation, Color::WHITE); } } diff --git a/src/lib/wrappers/complexanimation.rs b/src/lib/wrappers/complexanimation.rs new file mode 100644 index 0000000..ad87572 --- /dev/null +++ b/src/lib/wrappers/complexanimation.rs @@ -0,0 +1,32 @@ +use std::usize; + +use raylib::prelude::*; + +pub struct FrameRange { + pub min: usize, + pub max: usize, +} + +pub struct ComplexAnimationTool { + sprite_sheet: Texture2D, + frames_per_second: f32, + frame_size: Vector2, + sprite_sheet_size_frames: Vector2 +} + +impl ComplexAnimationTool { + pub fn render_loop(&self, context_2d: &mut RaylibMode2D, bounds: Rectangle, rotation: f32, range: &FrameRange) { + + } + + pub fn render_frame(&self, context_2d: &mut RaylibMode2D, bounds: Rectangle, rotation: f32, id: usize) { + + // Convert the ID to an xy + let col_id = id % self.sprite_sheet_size_frames.x as usize; + let row_id = id / self.sprite_sheet_size_frames.y as usize; + + + + + } +} diff --git a/src/lib/wrappers/mod.rs b/src/lib/wrappers/mod.rs index 9af9ea2..1a8f43a 100644 --- a/src/lib/wrappers/mod.rs +++ b/src/lib/wrappers/mod.rs @@ -1,2 +1,3 @@ pub mod audio; -pub mod animation; \ No newline at end of file +pub mod animation; +pub mod complexanimation; \ No newline at end of file diff --git a/src/logic/ingame/playerlogic.rs b/src/logic/ingame/playerlogic.rs index 0234ec0..1460984 100644 --- a/src/logic/ingame/playerlogic.rs +++ b/src/logic/ingame/playerlogic.rs @@ -9,7 +9,7 @@ const NORMAL_PLAYER_SPEED: i32 = 3; const BOOST_PLAYER_SPEED: i32 = NORMAL_PLAYER_SPEED * 2; const CAMERA_FOLLOW_SPEED: f32 = 0.7; const TURN_SPEED: f32 = 0.15; -const BOOST_DECREASE_PER_SECOND: f32 = 0.75; +const BOOST_DECREASE_PER_SECOND: f32 = 0.65; const BOOST_REGEN_PER_SECOND: f32 = 0.25; const BREATH_DECREASE_PER_SECOND: f32 = 0.01; @@ -84,10 +84,36 @@ pub fn update_player_movement( // Decrease the boost game_core.player.boost_percent -= BOOST_DECREASE_PER_SECOND * dt as f32; + game_core.player.is_boosting = true; + if game_core.player.boost_percent >= 0.9 { + game_core + .resources + .player_animation_boost_charge + .start(draw_handle); + game_core.resources.player_animation_regular.stop(); + game_core.player.is_boost_charging = true; + } else { + game_core.resources.player_animation_boost_charge.stop(); + game_core + .resources + .player_animation_boost + .start(draw_handle); + game_core.player.is_boost_charging = false; + } } else { // Set the speed multiplier speed_multiplier = NORMAL_PLAYER_SPEED as f32; + // Reset boost animation + game_core.player.is_boosting = false; + game_core.player.is_boost_charging = false; + game_core.resources.player_animation_boost_charge.stop(); + game_core.resources.player_animation_boost.stop(); + game_core + .resources + .player_animation_regular + .start(draw_handle); + // Handle boost regen if !user_request_boost { game_core.player.boost_percent = (game_core.player.boost_percent @@ -104,6 +130,9 @@ pub fn update_player_movement( let player_real_movement = game_core.player.direction * speed_multiplier; if raw_movement_direction.distance_to(Vector2::zero()) > game_core.player.size.y / 2.0 { game_core.player.position += player_real_movement; + game_core.player.is_moving = true; + } else { + game_core.player.is_moving = false; } // Move the camera to follow the player @@ -139,7 +168,7 @@ pub fn render_player(context_2d: &mut RaylibMode2D, game_core: x: player.position.x as i32 as f32, y: player.position.y as i32 as f32, }, - boost_ring_max_radius , + boost_ring_max_radius, boost_ring_max_radius + 1.0, 0, (360.0 * player.breath_percent) as i32, @@ -147,16 +176,31 @@ pub fn render_player(context_2d: &mut RaylibMode2D, game_core: TRANSLUCENT_WHITE_96, ); - // TODO: tmp rect - context_2d.draw_rectangle_pro( - Rectangle { - x: player.position.x, - y: player.position.y, - width: player.size.x, - height: player.size.y, - }, - player.size / 2.0, - player_rotation.to_degrees() + 90.0, - Color::BLACK, - ); + // Render the player based on what is happening + if player.is_boost_charging { + game_core.resources.player_animation_boost_charge.draw( + context_2d, + player.position, + player_rotation.to_degrees() - 90.0, + ); + } else if player.is_boosting { + game_core.resources.player_animation_boost.draw( + context_2d, + player.position, + player_rotation.to_degrees() - 90.0, + ); + } else if player.is_moving { + game_core.resources.player_animation_regular.draw( + context_2d, + player.position, + player_rotation.to_degrees() - 90.0, + ); + } else { + game_core.resources.player_animation_regular.draw_frame( + context_2d, + player.position, + player_rotation.to_degrees() - 90.0, + 0, + ); + } } diff --git a/src/player.rs b/src/player.rs index d37f539..95c34a2 100644 --- a/src/player.rs +++ b/src/player.rs @@ -9,7 +9,10 @@ pub struct Player { pub size: Vector2, pub coins: u32, pub boost_percent: f32, - pub breath_percent: f32 + pub breath_percent: f32, + pub is_moving: bool, + pub is_boosting: bool, + pub is_boost_charging: bool } impl Player { diff --git a/src/resources.rs b/src/resources.rs index 9ca9a7d..7397358 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -1,19 +1,61 @@ use failure::Error; -use raylib::{RaylibHandle, RaylibThread, texture::{Image, Texture2D}}; +use raylib::{ + math::Vector2, + texture::{Image, Texture2D}, + RaylibHandle, RaylibThread, +}; + +use crate::lib::wrappers::animation::FrameAnimationWrapper; /// This struct contains all textures and sounds that must be loaded into (V)RAM at the start of the game pub struct GlobalResources { - // Branding - pub game_logo: Texture2D + pub game_logo: Texture2D, + // Player + pub player_animation_regular: FrameAnimationWrapper, + pub player_animation_boost_charge: FrameAnimationWrapper, + pub player_animation_boost: FrameAnimationWrapper, } impl GlobalResources { /// Load all resources. **THIS WILL HANG!** - pub fn load_all(raylib: &mut RaylibHandle, thread: &RaylibThread) -> Result { + pub fn load_all( + raylib: &mut RaylibHandle, + thread: &RaylibThread, + ) -> Result { Ok(GlobalResources { - game_logo: raylib.load_texture_from_image(&thread, &Image::load_image("./assets/img/logos/game-logo.png")?)? + game_logo: raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/logos/game-logo.png")?, + )?, + player_animation_regular: FrameAnimationWrapper::new( + raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/character/diveNormal.png")?, + )?, + Vector2 { x: 11.0, y: 21.0 }, + 8, + 100 / 8, + ), + player_animation_boost_charge: FrameAnimationWrapper::new( + raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/character/diveStrokeCharge.png")?, + )?, + Vector2 { x: 11.0, y: 21.0 }, + 21, + 100 / 4, + ), + player_animation_boost: FrameAnimationWrapper::new( + raylib.load_texture_from_image( + &thread, + &Image::load_image("./assets/img/character/diveStroke.png")?, + )?, + Vector2 { x: 17.0, y: 21.0 }, + 21, + 30, + ), }) } }