diff --git a/assets/audio/monster-footsteps.ogg b/assets/audio/monster-footsteps.ogg new file mode 100644 index 0000000..3cf1f5c Binary files /dev/null and b/assets/audio/monster-footsteps.ogg differ diff --git a/assets/main.assets.ron b/assets/main.assets.ron index d01bbfe..99637d0 100644 --- a/assets/main.assets.ron +++ b/assets/main.assets.ron @@ -2,6 +2,7 @@ "lebron": File (path: "images/KingLebron.png"), "flash_hold_4": File (path: "images/pixelart/Flashlight_hold_4.png"), "flash_hold_4_pressed": File (path: "images/pixelart/Flashlight_click_4.png"), + "monster_footsteps": File (path: "audio/monster-footsteps.ogg"), "house": File (path: "meshes/House.glb"), "flashlight_click": File (path: "audio/flashlight-switch.ogg"), "library": Folder ( diff --git a/src/asset_loading/mod.rs b/src/asset_loading/mod.rs index a2ab2e5..0efc560 100644 --- a/src/asset_loading/mod.rs +++ b/src/asset_loading/mod.rs @@ -31,6 +31,8 @@ pub(super) fn plugin(app: &mut App) { pub(crate) struct AudioAssets { #[asset(key = "flashlight_click")] pub(crate) flash_click: Handle, + #[asset(key = "monster_footsteps")] + pub(crate) monster_footsteps: Handle, } #[derive(AssetCollection, Resource, Clone)] diff --git a/src/main.rs b/src/main.rs index 4554a3a..ddaf90c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ mod main_menu; mod player; mod util; mod debugging; +mod monster; fn main() { App::new() @@ -22,12 +23,13 @@ fn main() { RapierPhysicsPlugin::::default(), // RapierDebugRenderPlugin::default(), player::plugin, + monster::plugin, // debugging::plugin )) .init_state::() .insert_resource(AmbientLight { color: Color::srgba(0.8, 0.8, 1.0, 1.0), - // brightness: 10.0, + // brightness: 11.0, brightness: 80.0, }) .add_systems(OnEnter(GameState::Playing), setup) diff --git a/src/monster.rs b/src/monster.rs new file mode 100644 index 0000000..4acf752 --- /dev/null +++ b/src/monster.rs @@ -0,0 +1,355 @@ +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