Skip to content

Commit

Permalink
✨ minions damage players
Browse files Browse the repository at this point in the history
  • Loading branch information
fabienjuif committed Nov 1, 2023
1 parent c3981f0 commit 193da4a
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 22 deletions.
3 changes: 3 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ pub struct Team {
pub id: String,
pub color: Color,
}

#[derive(Component)]
pub struct Player;
62 changes: 49 additions & 13 deletions src/health_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,73 @@ use bevy::prelude::*;

#[derive(Component)]
pub struct Health {
pub health: f32,
pub value: f32,
pub max: f32,
}

impl Health {
pub fn new(max: f32) -> Self {
Self { value: max, max }
}
}

#[derive(Component)]
pub struct HealthBar {
pub entity: Entity,
pub translation: Vec3,
pub size: Vec2,
}

pub struct HealthBarPlugin;

impl Plugin for HealthBarPlugin {
fn build(&self, app: &mut App) {
app.add_systems(PostUpdate, update_health_bar_position);
app.add_systems(
PostUpdate,
(
update_health_bar_position,
update_health_bar_visual,
clear_orphans_healthbars,
),
);
}
}

fn update_health_bar_position(
fn clear_orphans_healthbars(
mut commands: Commands,
query_health: Query<&Health>,
mut query: Query<(&HealthBar, Entity)>,
) {
for (health_bar, entity) in &mut query {
if !query_health.contains(health_bar.entity) {
debug!("health bar alone, unspawning it");
commands.entity(entity).despawn_recursive();
}
}
}

fn update_health_bar_position(
query_health: Query<&GlobalTransform, With<Health>>,
mut query: Query<(&mut Transform, &HealthBar, Entity)>,
mut query: Query<(&mut Transform, &HealthBar)>,
) {
for (mut transform, health_bar, entity) in &mut query {
match query_health.get(health_bar.entity) {
Ok(parent_transform) => {
transform.translation =
parent_transform.to_scale_rotation_translation().2 + health_bar.translation;
}
Err(_) => {
debug!("health bar alone, unspawning it");
commands.entity(entity).despawn_recursive();
for (mut transform, health_bar) in &mut query {
if let Ok(parent_transform) = query_health.get(health_bar.entity) {
transform.translation =
parent_transform.to_scale_rotation_translation().2 + health_bar.translation;
}
}
}

fn update_health_bar_visual(
query_health: Query<&Health>,
mut query: Query<(&mut Sprite, &HealthBar)>,
) {
for (mut sprite, health_bar) in &mut query {
if let Ok(health) = query_health.get(health_bar.entity) {
if health.value >= 0. {
let mut size = health_bar.size;
size.x = health.value * health_bar.size.x / health.max;
sprite.custom_size = Some(size);
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ fn setup(mut commands: Commands) {
// RigidBody::KinematicVelocityBased,
RigidBody::Dynamic,
Collider::cuboid(25.0, 25.),
ActiveEvents::COLLISION_EVENTS,
LocalPlayer {},
Health { health: 100. },
Player {},
Health::new(100.),
Name("local_player".to_string()),
Team {
id: "a".to_string(),
Expand All @@ -109,6 +111,7 @@ fn setup(mut commands: Commands) {
.id();

commands.spawn((
// TODO: do a health bundle and move it to heath_bar crate
SpriteBundle {
sprite: Sprite {
color: DEFAULT_HEALTH_COLOR,
Expand All @@ -120,6 +123,7 @@ fn setup(mut commands: Commands) {
HealthBar {
entity,
translation: Vec3::new(0.0, 40.0, 0.1),
size: Vec2::new(50.0, 5.0), // TODO: once a bundle make sure this initialise the sprite
},
));
}
Expand Down
73 changes: 71 additions & 2 deletions src/minions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct Minion {

impl Plugin for MinionsPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, (update_move_minions, destroy_minions));
app.add_systems(Update, (update_move_minions, destroy_minions, hurt_player));
}
}

Expand All @@ -48,6 +48,7 @@ pub fn spawn_minion(commands: &mut Commands, transform: &Transform, team: Team)
},
RigidBody::Dynamic,
Collider::cuboid(5.0, 5.),
ActiveEvents::COLLISION_EVENTS,
// Restitution::coefficient(2.),
// Friction::coefficient(2.),
Minion {
Expand All @@ -58,12 +59,13 @@ pub fn spawn_minion(commands: &mut Commands, transform: &Transform, team: Team)
bevy::time::TimerMode::Once,
),
},
Health { health: 20. },
Health::new(20.),
team,
))
.id();

commands.spawn((
// TODO: do a health bundle and move it to heath_bar crate
SpriteBundle {
sprite: Sprite {
color: DEFAULT_HEALTH_COLOR,
Expand All @@ -75,6 +77,7 @@ pub fn spawn_minion(commands: &mut Commands, transform: &Transform, team: Team)
HealthBar {
entity,
translation: Vec3::new(0.0, 15.0, 0.1),
size: Vec2::new(10.0, 5.0), // TODO: once a bundle make sure this initialise the sprite
},
));

Expand Down Expand Up @@ -144,3 +147,69 @@ fn destroy_minions(
}
}
}

// maybe this is a bad idea to have a system per component since the collision event is having all contacts
// it makes us loop inside collision events multiple time
fn hurt_player(
mut query_players: Query<(Entity, &Team, &mut Health), With<Player>>,
query_minions: Query<(Entity, &Team), With<Minion>>,
mut collision_events: EventReader<CollisionEvent>,
// mut contact_force_events: EventReader<ContactForceEvent>,
) {
for collision_event in collision_events.iter() {
match collision_event {
CollisionEvent::Started(e1, e2, _) => {
let minion = match query_minions
.get(*e1)
.ok()
.or_else(|| query_minions.get(*e2).ok())
{
None => continue,
Some(p) => p,
};

// to avoid to borrow twice the query check with which entity the minion is resolved
// and take the other one to check if this is a player, this hacky way of doing it
// I am sure rust can resolve this better, here the first attempt in case someone
// want to explain me how to resolve it with Rust
//
// let player = match query_players
// .get_mut(*e1)
// .ok()
// .or_else(|| query_players.get_mut(*e2).ok())
// {
// None => continue,
// Some(p) => p,
// };
//
let player = if query_players.contains(*e1) {
query_players.get_mut(*e1).ok()
} else {
query_players.get_mut(*e2).ok()
};
let mut player = match player {
None => continue,
Some(p) => p,
};

// if they are from the same team, do nothing special
if player.1.id == minion.1.id {
continue;
}

// hurt the player
player.2.value -= 1.;
if player.2.value < 0. {
player.2.value = 0.
}

trace!(
"minion {} collision with the player {}",
minion.1.id,
player.1.id
)
}
CollisionEvent::Stopped(_, _, _) => {}
}
}
}
9 changes: 3 additions & 6 deletions src/racks.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use bevy::{
prelude::{
default, info, trace, App, Color, Commands, Component, Plugin, Query, Res, Startup,
Transform, Update, Vec2,
},
prelude::*,
sprite::{Sprite, SpriteBundle},
time::{Time, Timer, TimerMode},
};
Expand Down Expand Up @@ -50,7 +47,7 @@ fn spawn_minions(

// we are ready to start spawning
if rack.minion_spawn_timer.just_finished() {
info!("rack ready to spawn minions!");
debug!("[rack] ready to spawn minions!");
rack.minion_spawned_count = 0;
rack.minion_spawning = true;
}
Expand All @@ -65,7 +62,7 @@ fn spawn_minions(
rack.minion_spawned_count += 1;

if rack.minion_spawned_count >= rack.minion_spawn_count {
info!("every minions are spawned!");
debug!("[rack] every minions are spawned!");
rack.minion_spawning = false;
}
}
Expand Down

0 comments on commit 193da4a

Please sign in to comment.