Skip to content

Commit bf5ae02

Browse files
committed
✨ castles
1 parent 373811f commit bf5ae02

File tree

4 files changed

+168
-35
lines changed

4 files changed

+168
-35
lines changed

src/castles.rs

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use bevy::{
2+
prelude::*,
3+
sprite::{Sprite, SpriteBundle},
4+
time::{Timer, TimerMode},
5+
};
6+
use bevy_rapier2d::prelude::*;
7+
8+
use crate::{
9+
common::Rewards,
10+
health::Health,
11+
racks::Rack,
12+
teams::{Team, Teams},
13+
};
14+
15+
// TODO: Castle is a rack two...
16+
// TODO: So maybe having a common "minion spawner" rather than "rack" is better?
17+
18+
#[derive(Bundle)]
19+
pub struct CastleBundle {
20+
pub sprite_bundle: SpriteBundle,
21+
pub team: Team,
22+
pub rack: Rack,
23+
pub health: Health,
24+
pub rewards: Rewards,
25+
pub rigid_body: RigidBody,
26+
pub collider: Collider,
27+
pub events: ActiveEvents,
28+
pub mass: ColliderMassProperties,
29+
}
30+
31+
impl CastleBundle {
32+
pub fn new(team: Team, transform: Transform) -> Self {
33+
let size = Vec2::new(80.0, 80.0);
34+
CastleBundle {
35+
sprite_bundle: SpriteBundle {
36+
sprite: Sprite {
37+
color: team.color,
38+
custom_size: Some(size),
39+
..default()
40+
},
41+
transform,
42+
..default()
43+
},
44+
team,
45+
rack: Rack {
46+
minion_spawning: false,
47+
minion_spawned_count: 0,
48+
minion_spawn_count: 5,
49+
minion_spawn_timer: Timer::from_seconds(3., TimerMode::Repeating),
50+
minion_spawn_timer_q: Timer::from_seconds(0.2, TimerMode::Repeating),
51+
},
52+
health: Health::new(300.)
53+
.with_health_bar_position(Vec3::new(0.0, 50.0, 0.0))
54+
.with_health_bar_size(Vec2::new(size.x, 5.)),
55+
rewards: Rewards { gold: 500. },
56+
rigid_body: RigidBody::Dynamic,
57+
collider: Collider::cuboid(size.x / 2., size.y / 2.),
58+
events: ActiveEvents::COLLISION_EVENTS,
59+
mass: ColliderMassProperties::Mass(0.),
60+
}
61+
}
62+
}
63+
64+
pub struct CastlesPlugin;
65+
66+
impl Plugin for CastlesPlugin {
67+
fn build(&self, app: &mut App) {
68+
app.add_systems(Startup, setup)
69+
.add_systems(PostUpdate, destroy);
70+
}
71+
}
72+
73+
fn setup(mut commands: Commands, teams: Res<Teams>) {
74+
commands.spawn(CastleBundle::new(
75+
teams.get_expect("a".into()),
76+
Transform::from_xyz(-200.0, -300.0, 0.),
77+
));
78+
commands.spawn(CastleBundle::new(
79+
teams.get_expect("b".into()),
80+
Transform::from_xyz(300.0, 300.0, 0.),
81+
));
82+
commands.spawn(CastleBundle::new(
83+
teams.get_expect("c".into()),
84+
Transform::from_xyz(200.0, -200.0, 0.),
85+
));
86+
}
87+
88+
// TODO: maybe this system can be retrieve from health bar crate (give the type and insert it in the filter?)
89+
fn destroy(mut commands: Commands, mut query: Query<(&Health, Entity), With<Rack>>) {
90+
let mut kill = |entity| {
91+
trace!("Unspawning Minion: {:?}", entity);
92+
commands.entity(entity).despawn_recursive();
93+
};
94+
95+
for (health, entity) in &mut query {
96+
// just not enough health
97+
if health.is_dead() {
98+
kill(entity);
99+
}
100+
}
101+
}

src/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod castles;
12
mod common;
23
mod health;
34
mod minions;
@@ -11,6 +12,7 @@ use bevy::{
1112
DefaultPlugins,
1213
};
1314
use bevy_rapier2d::prelude::*;
15+
use castles::CastlesPlugin;
1416
use health::HealthPlugin;
1517
use minions::MinionsPlugin;
1618
use player::LocalPlayerPlugin;
@@ -32,6 +34,7 @@ fn main() {
3234
TeamsPlugin,
3335
MinionsPlugin,
3436
RacksPlugin,
37+
CastlesPlugin,
3538
HealthPlugin,
3639
LocalPlayerPlugin,
3740
))

src/minions.rs

+27-23
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use bevy::{
66
utils::{default, HashMap},
77
};
88
use bevy_rapier2d::prelude::*;
9-
use rand::Rng;
109

1110
const MINION_SCALE: f32 = 100.;
1211
const DESTROY_MINIONS_AFTER_SECS: f32 = 120.;
@@ -30,46 +29,51 @@ impl Plugin for MinionsPlugin {
3029
}
3130
}
3231

