Merge branch 'main' of github.com:Back777space/among-me
This commit is contained in:
@@ -164,7 +164,7 @@ fn spawn_level(
|
|||||||
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
round_door.clone(),
|
door.clone(),
|
||||||
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -180,7 +180,7 @@ fn spawn_level(
|
|||||||
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
round_door.clone(),
|
door.clone(),
|
||||||
pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians())),
|
pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians())),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -193,7 +193,7 @@ fn spawn_level(
|
|||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos),
|
(wall.clone(), pos),
|
||||||
(
|
(
|
||||||
round_door.clone(),
|
door.clone(),
|
||||||
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -206,7 +206,7 @@ fn spawn_level(
|
|||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos),
|
(wall.clone(), pos),
|
||||||
(
|
(
|
||||||
round_door.clone(),
|
door.clone(),
|
||||||
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::Stopwatch,
|
time::Stopwatch,
|
||||||
};
|
};
|
||||||
use bevy_kira_audio::{prelude::Volume, Audio, AudioControl, AudioTween};
|
use bevy_kira_audio::{prelude::Volume, Audio, AudioControl, AudioTween};
|
||||||
@@ -27,7 +27,7 @@ pub struct Monster {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum MonsterState {
|
pub enum MonsterState {
|
||||||
Lurking,
|
Lurking,
|
||||||
Hunting,
|
Hunting,
|
||||||
Wandering,
|
Wandering,
|
||||||
Dormant,
|
Dormant,
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ fn spawn_monster(
|
|||||||
// mut materials: ResMut<Assets<StandardMaterial>>,
|
// mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
) {
|
) {
|
||||||
let spawn_position = Vec3::new(20.0, 0.5, 20.0);
|
let spawn_position = Vec3::new(20.0, 0.5, 20.0);
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Monster::default(),
|
Monster::default(),
|
||||||
MonsterPathfinding {
|
MonsterPathfinding {
|
||||||
@@ -100,16 +100,16 @@ fn update_monster_state(
|
|||||||
player_query: Query<&Transform, With<Player>>,
|
player_query: Query<&Transform, With<Player>>,
|
||||||
) {
|
) {
|
||||||
let mut rand = rand::rng();
|
let mut rand = rand::rng();
|
||||||
|
|
||||||
if let Ok((mut monster, monster_transform, mut pathfinding, mut danger)) = monster_query.get_single_mut() {
|
if let Ok((mut monster, monster_transform, mut pathfinding, mut danger)) = monster_query.get_single_mut() {
|
||||||
let player_pos = if let Ok(player_transform) = player_query.get_single() {
|
let player_pos = if let Ok(player_transform) = player_query.get_single() {
|
||||||
Some(player_transform.translation)
|
Some(player_transform.translation)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
monster.state_timer.tick(time.delta());
|
monster.state_timer.tick(time.delta());
|
||||||
|
|
||||||
// when timer expires, potentially change state
|
// when timer expires, potentially change state
|
||||||
if monster.state_timer.just_finished() {
|
if monster.state_timer.just_finished() {
|
||||||
let next_state = match monster.state {
|
let next_state = match monster.state {
|
||||||
@@ -150,7 +150,7 @@ fn update_monster_state(
|
|||||||
MonsterState::Dormant
|
MonsterState::Dormant
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// set state
|
// set state
|
||||||
monster.state = next_state.clone();
|
monster.state = next_state.clone();
|
||||||
monster.state_timer = match next_state {
|
monster.state_timer = match next_state {
|
||||||
@@ -159,16 +159,16 @@ fn update_monster_state(
|
|||||||
MonsterState::Wandering => Timer::from_seconds(30.0, TimerMode::Once),
|
MonsterState::Wandering => Timer::from_seconds(30.0, TimerMode::Once),
|
||||||
MonsterState::Hunting => Timer::from_seconds(15.0, TimerMode::Once),
|
MonsterState::Hunting => Timer::from_seconds(15.0, TimerMode::Once),
|
||||||
};
|
};
|
||||||
|
|
||||||
// reset pathfinding when state changes
|
// reset pathfinding when state changes
|
||||||
pathfinding.current_target = None;
|
pathfinding.current_target = None;
|
||||||
println!("new state={:?}", monster.state);
|
println!("new state={:?}", monster.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// override state in some cases
|
// override state in some cases
|
||||||
if let Some(player_pos) = player_pos {
|
if let Some(player_pos) = player_pos {
|
||||||
let distance = monster_transform.translation.distance(player_pos);
|
let distance = monster_transform.translation.distance(player_pos);
|
||||||
|
|
||||||
// if player is very close and monster isn't hunting, switch to hunting
|
// if player is very close and monster isn't hunting, switch to hunting
|
||||||
if distance < monster.detection_range * 0.5 && monster.state != MonsterState::Hunting {
|
if distance < monster.detection_range * 0.5 && monster.state != MonsterState::Hunting {
|
||||||
if rand.random_bool(0.75) { // 75% chance to notice player
|
if rand.random_bool(0.75) { // 75% chance to notice player
|
||||||
@@ -178,7 +178,7 @@ fn update_monster_state(
|
|||||||
pathfinding.current_target = None;
|
pathfinding.current_target = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,10 +188,10 @@ fn move_monster(
|
|||||||
player_query: Query<&Transform, With<Player>>,
|
player_query: Query<&Transform, With<Player>>,
|
||||||
) {
|
) {
|
||||||
let mut rand = rand::rng();
|
let mut rand = rand::rng();
|
||||||
|
|
||||||
if let Ok((monster, mut transform, mut velocity, mut pathfinding)) = monster_query.get_single_mut() {
|
if let Ok((monster, mut transform, mut velocity, mut pathfinding)) = monster_query.get_single_mut() {
|
||||||
pathfinding.wander_target_timer.tick(time.delta());
|
pathfinding.wander_target_timer.tick(time.delta());
|
||||||
|
|
||||||
// move based on state
|
// move based on state
|
||||||
match monster.state {
|
match monster.state {
|
||||||
MonsterState::Dormant => {
|
MonsterState::Dormant => {
|
||||||
@@ -206,17 +206,17 @@ fn move_monster(
|
|||||||
0.0,
|
0.0,
|
||||||
rand.random_range(-range..range)
|
rand.random_range(-range..range)
|
||||||
);
|
);
|
||||||
|
|
||||||
let base_pos = if let Ok(player_transform) = player_query.get_single() {
|
let base_pos = if let Ok(player_transform) = player_query.get_single() {
|
||||||
player_transform.translation
|
player_transform.translation
|
||||||
} else {
|
} else {
|
||||||
transform.translation
|
transform.translation
|
||||||
};
|
};
|
||||||
|
|
||||||
pathfinding.current_target = Some(base_pos + random_offset);
|
pathfinding.current_target = Some(base_pos + random_offset);
|
||||||
pathfinding.wander_target_timer = Timer::from_seconds(10.0, TimerMode::Once);
|
pathfinding.wander_target_timer = Timer::from_seconds(10.0, TimerMode::Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
// move toward target at slow pace
|
// move toward target at slow pace
|
||||||
if let Some(target) = pathfinding.current_target {
|
if let Some(target) = pathfinding.current_target {
|
||||||
move_towards_target(&mut transform, &mut velocity, target, monster.speed * 0.65);
|
move_towards_target(&mut transform, &mut velocity, target, monster.speed * 0.65);
|
||||||
@@ -232,12 +232,12 @@ fn move_monster(
|
|||||||
0.0,
|
0.0,
|
||||||
rand.random_range(-range..range)
|
rand.random_range(-range..range)
|
||||||
);
|
);
|
||||||
|
|
||||||
pathfinding.current_target = Some(player_transform.translation + random_offset);
|
pathfinding.current_target = Some(player_transform.translation + random_offset);
|
||||||
pathfinding.wander_target_timer = Timer::from_seconds(7.0, TimerMode::Once);
|
pathfinding.wander_target_timer = Timer::from_seconds(7.0, TimerMode::Once);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move toward target at medium pace
|
// move toward target at medium pace
|
||||||
if let Some(target) = pathfinding.current_target {
|
if let Some(target) = pathfinding.current_target {
|
||||||
move_towards_target(&mut transform, &mut velocity, target, monster.speed * 0.85);
|
move_towards_target(&mut transform, &mut velocity, target, monster.speed * 0.85);
|
||||||
@@ -247,9 +247,9 @@ fn move_monster(
|
|||||||
// chase the motherfucker
|
// chase the motherfucker
|
||||||
if let Ok(player_transform) = player_query.get_single() {
|
if let Ok(player_transform) = player_query.get_single() {
|
||||||
move_towards_target(
|
move_towards_target(
|
||||||
&mut transform,
|
&mut transform,
|
||||||
&mut velocity,
|
&mut velocity,
|
||||||
player_transform.translation,
|
player_transform.translation,
|
||||||
monster.speed * 1.1
|
monster.speed * 1.1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,7 @@ fn move_towards_target(
|
|||||||
let direction = (target - transform.translation).normalize_or_zero();
|
let direction = (target - transform.translation).normalize_or_zero();
|
||||||
let direction = Vec3::new(direction.x, 0.0, direction.z).normalize_or_zero();
|
let direction = Vec3::new(direction.x, 0.0, direction.z).normalize_or_zero();
|
||||||
velocity.linvel = direction * speed;
|
velocity.linvel = direction * speed;
|
||||||
|
|
||||||
if direction.length_squared() > 0.01 {
|
if direction.length_squared() > 0.01 {
|
||||||
let look_direction = Vec3::new(direction.x, 0.0, direction.z).normalize_or_zero();
|
let look_direction = Vec3::new(direction.x, 0.0, direction.z).normalize_or_zero();
|
||||||
if look_direction != Vec3::ZERO {
|
if look_direction != Vec3::ZERO {
|
||||||
@@ -285,33 +285,43 @@ fn play_monster_sounds(
|
|||||||
player_query: Query<&Transform, With<Player>>,
|
player_query: Query<&Transform, With<Player>>,
|
||||||
) {
|
) {
|
||||||
let mut rand = rand::rng();
|
let mut rand = rand::rng();
|
||||||
if let (Ok((mut monster, monster_transform)), Ok(player_transform)) =
|
if let (Ok((mut monster, monster_transform)), Ok(player_transform)) =
|
||||||
(monster_query.get_single_mut(), player_query.get_single()) {
|
(monster_query.get_single_mut(), player_query.get_single()) {
|
||||||
|
|
||||||
monster.footstep_timer.tick(time.delta());
|
monster.footstep_timer.tick(time.delta());
|
||||||
let distance = monster_transform.translation.distance(player_transform.translation);
|
let distance = monster_transform.translation.distance(player_transform.translation);
|
||||||
|
|
||||||
// play footstep sound if the timer finished
|
// play footstep sound if the timer finished
|
||||||
if monster.footstep_timer.just_finished() {
|
if monster.footstep_timer.just_finished() {
|
||||||
match monster.state {
|
match monster.state {
|
||||||
MonsterState::Hunting => {
|
MonsterState::Hunting => {
|
||||||
monster.footstep_timer = Timer::from_seconds(1.5, TimerMode::Once);
|
monster.footstep_timer = Timer::from_seconds(10.0, TimerMode::Once);
|
||||||
|
monster.speed = 2.0
|
||||||
},
|
},
|
||||||
MonsterState::Lurking | MonsterState::Wandering => {
|
MonsterState::Lurking | MonsterState::Wandering => {
|
||||||
monster.footstep_timer = Timer::from_seconds(
|
monster.footstep_timer = Timer::from_seconds(
|
||||||
rand.random_range(3.0..7.0),
|
10.0,
|
||||||
TimerMode::Once
|
TimerMode::Once
|
||||||
);
|
);
|
||||||
|
monster.speed = 1.0
|
||||||
},
|
},
|
||||||
MonsterState::Dormant => {
|
MonsterState::Dormant => {
|
||||||
monster.footstep_timer = Timer::from_seconds(
|
monster.footstep_timer = Timer::from_seconds(
|
||||||
rand.random_range(10.0..20.0),
|
10.0,
|
||||||
TimerMode::Once
|
TimerMode::Once
|
||||||
);
|
);
|
||||||
|
monster.speed = 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
play_footstep_segment(audio, &audio_assets, distance, &monster.state, monster.footstep_timer.duration().as_secs_f32());
|
play_footstep_segment(
|
||||||
|
audio,
|
||||||
|
&audio_assets,
|
||||||
|
distance,
|
||||||
|
&monster.state,
|
||||||
|
monster.footstep_timer.duration().as_secs_f32(),
|
||||||
|
monster.speed
|
||||||
|
);
|
||||||
|
|
||||||
if monster.state == MonsterState::Hunting && rand.random_bool(0.3) {
|
if monster.state == MonsterState::Hunting && rand.random_bool(0.3) {
|
||||||
println!("Monster growl!");
|
println!("Monster growl!");
|
||||||
@@ -325,7 +335,8 @@ fn play_footstep_segment(
|
|||||||
audio_assets: &Res<AudioAssets>,
|
audio_assets: &Res<AudioAssets>,
|
||||||
distance: f32,
|
distance: f32,
|
||||||
state: &MonsterState,
|
state: &MonsterState,
|
||||||
dur: f32
|
dur: f32,
|
||||||
|
speed: f32
|
||||||
) {
|
) {
|
||||||
let base_volume: f32 = match state {
|
let base_volume: f32 = match state {
|
||||||
MonsterState::Dormant => 0.6,
|
MonsterState::Dormant => 0.6,
|
||||||
@@ -333,23 +344,25 @@ fn play_footstep_segment(
|
|||||||
MonsterState::Wandering => 0.8,
|
MonsterState::Wandering => 0.8,
|
||||||
MonsterState::Hunting => 1.0,
|
MonsterState::Hunting => 1.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// adjust volume based on distance (closer = louder)
|
// adjust volume based on distance (closer = louder)
|
||||||
let distance_factor: f32 = 1.0 - (distance / 100.0).clamp(0.0, 1.0);
|
let distance_factor: f32 = (1.0 - distance / 30.0).clamp(0.0, 1.0);
|
||||||
let volume = base_volume * distance_factor;
|
let volume = base_volume * distance_factor;
|
||||||
|
|
||||||
// play only a short segment of the footstep sound
|
// play only a short segment of the footstep sound
|
||||||
// by only playing the last part
|
// by only playing the last part
|
||||||
let mut start_time = 30.0 - dur;
|
let mut start_time = 30.0 - dur;
|
||||||
// let mut rand = rand::rng();
|
// let mut rand = rand::rng();
|
||||||
// start_time += rand.random_range(0.0..5.0);
|
// start_time += rand.random_range(0.0..5.0);
|
||||||
|
|
||||||
|
audio.stop();
|
||||||
audio.play(audio_assets.monster_footsteps.clone())
|
audio.play(audio_assets.monster_footsteps.clone())
|
||||||
.with_volume(volume as f64)
|
.with_volume(volume as f64)
|
||||||
.start_from((start_time as f64).min(27.0))
|
.start_from((start_time as f64).min(27.0))
|
||||||
.fade_in(AudioTween::linear(Duration::from_millis(100)))
|
.fade_in(AudioTween::linear(Duration::from_millis(100)))
|
||||||
|
.with_playback_rate(speed as f64)
|
||||||
.handle();
|
.handle();
|
||||||
|
|
||||||
println!("Monster footstep: State={:?}, Distance={:.2}, Volume={:.2}, Dur={:1}, Start={:1}",
|
println!("Monster footstep: State={:?}, Distance={:.2}, Volume={:.2}, Dur={:1}, Start={:1}",
|
||||||
state, distance, volume, dur, (start_time as f64).min(27.0));
|
state, distance, volume, dur, (start_time as f64).min(27.0));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -457,9 +457,9 @@ pub fn handle_spotlight(
|
|||||||
pub fn update_flashlight_charge(time: Res<Time>, mut flashlight_query: Query<&mut Flashlight>) {
|
pub fn update_flashlight_charge(time: Res<Time>, mut flashlight_query: Query<&mut Flashlight>) {
|
||||||
for mut flashlight in flashlight_query.iter_mut() {
|
for mut flashlight in flashlight_query.iter_mut() {
|
||||||
if flashlight.is_on {
|
if flashlight.is_on {
|
||||||
flashlight.charge = flashlight.charge - time.delta_secs() * 0.1;
|
flashlight.charge = flashlight.charge - time.delta_secs() * 0.2;
|
||||||
} else {
|
} else {
|
||||||
flashlight.charge = flashlight.charge + time.delta_secs() * 0.1;
|
flashlight.charge = flashlight.charge + time.delta_secs() * 0.2;
|
||||||
}
|
}
|
||||||
flashlight.charge = flashlight.charge.clamp(0.0, 4.0);
|
flashlight.charge = flashlight.charge.clamp(0.0, 4.0);
|
||||||
if flashlight.charge <= 0.0 {
|
if flashlight.charge <= 0.0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user