+1
-1
.idea/runConfigurations/Run_Web_dev.xml
+1
-1
.idea/runConfigurations/Run_Web_dev.xml
···
1
1
<component name="ProjectRunConfigurationManager">
2
2
<configuration default="false" name="Run Web dev" type="ShConfigurationType" folderName="Build">
3
-
<option name="SCRIPT_TEXT" value="bevy run --yes web" />
3
+
<option name="SCRIPT_TEXT" value="bevy run --yes web --open" />
4
4
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
5
5
<option name="SCRIPT_PATH" value="" />
6
6
<option name="SCRIPT_OPTIONS" value="" />
+2
-2
.vscode/bevy.code-snippets
+2
-2
.vscode/bevy.code-snippets
+1
-1
src/asset_tracking.rs
+1
-1
src/asset_tracking.rs
+1
-1
src/audio.rs
+1
-1
src/audio.rs
+1
-1
src/parylord/attack.rs
+1
-1
src/parylord/attack.rs
+18
-10
src/parylord/enemy.rs
+18
-10
src/parylord/enemy.rs
···
3
3
use crate::parylord::health::{DisplayHealth, Health, ZeroHealth};
4
4
use crate::parylord::player::Player;
5
5
use crate::parylord::ttl::Ttl;
6
-
use crate::parylord::CollisionLayer;
6
+
use crate::parylord::{CollisionLayer, ParrylordSingleton};
7
7
use crate::screens::Screen;
8
8
use crate::{AppSystems, PausableSystems};
9
9
use avian2d::prelude::{
···
54
54
mut events: EventReader<SpawnEnemy>,
55
55
mut commands: Commands,
56
56
enemy_assets: Res<EnemyAssets>,
57
+
singleton: Res<ParrylordSingleton>,
57
58
) {
58
59
for _ in events.read() {
59
-
commands.spawn(Enemy::bundle(&enemy_assets, get_random_vec2_in_play_area()));
60
+
commands.spawn(Enemy::bundle(
61
+
&enemy_assets,
62
+
get_random_vec2_in_play_area(),
63
+
Enemy::BASE_HEALTH.pow(u32::from(singleton.level) - 1),
64
+
));
60
65
}
61
66
}
62
67
···
92
97
}
93
98
}
94
99
95
-
const DEBUG_STATE_MACHINE: bool = true;
100
+
const DEBUG_STATE_MACHINE: bool = false;
96
101
97
102
#[tracing::instrument(skip_all)]
98
103
pub fn write_enemy_intents(
···
285
290
}
286
291
287
292
impl Enemy {
288
-
const SPEED: f32 = 100.0;
293
+
const SPEED: f32 = 300.0;
294
+
const BASE_HEALTH: u8 = 2;
289
295
290
296
#[tracing::instrument()]
291
-
pub fn bundle(enemy_assets: &EnemyAssets, position: Vec2) -> impl Bundle {
297
+
pub fn bundle(enemy_assets: &EnemyAssets, position: Vec2, health: u8) -> impl Bundle {
292
298
let mut rng = thread_rng();
293
299
let pick = rng.gen_range(0..=EnemyAssets::MAX_ASSETS);
294
300
(
295
301
Self::default(),
296
-
Health(15),
302
+
Health(health),
297
303
DisplayHealth::bundle(),
298
304
EnemyStateTimer(Timer::from_seconds(2.0, TimerMode::Repeating)),
299
-
Transform::from_translation(position.extend(1.0)),
305
+
Transform::from_translation(position.extend(1.0)).with_scale(Vec3::splat(0.8)),
300
306
Sprite {
301
307
image: match pick % EnemyAssets::MAX_ASSETS {
302
308
0 => enemy_assets.beige.clone(),
···
311
317
},
312
318
RigidBody::Dynamic,
313
319
LinearVelocity::default(),
314
-
CollisionEventsEnabled,
315
320
Collider::circle(64.0),
316
321
CollisionLayers::new(
317
322
[CollisionLayer::Enemy],
318
323
[
324
+
CollisionLayer::Enemy,
319
325
CollisionLayer::Walls,
320
-
CollisionLayer::Player,
321
326
CollisionLayer::PlayerProjectile,
322
327
CollisionLayer::PlayerHurt,
323
328
],
···
330
335
pub fn handle_dead_enemies(
331
336
dead_enemies: Query<Entity, (With<ZeroHealth>, With<Enemy>)>,
332
337
mut commands: Commands,
338
+
mut singleton: ResMut<ParrylordSingleton>,
333
339
) {
334
340
for entity in dead_enemies {
335
341
let Ok(mut entity) = commands.get_entity(entity) else {
336
342
continue;
337
343
};
338
-
entity.despawn();
344
+
entity.try_despawn();
345
+
346
+
singleton.enemies_killed += 1;
339
347
}
340
348
}
341
349
+1
-1
src/parylord/health.rs
+1
-1
src/parylord/health.rs
+5
-5
src/parylord/level.rs
+5
-5
src/parylord/level.rs
···
1
1
use crate::parylord::assets::{LevelAssets, PlayerAssets};
2
2
use crate::parylord::enemy::{Enemy, SpawnEnemy};
3
3
use crate::parylord::player::Player;
4
-
use crate::parylord::{CollisionLayer, ParrylordLevel};
4
+
use crate::parylord::{CollisionLayer, ParrylordSingleton};
5
5
use crate::screens::Screen;
6
6
use crate::PausableSystems;
7
7
use avian2d::prelude::{Collider, CollisionLayers, RigidBody};
8
8
use bevy::prelude::*;
9
9
10
-
pub(super) fn plugin(app: &mut App) {
10
+
pub fn plugin(app: &mut App) {
11
11
app.register_type::<Level>();
12
12
app.register_type::<EnemySpawn>();
13
13
app.register_type::<LevelAssets>();
···
60
60
fn new_level(
61
61
enemies: Query<(), With<Enemy>>,
62
62
mut spawn_enemy_event_writer: EventWriter<SpawnEnemy>,
63
-
mut level: ResMut<ParrylordLevel>,
63
+
mut singleton: ResMut<ParrylordSingleton>,
64
64
) {
65
65
if !enemies.is_empty() {
66
66
return;
67
67
}
68
68
69
-
for _ in 0..level.0 {
69
+
for _ in 0..singleton.level {
70
70
spawn_enemy_event_writer.write(SpawnEnemy);
71
71
}
72
72
73
-
level.0 += 1;
73
+
singleton.level += 1;
74
74
}
75
75
76
76
// let root = context.entity;
+6
-2
src/parylord/mod.rs
+6
-2
src/parylord/mod.rs
···
13
13
pub mod ttl;
14
14
15
15
pub fn plugin(app: &mut App) {
16
-
app.init_resource::<ParrylordLevel>();
16
+
app.init_resource::<ParrylordSingleton>();
17
17
18
18
app.add_plugins((
19
19
assets::plugin,
···
44
44
45
45
#[derive(Resource, Clone, Reflect, Debug, Default)]
46
46
#[reflect(Resource)]
47
-
pub struct ParrylordLevel(u8);
47
+
pub struct ParrylordSingleton {
48
+
pub enemies_killed: u32,
49
+
pub level: u32,
50
+
pub max_parried: u32,
51
+
}
+6
-5
src/parylord/player.rs
+6
-5
src/parylord/player.rs
···
71
71
fn flip_x(&self) -> bool {
72
72
match self {
73
73
Self::Front(_) => false,
74
-
Self::Walk(dir, _) | Self::Stand(dir) => {
75
-
!dir.is_sign_positive()
76
-
}
74
+
Self::Walk(dir, _) | Self::Stand(dir) => !dir.is_sign_positive(),
77
75
}
78
76
}
79
77
···
248
246
continue;
249
247
};
250
248
251
-
entity.despawn();
249
+
entity.try_despawn();
252
250
}
253
251
}
254
252
}
255
253
256
254
#[tracing::instrument(skip_all)]
257
-
fn handle_player_death(query: Option<Single<(), (With<Player>, With<ZeroHealth>)>>) {
255
+
fn handle_player_death(
256
+
query: Option<Single<(), (With<Player>, With<ZeroHealth>)>>,
257
+
next: ResMut<NextState<Screen>>,
258
+
) {
258
259
if query.is_none() {
259
260
return;
260
261
}
+22
-19
src/parylord/player_attack.rs
+22
-19
src/parylord/player_attack.rs
···
4
4
use crate::parylord::health::{Health, InvincibilityTimer};
5
5
use crate::parylord::player::Player;
6
6
use crate::parylord::ttl::Ttl;
7
-
use crate::parylord::CollisionLayer;
7
+
use crate::parylord::{CollisionLayer, ParrylordSingleton};
8
8
use crate::screens::Screen;
9
9
use crate::{exponential_decay, AppSystems, PausableSystems};
10
10
use avian2d::prelude::{
···
210
210
attack_assets: Res<AttackAssets>,
211
211
window: Single<&Window>,
212
212
camera: Single<(&Camera, &GlobalTransform)>,
213
+
mut singleton: ResMut<ParrylordSingleton>,
213
214
) {
214
215
let Some(entities) = entities else {
215
216
return;
216
217
};
217
218
218
-
let number_of_entities = entities.len().clamp(0, AttackAssets::MAX as usize - 1); //remove clamp
219
219
let Some(&entity) = entities.first() else {
220
220
warn!("Some(&entity) = entities.get(0)");
221
221
return;
222
222
};
223
-
let Ok((velocity, _transform, ttl)) = change_components.get(entity) else {
223
+
let Ok(_) = change_components.get(entity) else {
224
224
warn!("Entity {entity} not in change_components");
225
225
return;
226
226
};
227
227
228
-
let pos = entities
228
+
let Some((sum_speed, sum_pos, sum_ttl, total)) = entities
229
229
.iter()
230
230
.flat_map(|&x| change_components.get(x))
231
-
.map(|(_, x, _)| x)
232
-
.map(|x| x.translation)
233
-
.map(|x| x.truncate())
234
-
.collect::<Vec<_>>();
235
-
236
-
let Some(sum) = pos.iter().copied().reduce(|acc, x| acc + x) else {
231
+
.map(|(x, y, z)| (x.length(), y.translation, z.0.remaining_secs()))
232
+
.map(|(x, y, z)| (x, y.truncate(), z, 1u32))
233
+
.reduce(|(a, b, c, d), (x, y, z, w)| (a + x, b + y, c + z, d + w))
234
+
else {
237
235
warn!("Some(&sum) = pos.iter().reduce(|acc, x| acc + x)");
238
236
return;
239
237
};
240
-
let total = pos.len() as f32;
238
+
239
+
singleton.max_parried = singleton.max_parried.max(total);
240
+
241
+
#[allow(clippy::cast_precision_loss)]
242
+
let total_f32 = total as f32;
241
243
242
244
let window = *window;
243
245
let Some(mouse) = window.cursor_position() else {
···
253
255
camera_transform,
254
256
);
255
257
let angle = Vec2::from_angle(angle);
256
-
let speed = velocity.0.length();
257
258
258
-
let pos = sum / total;
259
-
let velocity = LinearVelocity(angle * speed); // average the speed
260
-
let ttl = Ttl::new(ttl.0.remaining_secs() + 1.0);
259
+
let pos = sum_pos / total_f32;
260
+
let velocity = LinearVelocity(angle * sum_speed / total_f32);
261
+
let ttl = Ttl::new((sum_ttl / total_f32) + 1.0);
262
+
263
+
let power = 2u8.checked_pow(total - 1).unwrap_or(u8::MAX);
261
264
262
265
commands.spawn(PlayerAttack::bundle(
263
-
number_of_entities as u8, //remove clamp
266
+
power,
264
267
&attack_assets,
265
268
pos,
266
269
velocity,
···
271
274
let Ok(mut entity) = commands.get_entity(entity) else {
272
275
continue;
273
276
};
274
-
entity.despawn();
277
+
entity.try_despawn();
275
278
}
276
279
}
277
280
···
286
289
continue 'outer;
287
290
};
288
291
289
-
health.0 -= attack.0 as i8;
292
+
health.0 = health.0.saturating_sub(attack.0);
290
293
291
294
commands
292
295
.entity(entity)
···
298
301
let Ok(mut attack_entity) = commands.get_entity(attack_entity) else {
299
302
continue;
300
303
};
301
-
attack_entity.despawn();
304
+
attack_entity.try_despawn();
302
305
}
303
306
}
304
307
}
+2
-2
src/parylord/ttl.rs
+2
-2
src/parylord/ttl.rs
···
18
18
19
19
#[derive(Component, Debug, Clone, PartialEq, Eq, Default, Reflect)]
20
20
#[reflect(Component)]
21
-
pub struct Ttl(pub(crate) Timer);
21
+
pub struct Ttl(pub Timer);
22
22
23
23
impl Ttl {
24
24
pub fn new(secs: f32) -> Self {
···
45
45
let Ok(mut entity) = commands.get_entity(timer) else {
46
46
continue;
47
47
};
48
-
entity.despawn();
48
+
entity.try_despawn();
49
49
}
50
50
}
+2
-2
src/screens/gameplay.rs
+2
-2
src/screens/gameplay.rs
···
2
2
3
3
use bevy::{input::common_conditions::input_just_pressed, prelude::*, ui::Val::*};
4
4
5
-
use crate::{Pause, parylord::level::spawn_level, menus::Menu, screens::Screen};
5
+
use crate::{menus::Menu, parylord::level::spawn_level, screens::Screen, Pause};
6
6
7
-
pub(super) fn plugin(app: &mut App) {
7
+
pub fn plugin(app: &mut App) {
8
8
app.add_systems(OnEnter(Screen::Gameplay), spawn_level);
9
9
10
10
// Toggle pause on key press.
+1
-1
src/screens/loading.rs
+1
-1
src/screens/loading.rs
+1
-1
src/screens/mod.rs
+1
-1
src/screens/mod.rs
+2
-2
src/screens/splash.rs
+2
-2
src/screens/splash.rs
···
6
6
prelude::*,
7
7
};
8
8
9
-
use crate::{AppSystems, screens::Screen, theme::prelude::*};
9
+
use crate::{screens::Screen, theme::prelude::*, AppSystems};
10
10
11
-
pub(super) fn plugin(app: &mut App) {
11
+
pub fn plugin(app: &mut App) {
12
12
// Spawn splash screen.
13
13
app.insert_resource(ClearColor(SPLASH_BACKGROUND_COLOR));
14
14
app.add_systems(OnEnter(Screen::Splash), spawn_splash_screen);
+1
-1
src/screens/title.rs
+1
-1
src/screens/title.rs
+1
-1
src/theme/interaction.rs
+1
-1
src/theme/interaction.rs
+2
-2
src/theme/mod.rs
+2
-2
src/theme/mod.rs
···
9
9
10
10
#[allow(unused_imports)]
11
11
pub mod prelude {
12
-
pub use super::{interaction::InteractionPalette, palette as ui_palette, widget};
12
+
pub use super::widget;
13
13
}
14
14
15
15
use bevy::prelude::*;
16
16
17
-
pub(super) fn plugin(app: &mut App) {
17
+
pub fn plugin(app: &mut App) {
18
18
app.add_plugins(interaction::plugin);
19
19
}