use std::time::Duration; use bevy::{ prelude::*, time::Stopwatch, }; use bevy_kira_audio::{prelude::Volume, Audio, AudioControl, AudioTween}; use bevy_rapier3d::prelude::*; use rand::prelude::*; use crate::{ asset_loading::AudioAssets, player::Player, GameState }; // Monster states and behavior configuration #[derive(Debug, Component)] pub struct Monster { pub speed: f32, pub detection_range: f32, pub state_timer: Timer, pub footstep_timer: Timer, pub state: MonsterState, } #[derive(Debug, Clone, PartialEq)] pub enum MonsterState { Lurking, Hunting, Wandering, Dormant, } impl Default for Monster { fn default() -> Self { Self { speed: 2.0, detection_range: 15.0, state_timer: Timer::from_seconds(1.0, TimerMode::Once), footstep_timer: Timer::from_seconds(3.0, TimerMode::Repeating), state: MonsterState::Dormant, } } } #[derive(Debug, Component, Default)] pub struct MonsterPathfinding { pub current_target: Option, pub wander_target_timer: Timer, } #[derive(Debug, Component, Default)] pub struct DangerIndicator { pub last_sound_timer: Stopwatch, } pub fn plugin(app: &mut App) { app .add_systems(OnEnter(GameState::Playing), spawn_monster) .add_systems( Update, ( update_monster_state, move_monster, play_monster_sounds, ).run_if(in_state(GameState::Playing)), ); } fn spawn_monster( mut commands: Commands, // mut meshes: ResMut>, // mut materials: ResMut>, ) { let spawn_position = Vec3::new(20.0, 0.5, 20.0); commands.spawn(( Monster::default(), MonsterPathfinding { current_target: None, wander_target_timer: Timer::from_seconds(5.0, TimerMode::Repeating), }, DangerIndicator::default(), RigidBody::Dynamic, Collider::capsule(Vec3::new(0.0, -0.5, 0.0), Vec3::new(0.0, 1.0, 0.0), 0.6), Velocity::zero(), LockedAxes::ROTATION_LOCKED, Damping { linear_damping: 1.0, angular_damping: 1.0, }, Transform::from_translation(spawn_position), )); } fn update_monster_state( time: Res