Skip to content

Commit a6d8b4a

Browse files
committed
Small fixes
1 parent f93ae0f commit a6d8b4a

File tree

8 files changed

+181
-74
lines changed

8 files changed

+181
-74
lines changed

src/enemy.rs

+36-27
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ use crate::{
33
particle::ParticleEmitter, phase::Phase, physics::*, player::Player, random, ship::Ships,
44
sprite::Sprites,
55
};
6-
use specs_blit::{specs::*, Sprite, SpriteRef};
6+
use derive_deref::{Deref, DerefMut};
7+
use specs_blit::{specs::*, Sprite};
78

89
const ENEMY_VELOCITY_RESOURCES_MIN_FACTOR_X: f64 = 0.1;
910
const ENEMY_VELOCITY_RESOURCES_MAX_FACTOR_X: f64 = 0.9;
1011
const ENEMY_VELOCITY_RESOURCES_MIN_FACTOR_Y: f64 = 0.02;
1112
const ENEMY_VELOCITY_RESOURCES_MAX_FACTOR_Y: f64 = 0.5;
12-
const ENEMY_VELOCITY_RESOURCES_SPEED_X: f64 = 0.1;
13+
const ENEMY_VELOCITY_RESOURCES_SPEED_X: f64 = 0.05;
1314
const ENEMY_VELOCITY_RESOURCES_SPEED_Y: f64 = 0.05;
14-
const ENEMY_VELOCITY_MIN_SPEED: f64 = 1.0;
15+
const ENEMY_VELOCITY_MIN_SPEED: f64 = 2.0;
1516
const ENEMY_ZIGZAG_RESOURCES_MIN_FACTOR: f64 = 0.1;
1617
const ENEMY_ZIGZAG_RESOURCES_MAX_FACTOR: f64 = 0.9;
17-
const ENEMY_ZIGZAG_RESOURCES_SPEED: f64 = 0.01;
18+
const ENEMY_ZIGZAG_RESOURCES_SPEED: f64 = 0.1;
1819

1920
const ENEMY_ENGINE_PARTICLE_LIFETIME: f64 = 10.0;
2021
const ENEMY_DEAD_EMITTER_LIFETIME: f64 = 5.0;
@@ -32,19 +33,11 @@ pub enum EnemyType {
3233
}
3334

