Skip to content

Commit

Permalink
✨ castles
Browse files Browse the repository at this point in the history
  • Loading branch information
fabienjuif committed Nov 5, 2023
1 parent 373811f commit bf5ae02
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 35 deletions.
101 changes: 101 additions & 0 deletions src/castles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use bevy::{
prelude::*,
sprite::{Sprite, SpriteBundle},
time::{Timer, TimerMode},
};
use bevy_rapier2d::prelude::*;

use crate::{
common::Rewards,
health::Health,
racks::Rack,
teams::{Team, Teams},
};

// TODO: Castle is a rack two...
// TODO: So maybe having a common "minion spawner" rather than "rack" is better?

#[derive(Bundle)]
pub struct CastleBundle {
pub sprite_bundle: SpriteBundle,
pub team: Team,
pub rack: Rack,
pub health: Health,
pub rewards: Rewards,
pub rigid_body: RigidBody,
pub collider: Collider,
pub events: ActiveEvents,
pub mass: ColliderMassProperties,
}

impl CastleBundle {
pub fn new(team: Team, transform: Transform) -> Self {
let size = Vec2::new(80.0, 80.0);
CastleBundle {
sprite_bundle: SpriteBundle {
sprite: Sprite {
color: team.color,
custom_size: Some(size),
..default()
},
transform,
..default()
},
team,
rack: Rack {
minion_spawning: false,
minion_spawned_count: 0,
minion_spawn_count: 5,
minion_spawn_timer: Timer::from_seconds(3., TimerMode::Repeating),
minion_spawn_timer_q: Timer::from_seconds(0.2, TimerMode::Repeating),
},
health: Health::new(300.)
.with_health_bar_position(Vec3::new(0.0, 50.0, 0.0))
.with_health_bar_size(Vec2::new(size.x, 5.)),
rewards: Rewards { gold: 500. },
rigid_body: RigidBody::Dynamic,
collider: Collider::cuboid(size.x / 2., size.y / 2.),
events: ActiveEvents::COLLISION_EVENTS,
mass: ColliderMassProperties::Mass(0.),
}
}
}

pub struct CastlesPlugin;

impl Plugin for CastlesPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup)
.add_systems(PostUpdate, destroy);
}
}

fn setup(mut commands: Commands, teams: Res<Teams>) {
commands.spawn(CastleBundle::new(
teams.get_expect("a".into()),
Transform::from_xyz(-200.0, -300.0, 0.),
));
commands.spawn(CastleBundle::new(
teams.get_expect("b".into()),
Transform::from_xyz(300.0, 300.0, 0.),
));
commands.spawn(CastleBundle::new(
teams.get_expect("c".into()),
Transform::from_xyz(200.0, -200.0, 0.),
));
}

// TODO: maybe this system can be retrieve from health bar crate (give the type and insert it in the filter?)
fn destroy(mut commands: Commands, mut query: Query<(&Health, Entity), With<Rack>>) {
let mut kill = |entity| {
trace!("Unspawning Minion: {:?}", entity);
commands.entity(entity).despawn_recursive();
};

for (health, entity) in &mut query {
// just not enough health
if health.is_dead() {
kill(entity);
}
}
}
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod castles;
mod common;
mod health;
mod minions;
Expand All @@ -11,6 +12,7 @@ use bevy::{
DefaultPlugins,
};
use bevy_rapier2d::prelude::*;
use castles::CastlesPlugin;
use health::HealthPlugin;
use minions::MinionsPlugin;
use player::LocalPlayerPlugin;
Expand All @@ -32,6 +34,7 @@ fn main() {
TeamsPlugin,
MinionsPlugin,
RacksPlugin,
CastlesPlugin,
HealthPlugin,
LocalPlayerPlugin,
))
Expand Down
50 changes: 27 additions & 23 deletions src/minions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use bevy::{
utils::{default, HashMap},
};
use bevy_rapier2d::prelude::*;
use rand::Rng;

const MINION_SCALE: f32 = 100.;
const DESTROY_MINIONS_AFTER_SECS: f32 = 120.;
Expand All @@ -30,46 +29,51 @@ impl Plugin for MinionsPlugin {
}
}

