Skip to content

Commit

Permalink
Fire breath ability (#69)
Browse files Browse the repository at this point in the history
Issue:
==============
Closes #36 

What was done:
==============
* Added Bevy Particles System 
* Added SpawnFireBreathEvent
* Added fire particle to spawn when the key is pressed
* Added collisions between fire and enemies
  • Loading branch information
cnmorales authored Nov 18, 2023
1 parent bc161c9 commit 9e3475b
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 13 deletions.
64 changes: 57 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ bevy = { version = "0.12.0", default-features = false, features = [
"vorbis",
"x11",
] }

bevy_rapier2d = { version = "0.23.0", features = ["debug-render-2d"] }
bevy_particle_systems = "0.11.0"
noise = "0.8.2"
rand = "0.7.3"

Expand Down
Binary file added assets/textures/fire_breath.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 31 additions & 2 deletions src/game/combat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use bevy_rapier2d::prelude::*;

use crate::playing;

use super::{Hitpoints, Player, HALF_TILE_SIZE};
use super::{
player::{Damage, Fire},
Enemy, Hitpoints, Player, HALF_TILE_SIZE,
};

pub(super) struct CombatPlugin;

Expand All @@ -13,7 +16,12 @@ impl Plugin for CombatPlugin {

app.add_systems(
FixedUpdate,
(projectile_collision_with_player, spawn_projectiles).run_if(playing()),
(
projectile_collision_with_player,
spawn_projectiles,
compute_damage_from_intersections,
)
.run_if(playing()),
);
}
}
Expand Down Expand Up @@ -137,3 +145,24 @@ fn projectile_collision_with_player(
}
}
}

fn compute_damage_from_intersections(
mut commands: Commands,
fire_query: Query<(Entity, &Damage), With<Fire>>,
mut enemy_query: Query<(Entity, &mut Hitpoints), With<Enemy>>,
rapier_context: Res<RapierContext>,
) {
for (entity, damage) in &fire_query {
/* Iterate through all the intersection pairs involving a specific collider. */
for (entity1, entity2, intersecting) in rapier_context.intersections_with(entity) {
let other_entity = if entity1 == entity { entity2 } else { entity1 };

if intersecting {
if let Ok((enemy_entity, mut enemy_hitpoints)) = enemy_query.get_mut(other_entity) {
enemy_hitpoints.subtract(damage.0);
commands.entity(enemy_entity).despawn_recursive();
}
}
}
}
}
9 changes: 8 additions & 1 deletion src/game/enemy.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use rand::seq::IteratorRandom;

use crate::{physics::Speed, playing};

use super::{
combat::{AttackDamage, AttackTimer, Range, SpawnProjectileEvent},
BorderTile, Hitpoints, Player, TILE_SIZE,
BorderTile, Hitpoints, Player, HALF_TILE_SIZE, TILE_SIZE,
};

pub(super) struct EnemyPlugin;
Expand All @@ -31,6 +32,9 @@ pub struct EnemyBundle {
pub range: Range,
pub speed: Speed,
pub sprite: SpriteBundle,
pub collider: Collider,
pub rigid_body: RigidBody,
pub collision_groups: CollisionGroups,
}

#[derive(Component)]
Expand Down Expand Up @@ -80,6 +84,9 @@ fn spawn_enemies(
transform: Transform::from_translation(translation),
..default()
},
collider: Collider::cuboid(HALF_TILE_SIZE.x, HALF_TILE_SIZE.y),
rigid_body: RigidBody::Dynamic,
collision_groups: CollisionGroups::new(Group::GROUP_2, Group::GROUP_2),
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ pub use constants::*;
pub use enemy::Enemy;
pub use hitpoints::Hitpoints;
pub use level::{BorderTile, Tile};
pub use player::Player;
pub use player::{Player, SpawnFireBreathEvent};
pub use plugin::GamePlugin;
75 changes: 74 additions & 1 deletion src/game/player.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bevy::prelude::*;
use bevy_rapier2d::prelude::Collider;
use bevy_particle_systems::*;
use bevy_rapier2d::prelude::*;