3435
impl EnemyType {
35-
pub fn sprite(self, ships: &Ships) -> SpriteRef {
36-
match self {
37-
EnemyType::Small => ships.enemy_small.clone(),
38-
EnemyType::Medium => ships.enemy_medium.clone(),
39-
EnemyType::Big => ships.enemy_big.clone(),
40-
}
41-
}
42-
4336
pub fn bb(self) -> BoundingBox {
4437
match self {
4538
EnemyType::Small => BoundingBox::new(10.0, 16.0),
4639
EnemyType::Medium => BoundingBox::new(13.0, 20.0),
47-
_ => unimplemented!(),
40+
EnemyType::Big => BoundingBox::new(22.0, 24.0),
4841
}
4942
}
5043
}
@@ -86,7 +79,7 @@ impl EnemyEmitter {
8679
.collect::<Vec<_>>();
8780

8881
// Always spawn the first one immediately
89-
spawner[0].0 = 5.0;
82+
spawner[0].0 = 30.0;
9083

9184
Self {
9285
spawner,
@@ -105,18 +98,16 @@ impl EnemyEmitter {
10598
let enemy = entities.create();
10699
updater.insert(enemy, Enemy);
107100

108-
let type_ = if resources < 50.0 {
109-
EnemyType::Small
110-
} else if resources < 100.0 {
111-
resources -= 50.0;
101+
let type_ = if resources > 100.0 && random::bool() {
102+
resources -= 40.0;
103+
EnemyType::Big
104+
} else if resources > 50.0 && random::bool() {
105+
resources -= 20.0;
112106
EnemyType::Medium
113107
} else {
114-
resources -= 100.0;
115-
EnemyType::Big
108+
EnemyType::Small
116109
};
117110

118-
updater.insert(enemy, Sprite::new(type_.sprite(ships)));
119-
120111
updater.insert(
121112
enemy,
122113
Position::new(
@@ -129,9 +120,10 @@ impl EnemyEmitter {
129120
updater.insert(
130121
enemy,
131122
ParticleEmitter::new(
132-
ENEMY_ENGINE_PARTICLE_LIFETIME,
123+
ENEMY_ENGINE_PARTICLE_LIFETIME + resources / 100.0,
133124
sprites.white_particle.clone(),
134125
)
126+
.with_dispersion(1.0 - resources / 200.0)
135127
.with_offset(bb.to_aabr(&Position::new(0.0, 0.0)).center()),
136128
);
137129

@@ -143,6 +135,8 @@ impl EnemyEmitter {
143135
) * resources;
144136
resources -= x_velocity_resources;
145137

138+
updater.insert(enemy, Sprite::new(ships.enemy(type_)));
139+
146140
if random::bool() {
147141
// Straight pattern
148142
let y_velocity_resources = random::range(
@@ -185,23 +179,31 @@ impl EnemyEmitter {
185179
);
186180
}
187181
}
182+
183+
pub fn enemies_left(&self) -> usize {
184+
self.spawner.len()
185+
}
188186
}
189187

190188
#[derive(Component, Debug, Default)]
191189
#[storage(NullStorage)]
192190
pub struct Enemy;
193191

192+
#[derive(Debug, Default, Deref, DerefMut)]
193+
pub struct EnemiesLeft(pub usize);
194+
194195
pub struct EnemySystem;
195196
impl<'a> System<'a> for EnemySystem {
196197
type SystemData = (
197198
Entities<'a>,
199+
Write<'a, Phase>,
198200
Option<Write<'a, Lives>>,
199201
ReadStorage<'a, Enemy>,
200202
ReadStorage<'a, Position>,
201203
Read<'a, LazyUpdate>,
202204
);
203205

204-
fn run(&mut self, (entities, lives, enemy, pos, updater): Self::SystemData) {
206+
fn run(&mut self, (entities, mut phase, lives, enemy, pos, updater): Self::SystemData) {
205207
if let Some(mut lives) = lives {
206208
for (entity, pos, _) in (&*entities, &pos, &enemy).join() {
207209
if pos.0.x <= 0.0 {
@@ -215,6 +217,10 @@ impl<'a> System<'a> for EnemySystem {
215217
}
216218
}
217219
}
220+
221+
if *phase == Phase::WaitingForLastEnemy && enemy.is_empty() {
222+
*phase = Phase::SwitchTo(Box::new(Phase::Setup));
223+
}
218224
}
219225
}
220226

@@ -265,19 +271,22 @@ impl<'a> System<'a> for EnemyEmitterSystem {
265271
ReadExpect<'a, Sprites>,
266272
Option<Read<'a, Ships>>,
267273
Write<'a, Phase>,
274+
Write<'a, EnemiesLeft>,
268275
WriteStorage<'a, EnemyEmitter>,
269276
Read<'a, LazyUpdate>,
270277
);
271278

272279
fn run(
273280
&mut self,
274-
(entities, sprites, ships, mut phase, mut emitter, updater): Self::SystemData,
281+
(entities, sprites, ships, mut phase, mut enemies_left, mut emitter, updater): Self::SystemData,
275282
) {
276283
if let Some(ships) = ships {
277-
for emitter in (&mut emitter).join() {
284+
for (entity, emitter) in (&*entities, &mut emitter).join() {
285+
enemies_left.0 = emitter.enemies_left();
278286
if emitter.current_time >= emitter.total_time {
279287
// Time ran out, change to another phase
280-
*phase = Phase::SwitchTo(Box::new(Phase::Setup));
288+
*phase = Phase::WaitingForLastEnemy;
289+
let _ = entities.delete(entity);
281290
break;
282291
}
283292
emitter.current_time += 1.0;

src/lives.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ impl Lives {
4545
}
4646

4747
pub fn reduce(&mut self) {
48-
self.amount -= 1;
48+
if self.amount > 0 {
49+
self.amount -= 1;
50+
}
4951
}
5052

5153
pub fn is_dead(&self) -> bool {

src/main.rs

+49-25
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ mod render;
1515
mod ship;
1616
mod sprite;
1717

18-
use crate::{gui::Gui, input::Input, lives::Lives, phase::Phase, render::Render};
18+
use crate::{
19+
enemy::EnemiesLeft, gui::Gui, input::Input, lives::Lives, phase::Phase, render::Render,
20+
};
1921
use anyhow::Result;
2022
use miniquad::{
2123
conf::{Conf, Loading},
@@ -26,6 +28,9 @@ use specs_blit::{specs::prelude::*, PixelBuffer};
2628
pub const WIDTH: usize = 400;
2729
pub const HEIGHT: usize = 300;
2830

31+
const LEVEL_TIME_SCALE: f64 = 120.0;
32+
const LEVEL_RESOURCES_SCALE: f64 = 100.0;
33+
2934
/// Our game state.
3035
struct Game<'a, 'b> {
3136
/// The specs world.
@@ -35,7 +40,7 @@ struct Game<'a, 'b> {
3540
/// Our wrapper around the OpenGL calls.
3641
render: Render,
3742

38-
level: u8,
43+
level: f64,
3944
}
4045

4146
impl<'a, 'b> Game<'a, 'b> {
@@ -81,6 +86,9 @@ impl<'a, 'b> Game<'a, 'b> {
8186
// The current phase
8287
world.insert(Phase::default());
8388

89+
// Enemies left
90+
world.insert(EnemiesLeft::default());
91+
8492
// Setup the dispatcher with the blit system
8593
let dispatcher = DispatcherBuilder::new()
8694
.with(particle::ParticleEmitterSystem, "particle_emitter", &[])
@@ -108,9 +116,9 @@ impl<'a, 'b> Game<'a, 'b> {
108116
world,
109117
dispatcher,
110118
render,
111-
level: 1,
119+
level: 1.0,
112120
};
113-
game.switch_phase(Phase::Menu);
121+
game.switch_phase(Phase::default());
114122

115123
Ok(game)
116124
}
@@ -125,27 +133,31 @@ impl<'a, 'b> Game<'a, 'b> {
125133
self.world.delete_all();
126134

127135
match phase {
128-
Phase::Menu => {
129-
// Flash the screen for a bit
130-
self.world
131-
.create_entity()
132-
.with(effect::ScreenFlash::new(color::FOREGROUND))
133-
.with(entity::Lifetime::new(5.0))
134-
.build();
135-
}
136+
Phase::Menu => {}
136137
Phase::Initialize => {
137138
// Generate the ships
138139
self.world.insert(ship::Ships::generate());
139140

140141
self.switch_phase(Phase::Play);
141142
}
142-
Phase::Setup => {}
143+
Phase::Setup => {
144+
self.level += 1.0;
145+
}
143146
Phase::Play => {
147+
self.world
148+
.create_entity()
149+
.with(effect::ScreenFlash::new(color::FOREGROUND))
150+
.with(entity::Lifetime::new(5.0))
151+
.build();
152+
144153
self.world.insert(Lives::new(3));
145154

146155
self.world
147156
.create_entity()
148-
.with(enemy::EnemyEmitter::new(200.0, 30.0 * 60.0))
157+
.with(enemy::EnemyEmitter::new(
158+
200.0 + self.level * LEVEL_RESOURCES_SCALE,
159+
20.0 * 60.0 + self.level * LEVEL_TIME_SCALE,
160+
))
149161
.build();
150162

151163
// Spawn the paddle
@@ -158,25 +170,33 @@ impl<'a, 'b> Game<'a, 'b> {
158170
pub fn render_phase(&mut self) {
159171
let phase = self.world.read_resource::<Phase>();
160172

173+
let mut buffer = self.world.write_resource::<PixelBuffer>();
174+
let mut gui = self.world.write_resource::<Gui>();
161175
match *phase {
162176
Phase::Menu => {
163-
let mut buffer = self.world.write_resource::<PixelBuffer>();
164-
165177
// Render the GUI
166-
let mut gui = self.world.write_resource::<Gui>();
167-
gui.draw_label(&mut buffer, "Press SPACE to play!", 20, 20);
178+
gui.draw_label(&mut buffer, "Press SPACE to play!", 110, 200);
168179
}
169-
Phase::Play => {
170-
let mut buffer = self.world.write_resource::<PixelBuffer>();
180+
Phase::Setup => {
181+
gui.draw_label(&mut buffer, format!("Level: {}", self.level), 160, 20);
182+
gui.draw_label(&mut buffer, "Press SPACE to start!", 105, 200);
183+
}
184+
Phase::Play | Phase::WaitingForLastEnemy => {
171185
let lives = self.world.read_resource::<Lives>();
172186
lives.render(&mut buffer, 100, 5);
173187

174-
let mut gui = self.world.write_resource::<Gui>();
175-
gui.draw_label(&mut buffer, format!("Level {}", self.level), 20, 5);
188+
gui.draw_label(&mut buffer, format!("Level: {}", self.level), 20, 5);
189+
190+
if *phase == Phase::Play {
191+
gui.draw_label(
192+
&mut buffer,
193+
format!("Enemies: {}", self.world.read_resource::<EnemiesLeft>().0),
194+
200,
195+
5,
196+
);
197+
}
176198
}
177199
Phase::GameOver => {
178-
let mut buffer = self.world.write_resource::<PixelBuffer>();
179-
let mut gui = self.world.write_resource::<Gui>();
180200
gui.draw_label(&mut buffer, "GAME OVER!", 20, 20);
181201
}
182202
_ => (),
@@ -193,7 +213,9 @@ impl<'a, 'b> EventHandler for Game<'a, 'b> {
193213
self.world.maintain();
194214

195215
let mut phase = (*self.world.read_resource::<Phase>()).clone();
196-
if phase == Phase::Play && self.world.read_resource::<Lives>().is_dead() {
216+
if (phase == Phase::Play || phase == Phase::WaitingForLastEnemy)
217+
&& self.world.read_resource::<Lives>().is_dead()
218+
{
197219
phase = Phase::SwitchTo(Box::new(Phase::GameOver));
198220
}
199221

@@ -231,6 +253,8 @@ impl<'a, 'b> EventHandler for Game<'a, 'b> {
231253
let phase = (*self.world.read_resource::<Phase>()).clone();
232254
if phase == Phase::Menu && keycode == KeyCode::Space {
233255
self.switch_phase(Phase::Initialize);
256+
} else if phase == Phase::Setup && keycode == KeyCode::Space {
257+
self.switch_phase(Phase::Play);
234258
}
235259

236260
// Pass the input to the resource

src/movement.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::physics::*;
1+
use crate::{physics::*, random};
22
use specs_blit::specs::*;
33

44
#[derive(Component, Debug)]
@@ -11,7 +11,7 @@ pub struct Zigzag {
1111
impl Zigzag {
1212
pub fn new(amount: f64, time_div: f64) -> Self {
1313
Self {
14-
time: 0.0,
14+
time: random::range(0.0, 100.0),
1515
amount,
1616
time_div,
1717
}

src/particle.rs

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ impl ParticleEmitter {
4848

4949
self
5050
}
51+
52+
pub fn with_dispersion(mut self, dispersion: f64) -> Self {
53+
self.dispersion = dispersion;
54+
55+
self
56+
}
5157
}
5258

5359
/// System that will spawn particles.

src/phase.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub enum Phase {
44
Initialize,
55
Setup,
66
Play,
7+
WaitingForLastEnemy,
78
GameOver,
89
SwitchTo(Box<Phase>),
910
}

src/random.rs

+6
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ pub fn bool() -> bool {
99

1010
r > miniquad::RAND_MAX as i32 / 2
1111
}
12+
13+
pub fn index<T>(v: &Vec<T>) -> &T {
14+
let r = unsafe { miniquad::rand() } as usize / (miniquad::RAND_MAX as usize / v.len());
15+
16+
&v[r]
17+
}

0 commit comments

Comments
 (0)