pub fn spawn_minion(commands: &mut Commands, transform: &Transform, team: Team) {
let mut rng = rand::thread_rng();
#[derive(Bundle)]
pub struct MinionBundle {
minion: Minion,
sprite: SpriteBundle,
health: Health,
rewards: Rewards,
team: Team,

// physics
body: RigidBody,
collider: Collider,
events: ActiveEvents,
}

let entity = commands
.spawn((
SpriteBundle {
impl MinionBundle {
pub fn new(translation: Vec3, team: Team) -> Self {
MinionBundle {
sprite: SpriteBundle {
sprite: Sprite {
color: team.color,
custom_size: Some(Vec2::new(10.0, 10.0)),
..default()
},
transform: Transform::from_xyz(
transform.translation.x + rng.gen_range(-40.0..40.0),
transform.translation.y + rng.gen_range(-40.0..40.0),
0.0,
),
transform: Transform::from_translation(translation),
..default()
},
RigidBody::Dynamic,
Collider::cuboid(5.0, 5.),
ActiveEvents::COLLISION_EVENTS,
// Restitution::coefficient(2.),
// Friction::coefficient(2.),
Minion {
minion: Minion {
// to avoid leaks
// maybe a better option on top of that is to leach health every seconds on minions and make them die!
destroy_timer: Timer::from_seconds(
DESTROY_MINIONS_AFTER_SECS,
bevy::time::TimerMode::Once,
),
},
Health::new(20.)
health: Health::new(20.)
.with_health_bar_position(Vec3::new(0.0, 15.0, 0.1))
.with_health_bar_size(Vec2::new(10.0, 5.0)),
Rewards { gold: REWARDS_GOLD },
rewards: Rewards { gold: REWARDS_GOLD },
team,
))
.id();

trace!("Spawning Minion: {:?}", entity);
// physics
body: RigidBody::Dynamic,
collider: Collider::cuboid(5.0, 5.),
events: ActiveEvents::COLLISION_EVENTS,
}
}
}

fn update_move_minions(
Expand Down
49 changes: 37 additions & 12 deletions src/racks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@ use bevy_rapier2d::prelude::*;
use crate::{
common::Rewards,
health::Health,
minions::MinionBundle,
teams::{Team, Teams},
};

use rand::Rng;

pub const RACK_GOLD_VALUE: f32 = 10.;

#[derive(Component)]
pub struct Rack {
minion_spawn_timer: Timer,
minion_spawn_timer_q: Timer,
minion_spawn_count: u32,
minion_spawned_count: u32,
minion_spawning: bool,
pub minion_spawn_timer: Timer,
pub minion_spawn_timer_q: Timer,
pub minion_spawn_count: u32,
pub minion_spawned_count: u32,
pub minion_spawning: bool,
}

#[derive(Bundle)]
Expand Down Expand Up @@ -96,9 +99,11 @@ fn setup(mut commands: Commands, teams: Res<Teams>) {
fn spawn_minions(
mut commands: Commands,
time: Res<Time>,
mut query: Query<(&mut Rack, &Transform, &Team)>,
mut query: Query<(&mut Rack, &Collider, &Transform, &Team)>,
) {
for (mut rack, transform, team) in &mut query {
let mut rng: rand::rngs::ThreadRng = rand::thread_rng();

for (mut rack, collider, transform, team) in &mut query {
// ticks timers
rack.minion_spawn_timer_q.tick(time.delta());
rack.minion_spawn_timer.tick(time.delta());
Expand All @@ -116,12 +121,32 @@ fn spawn_minions(
&& rack.minion_spawn_timer_q.just_finished()
&& rack.minion_spawned_count < rack.minion_spawn_count
{
crate::minions::spawn_minion(&mut commands, transform, team.clone());
rack.minion_spawned_count += 1;
// TODO: should RNG an angle instead
if let Some(cuboid) = collider.as_cuboid() {
let mut offset_x = cuboid.half_extents().x + rng.gen_range(2.0..10.0);
let mut offset_y = cuboid.half_extents().y + rng.gen_range(2.0..10.0);

if rng.gen_bool(0.5) {
offset_x *= -1.;
}
if rng.gen_bool(0.5) {
offset_y *= -1.;
}

commands.spawn(MinionBundle::new(
Vec3::new(
transform.translation.x + offset_x,
transform.translation.y + offset_y,
transform.translation.z,
),
team.clone(),
));
rack.minion_spawned_count += 1;

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

0 comments on commit bf5ae02

Please sign in to comment.