use crate::{
animation::{AnimationIndices, AnimationTimer},
Expand All @@ -12,6 +13,9 @@ pub(super) struct PlayerPlugin;

impl Plugin for PlayerPlugin {
fn build(&self, app: &mut App) {
app.add_event::<SpawnFireBreathEvent>();
app.add_plugins(ParticleSystemPlugin::default());
app.add_systems(Update, spawn_fire_breath);
app.add_systems(OnEnter(AppState::InGame), spawn_player);
}
}
Expand All @@ -21,14 +25,42 @@ pub struct PlayerBundle {
pub animation_indices: AnimationIndices,
pub animation_timer: AnimationTimer,
pub collider: Collider,
pub collision_groups: CollisionGroups,
pub hitpoints: Hitpoints,
pub marker: Player,
pub spritesheet: SpriteSheetBundle,
}

#[derive(Component)]
pub struct Fire;

#[derive(Bundle)]
pub struct FireBreathBundle {
pub particle_system: ParticleSystemBundle,
pub damage: Damage,
pub sensor: Sensor,
pub collider: Collider,
pub marker: Fire,
}

#[derive(Component)]
pub struct Damage(pub i16);

#[derive(Component)]
pub struct Player;

#[derive(Event)]
pub struct SpawnFireBreathEvent {
damage: i16,
position: Vec2,
}

impl SpawnFireBreathEvent {
pub fn new(damage: i16, position: Vec2) -> Self {
Self { damage, position }
}
}

fn spawn_player(mut commands: Commands, asset_server: Res<AssetServer>) {
let texture = asset_server
.get_handle("textures/dragon.png")
Expand All @@ -40,6 +72,7 @@ fn spawn_player(mut commands: Commands, asset_server: Res<AssetServer>) {
animation_indices: AnimationIndices::new(0, 2),
animation_timer: AnimationTimer::from_seconds(0.2),
collider: Collider::ball(80.5),
collision_groups: CollisionGroups::new(Group::GROUP_1, Group::GROUP_1),
hitpoints: Hitpoints::new(100),
marker: Player,
spritesheet: SpriteSheetBundle {
Expand All @@ -50,3 +83,43 @@ fn spawn_player(mut commands: Commands, asset_server: Res<AssetServer>) {
},
});
}

fn spawn_fire_breath(
mut commands: Commands,
mut spawn_fire_breath_event_reader: EventReader<SpawnFireBreathEvent>,
asset_server: Res<AssetServer>,
) {
for &SpawnFireBreathEvent { damage, position } in spawn_fire_breath_event_reader.read() {
commands
.spawn(FireBreathBundle {
marker: Fire,
particle_system: ParticleSystemBundle {
transform: Transform::from_translation(position.extend(1.0)),
particle_system: ParticleSystem {
z_value_override: Some(JitteredValue::new(0.9)), // temporary value 0.9 (under dragon), if set to 2, the fire is above
max_particles: 10_000,
texture: ParticleTexture::Sprite(
asset_server.load("textures/fire_breath.png"),
),
spawn_rate_per_second: 10.0.into(),
initial_speed: JitteredValue::jittered(3.0, -1.0..1.0),
lifetime: JitteredValue::jittered(4.0, -1.0..1.0),
/* color: ColorOverTime::Gradient(Gradient::new(vec![
ColorPoint::new(Color::WHITE, 0.0),
ColorPoint::new(Color::rgba(0.0, 0.0, 1.0, 0.0), 1.0),
])), */
looping: false,
despawn_on_finish: true,
system_duration_seconds: 1.0,
..ParticleSystem::default()
},
..ParticleSystemBundle::default()
},
sensor: Sensor,
collider: Collider::ball(25.0),
damage: Damage(damage),
})
// Add the playing component so it starts playing. This can be added later as well.
.insert(Playing);
}
}
18 changes: 17 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::f32::consts::FRAC_PI_2;

use bevy::{ecs::system::SystemParam, prelude::*, window::PrimaryWindow};

use crate::{game::Player, playing};
use crate::{game::Player, game::SpawnFireBreathEvent, playing};

pub struct InputPlugin;

Expand Down Expand Up @@ -35,6 +35,7 @@ impl CursorWorldPositionChecker<'_, '_> {
fn mouse_input(
mouse_input: ResMut<Input<MouseButton>>,
cursor_world_position_checker: CursorWorldPositionChecker,
mut spawn_fire_breath_event_writer: EventWriter<SpawnFireBreathEvent>,
mut query: Query<&mut Transform, With<Player>>,
) {
if mouse_input.pressed(MouseButton::Right) {
Expand Down Expand Up @@ -63,4 +64,19 @@ fn mouse_input(
}
}
}
if mouse_input.pressed(MouseButton::Left) {
let player_transform = query.single();

let player_direction = player_transform.rotation.mul_vec3(Vec3::Y).truncate(); // already normalized

let mut fire_transform = player_transform.clone();

fire_transform.translation.x += player_direction.x * 90.; // TODO replace constant with sprite dimensions
fire_transform.translation.y += player_direction.y * 90.;

spawn_fire_breath_event_writer.send(SpawnFireBreathEvent::new(
1000,
fire_transform.translation.truncate(),
));
}
}

0 comments on commit 9e3475b

Please sign in to comment.