diff --git a/assets/img/character/stunned.json b/assets/img/character/stunned.json new file mode 100644 index 0000000..5d29dfc --- /dev/null +++ b/assets/img/character/stunned.json @@ -0,0 +1,50 @@ +{ "frames": { + "walk1 0.png": { + "frame": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + }, + "walk1 1.png": { + "frame": { "x": 12, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + }, + "walk1 2.png": { + "frame": { "x": 24, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + }, + "walk1 3.png": { + "frame": { "x": 36, "y": 0, "w": 12, "h": 22 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 12, "h": 22 }, + "sourceSize": { "w": 12, "h": 22 }, + "duration": 200 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "stunned.png", + "format": "RGBA8888", + "size": { "w": 48, "h": 22 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/character/stunned.png b/assets/img/character/stunned.png new file mode 100644 index 0000000..986e8f0 Binary files /dev/null and b/assets/img/character/stunned.png differ diff --git a/assets/img/enemies/jelly.aseprite b/assets/img/enemies/jelly.aseprite new file mode 100644 index 0000000..ef41716 Binary files /dev/null and b/assets/img/enemies/jelly.aseprite differ diff --git a/assets/img/enemies/jelly.json b/assets/img/enemies/jelly.json new file mode 100644 index 0000000..2f02023 --- /dev/null +++ b/assets/img/enemies/jelly.json @@ -0,0 +1,66 @@ +{ "frames": { + "Sprite-0001 0.": { + "frame": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 1.": { + "frame": { "x": 10, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 2.": { + "frame": { "x": 20, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 3.": { + "frame": { "x": 30, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 4.": { + "frame": { "x": 40, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + }, + "Sprite-0001 5.": { + "frame": { "x": 50, "y": 0, "w": 10, "h": 10 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 10, "h": 10 }, + "sourceSize": { "w": 10, "h": 10 }, + "duration": 300 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "jelly.png", + "format": "RGBA8888", + "size": { "w": 60, "h": 10 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer 1", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/enemies/jelly.png b/assets/img/enemies/jelly.png new file mode 100644 index 0000000..8e670f2 Binary files /dev/null and b/assets/img/enemies/jelly.png differ diff --git a/assets/img/enemies/jellyAttack.json b/assets/img/enemies/jellyAttack.json new file mode 100644 index 0000000..3e97d87 --- /dev/null +++ b/assets/img/enemies/jellyAttack.json @@ -0,0 +1,138 @@ +{ "frames": { + "jelly 0.aseprite": { + "frame": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 1.aseprite": { + "frame": { "x": 20, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 2.aseprite": { + "frame": { "x": 40, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 3.aseprite": { + "frame": { "x": 60, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 4.aseprite": { + "frame": { "x": 80, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 5.aseprite": { + "frame": { "x": 100, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 6.aseprite": { + "frame": { "x": 120, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 7.aseprite": { + "frame": { "x": 140, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 8.aseprite": { + "frame": { "x": 160, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 9.aseprite": { + "frame": { "x": 180, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 10.aseprite": { + "frame": { "x": 200, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 11.aseprite": { + "frame": { "x": 220, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 12.aseprite": { + "frame": { "x": 240, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 13.aseprite": { + "frame": { "x": 260, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + }, + "jelly 14.aseprite": { + "frame": { "x": 280, "y": 0, "w": 20, "h": 20 }, + "rotated": false, + "trimmed": false, + "spriteSourceSize": { "x": 0, "y": 0, "w": 20, "h": 20 }, + "sourceSize": { "w": 20, "h": 20 }, + "duration": 200 + } + }, + "meta": { + "app": "http://www.aseprite.org/", + "version": "1.2.27-x64", + "image": "jellyAttack.png", + "format": "RGBA8888", + "size": { "w": 300, "h": 20 }, + "scale": "1", + "frameTags": [ + ], + "layers": [ + { "name": "Layer 3", "opacity": 255, "blendMode": "normal" } + ], + "slices": [ + ] + } +} diff --git a/assets/img/enemies/jellyAttack.png b/assets/img/enemies/jellyAttack.png new file mode 100644 index 0000000..2fd06a8 Binary files /dev/null and b/assets/img/enemies/jellyAttack.png differ diff --git a/src/entities/enemy/jellyfish.rs b/src/entities/enemy/jellyfish.rs new file mode 100644 index 0000000..4ddad72 --- /dev/null +++ b/src/entities/enemy/jellyfish.rs @@ -0,0 +1,92 @@ +use super::base::EnemyBase; +use crate::{ + lib::utils::calculate_linear_slide, pallette::TRANSLUCENT_RED_64, player::Player, + resources::GlobalResources, +}; +use raylib::prelude::*; +use serde::{Deserialize, Serialize}; + +const JELLYFISH_STUN_DURATION: f64 = 0.75; +const JELLYFISH_STUN_REACH: f32 = 20.0; + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct JellyFish { + pub position: Vector2, + + #[serde(skip)] + pub stunned_timer: f64, + #[serde(skip)] + pub max_stunned_time: f64, + + #[serde(skip)] + pub do_stun_player: bool, +} + +impl JellyFish {} + +impl EnemyBase for JellyFish { + fn render( + &mut self, + context_2d: &mut raylib::prelude::RaylibMode2D, + resources: &mut GlobalResources, + dt: f64, + ) { + let is_jelly_stunned = self.stunned_timer > 0.0; + + // Simple sine position + let v_trans = if is_jelly_stunned { + 0.0 + } else { + context_2d.get_time().sin() + }; + let trans_pose = Vector2 { + x: self.position.x, + y: self.position.y + (2.0 * v_trans as f32), + }; + + // Render the stun ring + if self.max_stunned_time > 0.0 && self.stunned_timer > 0.0 { + let stun_ring_alpha = + calculate_linear_slide(self.stunned_timer / self.max_stunned_time); + context_2d.draw_circle_v( + trans_pose, + JELLYFISH_STUN_REACH, + TRANSLUCENT_RED_64.fade(0.55 * stun_ring_alpha as f32), + ); + self.stunned_timer -= dt; + } + + // Render the jellyfish + resources + .jellyfish_animation_regular + .draw(context_2d, trans_pose, 0.0); + + // Only do stun loop if not stunned + if !is_jelly_stunned { + resources + .jellyfish_animation_attack + .draw(context_2d, trans_pose, 0.0); + } + + // Check if the jelly is in stun mode + self.do_stun_player = (resources + .jellyfish_animation_attack + .get_current_frame_id(context_2d) + == 13) + && !is_jelly_stunned; + } + + fn handle_logic(&mut self, player: &mut Player, dt: f64) { + // Handle stunning the player + if self.do_stun_player { + if self.position.distance_to(player.position).abs() <= JELLYFISH_STUN_REACH { + player.set_stun_seconds(JELLYFISH_STUN_DURATION); + } + } + } + + fn handle_getting_attacked(&mut self, stun_duration: f64) { + self.stunned_timer = stun_duration; + self.max_stunned_time = stun_duration; + } +} diff --git a/src/items.rs b/src/items.rs index d2eb2dc..a8c206d 100644 --- a/src/items.rs +++ b/src/items.rs @@ -2,84 +2,45 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct StunGun { - pub level: u32, - pub cost: u32, pub range: f32, pub duration: f64, - pub name: String, } impl StunGun { - pub fn level_up(new_level: u32) -> Self { + pub fn lvl1() -> Self { Self { - level: new_level, - range: (new_level * 30) as f32, - duration: new_level as f64 * 0.25, - cost: 5 * new_level, - name: String::from("Stun Gun"), + range: 30.0, + duration: 0.5, + } + } + pub fn lvl2() -> Self { + Self { + range: 60.0, + duration: 0.75, } } } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub struct AirBag { - pub level: u32, - pub cost: u32, - pub additonal_oxegen: u32, - pub name: String, -} - -impl AirBag { - pub fn level_up(new_level: u32) -> Self { - Self { - level: new_level, - cost: new_level * 5, - additonal_oxegen: 15 * new_level, - name: String::from("Air Bag"), - } - } -} +pub struct AirBag; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -pub struct Flashlight { - pub level: u32, - pub cost: u32, - pub brightness: u32, - pub battery_life: u32, - pub name: String, -} - -impl Flashlight { - pub fn level_up(new_level: u32) -> Self { - Self { - level: new_level, - cost: new_level * 5, - brightness: (0.5 * new_level as f64) as u32, - battery_life: 5 * new_level, - name: String::from("Flash Light"), - } - } -} +pub struct Flashlight; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Flippers { - pub level: u32, - pub cost: u32, pub speed_increase: f32, - pub name: String, } impl Flippers { - pub fn level_up(new_level: u32) -> Self { + pub fn lvl1() -> Self { Self { - level: new_level, - cost: new_level * 5, - speed_increase: (if new_level > 1 { - 1.2 + (new_level as f32) * 0.3 - } else { - 1.2 * new_level as f32 - }), - name: String::from("Flippers"), + speed_increase: 1.2 + } + } + pub fn lvl2() -> Self { + Self { + speed_increase: 1.5 } } } @@ -90,43 +51,58 @@ pub enum ShopItems { StunGun(u8, u8, String), AirBag(u8, u8, String), Flashlight(u8, u8, String), - Flippers(u8, u8, String), + Flippers(u8, u8, String) } -impl ShopItems { - pub fn get_inital_items() -> [ShopItems; 4] { - [ - ShopItems::StunGun(1, 10, String::from("Stun Gun")), - ShopItems::AirBag(1, 10, String::from("Air Bag")), - ShopItems::Flashlight(1, 12, String::from("Flash Light")), - ShopItems::Flippers(1, 10, String::from("Flippers")), - ] - } +impl ShopItems{ - pub fn get_level(item: &ShopItems) -> u8 { - match item { - ShopItems::StunGun(x, _, _) => *x, - ShopItems::AirBag(x, _, _) => *x, - ShopItems::Flashlight(x, _, _) => *x, - ShopItems::Flippers(x, _, _) => *x, - } - } + pub fn get_inital_items() -> [ShopItems; 4]{ + + [ShopItems::StunGun(1, 10, String::from("Stun Gun")), ShopItems::AirBag(1, 10, String::from("Air Bag")), + ShopItems::Flashlight(1, 12, String::from("Flash Light")), ShopItems::Flippers(1, 10, String::from("Flippers"))] + + + } + + pub fn get_level(item: &ShopItems) -> u8{ + + + match item { + ShopItems::StunGun(x, _, _) => *x, + ShopItems::AirBag(x, _, _) => *x, + ShopItems::Flashlight(x, _, _) => *x, + ShopItems::Flippers(x, _, _) => *x + } + + } + + + pub fn get_cost(item: &ShopItems) -> u8{ + + match item { + ShopItems::StunGun(x, _, _) => *x, + ShopItems::AirBag(x, _, _) => *x, + ShopItems::Flashlight(x, _, _) => *x, + ShopItems::Flippers(x, _, _) => *x + } + + } + + pub fn get_name(item: &ShopItems) -> String{ + + match item { + ShopItems::StunGun(_, _, x) => x.to_string(), + ShopItems::AirBag(_, _, x) => x.to_string(), + ShopItems::Flashlight(_, _, x) => x.to_string(), + ShopItems::Flippers(_, _, x) => x.to_string() + } + + } - pub fn get_cost(item: &ShopItems) -> u8 { - match item { - ShopItems::StunGun(x, _, _) => *x, - ShopItems::AirBag(x, _, _) => *x, - ShopItems::Flashlight(x, _, _) => *x, - ShopItems::Flippers(x, _, _) => *x, - } - } - pub fn get_name(item: &ShopItems) -> String { - match item { - ShopItems::StunGun(_, _, x) => x.to_string(), - ShopItems::AirBag(_, _, x) => x.to_string(), - ShopItems::Flashlight(_, _, x) => x.to_string(), - ShopItems::Flippers(_, _, x) => x.to_string(), - } - } } + + + + +