Skip to content

Commit 9911800

Browse files
committed
Hook up a few sfx
1 parent 7c685a0 commit 9911800

19 files changed

+127
-51
lines changed

assets/audio/sfx/Level Up_1.ogg

14.5 KB
Binary file not shown.

assets/audio/sfx/Level Up_1.wav

148 KB
Binary file not shown.
32 KB
Binary file not shown.
565 KB
Binary file not shown.
16.7 KB
Binary file not shown.
457 KB
Binary file not shown.

assets/audio/sfx/Restart_1.ogg

52.3 KB
Binary file not shown.

assets/audio/sfx/Restart_1.wav

694 KB
Binary file not shown.

assets/config/audio.ron

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
(
2-
bpm: 122,
3-
zeroth_beat: 0.1,
2+
global_volume: 0.6,
3+
4+
music_bpm: 122,
5+
music_zeroth_beat: 0.1,
6+
music_volume: 0.7,
7+
music_loop_end: 126.1,
48
)

assets/config/projectile.ron

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"quarter_note": Projectile(
44
name: "Quarter Note",
55

6-
texture_path: "image/projectile/quarter_note.png",
6+
texture: "image/projectile/quarter_note.png",
7+
spawn_sfx: "audio/sfx/Projectile Hits Enemy.ogg",
8+
spawn_sfx_volume: 0.5,
79

810
lifetime: 1,
911
radius: 3.0,
@@ -14,7 +16,9 @@
1416
"double_beat": Projectile(
1517
name: "Double Beat",
1618

17-
texture_path: "image/projectile/quarter_note.png",
19+
texture: "image/projectile/quarter_note.png",
20+
spawn_sfx: "audio/sfx/Projectile Hits Enemy.ogg",
21+
spawn_sfx_volume: 0.5,
1822

1923
lifetime: 2,
2024
radius: 3.0,

src/game/actor/attack.rs

+7-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use serde::Serialize;
88
use crate::core::UpdateSet;
99
use crate::game::actor::faction::Faction;
1010
use crate::game::combat::projectile::projectile;
11-
use crate::game::GameLayer;
1211
use crate::util::prelude::*;
1312

1413
pub(super) fn plugin(app: &mut App) {
@@ -31,7 +30,8 @@ pub struct Attack {
3130
/// The relative distance to spawn projectiles from.
3231
pub offset: f32,
3332
/// The key of the projectile to attack with.
34-
pub projectile: Option<String>,
33+
#[serde(rename = "projectile")]
34+
pub projectile_key: Option<String>,
3535
}
3636

3737
impl Configure for Attack {
@@ -48,7 +48,7 @@ impl Default for Attack {
4848
force: 1.0,
4949
color: Color::WHITE,
5050
offset: 5.0,
51-
projectile: None,
51+
projectile_key: None,
5252
}
5353
}
5454
}
@@ -63,24 +63,18 @@ fn apply_attack(
6363
&Faction,
6464
)>,
6565
) {
66-
for (attack, controller, gt, velocity, faction) in &attack_query {
66+
for (attack, controller, gt, velocity, &faction) in &attack_query {
6767
if !controller.fire || controller.aim == Vec2::ZERO {
6868
continue;
6969
}
70-
let projectile_key = c!(attack.projectile.as_ref());
70+
let projectile_key = c!(attack.projectile_key.as_ref());
7171

7272
let translation = gt.translation();
7373
// Spawn projectile at an initial distance away from attacker.
7474
let pos = translation.xy() + attack.offset * controller.aim;
7575
// Render projectile above attacker.
7676
let translation = pos.extend(translation.z + 2.0);
7777

78-
let mut target_layers = LayerMask::ALL;
79-
// Projectiles cannot collide with each other.
80-
target_layers.remove(GameLayer::Projectile);
81-
// Projectiles cannot collide with their owner's layer.
82-
target_layers.remove(faction.layer());
83-
8478
// Projectiles get a boost if the actor is moving in the same direction.
8579
let aligned_speed = velocity
8680
.filter(|v| v.0 != Vec2::ZERO)
@@ -93,14 +87,12 @@ fn apply_attack(
9387
commands
9488
.spawn_with(projectile(
9589
projectile_key,
90+
faction,
9691
attack.power,
9792
attack.force * controller.aim * speed_force,
9893
attack.color,
9994
))
100-
.insert((
101-
Transform::from_translation(translation),
102-
CollisionLayers::new(GameLayer::Projectile, target_layers),
103-
));
95+
.insert(Transform::from_translation(translation));
10496
}
10597
}
10698

src/game/actor/level/up.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use bevy::prelude::*;
2+
use bevy_kira_audio::prelude::*;
23

34
use crate::core::UpdateSet;
45
use crate::game::actor::level::xp::Xp;
56
use crate::game::actor::level::Level;
67
use crate::game::actor::level::LevelConfig;
8+
use crate::screen::playing::PlayingAssets;
79
use crate::util::prelude::*;
810

911
// TODO: System that enters level up menu on LevelUp event.
@@ -21,6 +23,9 @@ impl Configure for LevelUp {
2123
app.add_systems(
2224
Update,
2325
(
26+
play_level_up_sfx
27+
.in_set(UpdateSet::Update)
28+
.run_if(on_event::<Self>()),
2429
update_level_up_from_xp.in_set(UpdateSet::TriggerLevelUp),
2530
// TODO: Only run if not in level up menu.
2631
trigger_level_up.in_set(UpdateSet::TriggerLevelUp),
@@ -66,3 +71,7 @@ fn trigger_level_up(
6671
level_up_events.send(LevelUp(entity));
6772
}
6873
}
74+
75+
fn play_level_up_sfx(audio: Res<Audio>, assets: Res<PlayingAssets>) {
76+
audio.play(assets.sfx_level_up.clone()).with_volume(0.8);
77+
}

src/game/audio.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
pub mod music;
22

33
use bevy::prelude::*;
4+
use bevy_kira_audio::prelude::*;
45
use serde::Deserialize;
56
use serde::Serialize;
67

8+
use crate::game::audio::music::MusicHandle;
9+
use crate::screen::playing::PlayingAssets;
710
use crate::util::prelude::*;
811

912
pub(super) fn plugin(app: &mut App) {
@@ -14,13 +17,38 @@ pub(super) fn plugin(app: &mut App) {
1417

1518
#[derive(Asset, Reflect, Serialize, Deserialize)]
1619
pub struct AudioConfig {
20+
pub global_volume: f64,
21+
1722
/// The precise beats-per-minute of the music.
18-
pub bpm: f64,
23+
pub music_bpm: f64,
1924
/// The position (in seconds) of the zeroth beat.
20-
pub zeroth_beat: f64,
25+
pub music_zeroth_beat: f64,
26+
pub music_volume: f64,
27+
#[serde(default)]
28+
pub music_loop_start: f64,
29+
pub music_loop_end: f64,
2130
}
2231

2332
impl Config for AudioConfig {
2433
const PATH: &'static str = "config/audio.ron";
2534
const EXTENSION: &'static str = "audio.ron";
35+
36+
fn on_load(&mut self, world: &mut World) {
37+
world.resource::<Audio>().set_volume(self.global_volume);
38+
39+
if !world
40+
.resource::<Assets<AudioInstance>>()
41+
.contains(&world.resource::<MusicHandle>().0)
42+
{
43+
let music_handle = world
44+
.resource::<Audio>()
45+
.play(world.resource::<PlayingAssets>().music.clone())
46+
.with_volume(self.music_volume)
47+
.loop_from(self.music_loop_start)
48+
.loop_until(self.music_loop_end)
49+
.paused()
50+
.handle();
51+
world.resource_mut::<MusicHandle>().0 = music_handle;
52+
}
53+
}
2654
}

src/game/audio/music.rs

+2-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use pyri_state::prelude::*;
55
use crate::core::pause::Pause;
66
use crate::core::UpdateSet;
77
use crate::game::audio::AudioConfig;
8-
use crate::screen::playing::PlayingAssets;
98
use crate::screen::Screen;
109
use crate::util::prelude::*;
1110

@@ -21,7 +20,6 @@ impl Configure for MusicHandle {
2120
fn configure(app: &mut App) {
2221
app.register_type::<Self>();
2322
app.init_resource::<Self>();
24-
app.add_systems(Startup, spawn_music);
2523
app.add_systems(
2624
StateFlush,
2725
Pause.on_edge(
@@ -33,16 +31,6 @@ impl Configure for MusicHandle {
3331
}
3432
}
3533

36-
fn spawn_music(mut commands: Commands, audio: Res<Audio>, assets: Res<PlayingAssets>) {
37-
let handle = audio
38-
.play(assets.music.clone())
39-
.with_volume(0.9)
40-
.loop_until(126.1)
41-
.paused()
42-
.handle();
43-
commands.insert_resource(MusicHandle(handle));
44-
}
45-
4634
pub fn start_music(
4735
music_handle: Res<MusicHandle>,
4836
mut audio_instances: ResMut<Assets<AudioInstance>>,
@@ -95,7 +83,8 @@ fn update_beat(
9583
let music = r!(audio_instances.get(&music_handle.0));
9684

9785
let position = music.state().position().unwrap_or(0.0);
98-
let real_beats = ((position - config.zeroth_beat) * config.bpm * 8.0 / 60.0) as usize;
86+
let real_beats =
87+
((position - config.music_zeroth_beat) * config.music_bpm * 8.0 / 60.0) as usize;
9988

10089
beat.this_tick = real_beats.saturating_sub(beat.total);
10190
beat.total = real_beats;

src/game/card/attack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn double_beat(mut attack_query: Query<(&mut Attack, &mut AttackController, &Dou
5656
for (mut attack, mut controller, double_beat) in &mut attack_query {
5757
attack.power = double_beat.0.power;
5858
attack.force = double_beat.0.force;
59-
attack.projectile = double_beat.0.projectile.clone();
59+
attack.projectile_key = double_beat.0.projectile_key.clone();
6060

6161
controller.fire = true;
6262
}

src/game/combat/projectile.rs

+50-11
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ use std::time::Duration;
22

33
use avian2d::prelude::*;
44
use bevy::ecs::system::EntityCommand;
5+
use bevy::ecs::system::SystemState;
56
use bevy::prelude::*;
67
use bevy::utils::HashMap;
8+
use bevy_kira_audio::prelude::*;
79
use bevy_tweening::*;
810
use serde::Deserialize;
911
use serde::Serialize;
1012

13+
use crate::game::actor::faction::Faction;
1114
use crate::game::cleanup::DespawnOnHit;
1215
use crate::game::cleanup::DespawnOnTimer;
1316
use crate::game::cleanup::DespawnRadiusSq;
1417
use crate::game::combat::damage::HitboxDamage;
1518
use crate::game::combat::hit::Hitbox;
1619
use crate::game::combat::knockback::HitboxKnockback;
20+
use crate::game::GameLayer;
1721
use crate::game::GameRoot;
1822
use crate::util::prelude::*;
1923

@@ -39,23 +43,38 @@ impl Config for ProjectileConfig {
3943

4044
for projectile in self.projectiles.values_mut() {
4145
projectile.texture = asset_server.load(&projectile.texture_path);
46+
if !projectile.spawn_sfx_path.is_empty() {
47+
projectile.spawn_sfx = Some(asset_server.load(&projectile.spawn_sfx_path));
48+
}
4249
}
4350
}
4451

4552
fn is_ready(&self, asset_server: &AssetServer) -> bool {
4653
self.projectiles
4754
.values()
4855
.all(|x| asset_server.is_loaded_with_dependencies(&x.texture))
56+
&& self.projectiles.values().all(|x| {
57+
!x.spawn_sfx
58+
.as_ref()
59+
.is_some_and(|x| !asset_server.is_loaded_with_dependencies(x))
60+
})
4961
}
5062
}
5163

52-
#[derive(Reflect, Serialize, Deserialize)]
64+
#[derive(Reflect, Serialize, Deserialize, Clone)]
5365
pub struct Projectile {
5466
pub name: String,
5567

68+
#[serde(rename = "texture")]
5669
pub texture_path: String,
5770
#[serde(skip)]
5871
pub texture: Handle<Image>,
72+
#[serde(rename = "spawn_sfx", default)]
73+
pub spawn_sfx_path: String,
74+
#[serde(skip)]
75+
pub spawn_sfx: Option<Handle<AudioSource>>,
76+
#[serde(default = "one")]
77+
pub spawn_sfx_volume: f64,
5978

6079
/// Lifetime in seconds, not beats.
6180
pub lifetime: f32,
@@ -66,27 +85,46 @@ pub struct Projectile {
6685
pub knockback: f32,
6786
}
6887

88+
fn one() -> f64 {
89+
1.0
90+
}
91+
6992
const FADE_SECS: f32 = 0.2;
7093

7194
pub fn projectile(
7295
key: impl Into<String>,
96+
faction: Faction,
7397
power: f32,
7498
force: Vec2,
7599
color: impl Into<Color>,
76-
) -> impl EntityCommand<World> {
100+
) -> impl EntityCommand {
77101
let key = key.into();
78102
let color = color.into();
79103

80-
move |mut entity: EntityWorldMut| {
81-
let config_handle = entity.world().resource::<ConfigHandle<ProjectileConfig>>();
82-
let config = r!(entity
83-
.world()
84-
.resource::<Assets<ProjectileConfig>>()
85-
.get(&config_handle.0));
86-
let projectile = r!(config.projectiles.get(&key));
87-
let parent = entity.world().resource::<GameRoot>().projectiles;
104+
move |entity: Entity, world: &mut World| {
105+
let mut system_state =
106+
SystemState::<(ConfigRef<ProjectileConfig>, Res<GameRoot>, Res<Audio>)>::new(world);
107+
let (config, game_root, audio) = system_state.get(world);
108+
let config = r!(config.get());
109+
let projectile = r!(config.projectiles.get(&key)).clone();
110+
let parent = game_root.projectiles;
111+
let target_layers = {
112+
let mut x = LayerMask::ALL;
113+
// Projectiles cannot collide with each other.
114+
x.remove(GameLayer::Projectile);
115+
// Projectiles cannot collide with their owner's layer.
116+
x.remove(faction.layer());
117+
x
118+
};
119+
120+
if let (Faction::Player, Some(spawn_sfx)) = (faction, projectile.spawn_sfx) {
121+
audio
122+
.play(spawn_sfx)
123+
.with_volume(projectile.spawn_sfx_volume);
124+
}
88125

89-
entity
126+
world
127+
.entity_mut(entity)
90128
.insert((
91129
Name::new(projectile.name.replace(' ', "")),
92130
// Appearance:
@@ -114,6 +152,7 @@ pub fn projectile(
114152
(
115153
RigidBody::Kinematic,
116154
Collider::circle(projectile.radius),
155+
CollisionLayers::new(GameLayer::Projectile, target_layers),
117156
LockedAxes::ROTATION_LOCKED,
118157
LinearVelocity(projectile.speed * force),
119158
),

src/screen/playing.rs

+6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ pub struct PlayingAssets {
7777

7878
#[asset(path = "audio/music/Menu Theme.ogg")]
7979
pub music: Handle<AudioSource>,
80+
#[asset(path = "audio/sfx/Restart_1.ogg")]
81+
pub sfx_restart: Handle<AudioSource>,
82+
#[asset(path = "audio/sfx/Level Up_1.ogg")]
83+
pub sfx_level_up: Handle<AudioSource>,
84+
#[asset(path = "audio/sfx/Projectile Hits Player-02.ogg")]
85+
pub sfx_hurt: Handle<AudioSource>,
8086
}
8187

8288
impl Configure for PlayingAssets {

0 commit comments

Comments
 (0)