33-
pub fn spawn_minion(commands: &mut Commands, transform: &Transform, team: Team) {
34-
let mut rng = rand::thread_rng();
32+
#[derive(Bundle)]
33+
pub struct MinionBundle {
34+
minion: Minion,
35+
sprite: SpriteBundle,
36+
health: Health,
37+
rewards: Rewards,
38+
team: Team,
39+
40+
// physics
41+
body: RigidBody,
42+
collider: Collider,
43+
events: ActiveEvents,
44+
}
3545

36-
let entity = commands
37-
.spawn((
38-
SpriteBundle {
46+
impl MinionBundle {
47+
pub fn new(translation: Vec3, team: Team) -> Self {
48+
MinionBundle {
49+
sprite: SpriteBundle {
3950
sprite: Sprite {
4051
color: team.color,
4152
custom_size: Some(Vec2::new(10.0, 10.0)),
4253
..default()
4354
},
44-
transform: Transform::from_xyz(
45-
transform.translation.x + rng.gen_range(-40.0..40.0),
46-
transform.translation.y + rng.gen_range(-40.0..40.0),
47-
0.0,
48-
),
55+
transform: Transform::from_translation(translation),
4956
..default()
5057
},
51-
RigidBody::Dynamic,
52-
Collider::cuboid(5.0, 5.),
53-
ActiveEvents::COLLISION_EVENTS,
54-
// Restitution::coefficient(2.),
55-
// Friction::coefficient(2.),
56-
Minion {
58+
minion: Minion {
5759
// to avoid leaks
5860
// maybe a better option on top of that is to leach health every seconds on minions and make them die!
5961
destroy_timer: Timer::from_seconds(
6062
DESTROY_MINIONS_AFTER_SECS,
6163
bevy::time::TimerMode::Once,
6264
),
6365
},
64-
Health::new(20.)
66+
health: Health::new(20.)
6567
.with_health_bar_position(Vec3::new(0.0, 15.0, 0.1))
6668
.with_health_bar_size(Vec2::new(10.0, 5.0)),
67-
Rewards { gold: REWARDS_GOLD },
69+
rewards: Rewards { gold: REWARDS_GOLD },
6870
team,
69-
))
70-
.id();
71-
72-
trace!("Spawning Minion: {:?}", entity);
71+
// physics
72+
body: RigidBody::Dynamic,
73+
collider: Collider::cuboid(5.0, 5.),
74+
events: ActiveEvents::COLLISION_EVENTS,
75+
}
76+
}
7377
}
7478

7579
fn update_move_minions(

src/racks.rs

+37-12
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@ use bevy_rapier2d::prelude::*;
88
use crate::{
99
common::Rewards,
1010
health::Health,
11+
minions::MinionBundle,
1112
teams::{Team, Teams},
1213
};
1314

15+
use rand::Rng;
16+
1417
pub const RACK_GOLD_VALUE: f32 = 10.;
1518

1619
#[derive(Component)]
1720
pub struct Rack {
18-
minion_spawn_timer: Timer,
19-
minion_spawn_timer_q: Timer,
20-
minion_spawn_count: u32,
21-
minion_spawned_count: u32,
22-
minion_spawning: bool,
21+
pub minion_spawn_timer: Timer,
22+
pub minion_spawn_timer_q: Timer,
23+
pub minion_spawn_count: u32,
24+
pub minion_spawned_count: u32,
25+
pub minion_spawning: bool,
2326
}
2427

2528
#[derive(Bundle)]
@@ -96,9 +99,11 @@ fn setup(mut commands: Commands, teams: Res<Teams>) {
9699
fn spawn_minions(
97100
mut commands: Commands,
98101
time: Res<Time>,
99-
mut query: Query<(&mut Rack, &Transform, &Team)>,
102+
mut query: Query<(&mut Rack, &Collider, &Transform, &Team)>,
100103
) {
101-
for (mut rack, transform, team) in &mut query {
104+
let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
105+
106+
for (mut rack, collider, transform, team) in &mut query {
102107
// ticks timers
103108
rack.minion_spawn_timer_q.tick(time.delta());
104109
rack.minion_spawn_timer.tick(time.delta());
@@ -116,12 +121,32 @@ fn spawn_minions(
116121
&& rack.minion_spawn_timer_q.just_finished()
117122
&& rack.minion_spawned_count < rack.minion_spawn_count
118123
{
119-
crate::minions::spawn_minion(&mut commands, transform, team.clone());
120-
rack.minion_spawned_count += 1;
124+
// TODO: should RNG an angle instead
125+
if let Some(cuboid) = collider.as_cuboid() {
126+
let mut offset_x = cuboid.half_extents().x + rng.gen_range(2.0..10.0);
127+
let mut offset_y = cuboid.half_extents().y + rng.gen_range(2.0..10.0);
128+
129+
if rng.gen_bool(0.5) {
130+
offset_x *= -1.;
131+
}
132+
if rng.gen_bool(0.5) {
133+
offset_y *= -1.;
134+
}
135+
136+
commands.spawn(MinionBundle::new(
137+
Vec3::new(
138+
transform.translation.x + offset_x,
139+
transform.translation.y + offset_y,
140+
transform.translation.z,
141+
),
142+
team.clone(),
143+
));
144+
rack.minion_spawned_count += 1;
121145

122-
if rack.minion_spawned_count >= rack.minion_spawn_count {
123-
debug!("[rack] every minions are spawned!");
124-
rack.minion_spawning = false;
146+
if rack.minion_spawned_count >= rack.minion_spawn_count {
147+
debug!("[rack] every minions are spawned!");
148+
rack.minion_spawning = false;
149+
}
125150
}
126151
}
127152
}

0 commit comments

Comments
 (0)