diff --git a/src/interaction/mod.rs b/src/interaction/mod.rs index 031e8ba..6d5ee2f 100644 --- a/src/interaction/mod.rs +++ b/src/interaction/mod.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -mod objects; +pub mod objects; mod ui; #[derive(Component)] diff --git a/src/interaction/objects.rs b/src/interaction/objects.rs index a57d3d7..242b173 100644 --- a/src/interaction/objects.rs +++ b/src/interaction/objects.rs @@ -2,16 +2,26 @@ use bevy::prelude::*; use bevy_rapier3d::prelude::ColliderDisabled; use crate::{ - level_instantiation::Door, player::{toolbar::Item, Player, PlayerAction}, util::single_mut + level_instantiation::Door, + player::{Player, PlayerAction, toolbar::Item}, + util::single_mut, }; use super::{Interact, ui::InteractionOpportunity}; +#[derive(Component, Clone, Copy)] +pub struct PickUpAble; #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -struct KeyCardId(u64); +pub struct KeyCardId(pub usize); #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] -struct OpenedByKey(KeyCardId); +pub struct OpenedByKey(pub KeyCardId); + +impl OpenedByKey { + pub fn can_be_opened_by(&self, card: KeyCardId) -> bool { + self.0 == card + } +} pub fn plugin(_app: &mut App) {} @@ -19,41 +29,45 @@ pub fn handle_pick_up( mut commands: Commands, // current action mut player_query: Query<(&PlayerAction, &Transform, &mut Item), With>, - mut vis_query: Query<&mut Visibility>, + mut vis_query: Query<&mut Visibility, With>, children: Query<&mut Children>, - mut item_transform: Query<&mut Transform, (With, Without)>, + mut item_transform: Query<&mut Transform, (With, Without, With)>, // current interactable mut interaction_opportunity: ResMut, ) { - let (action, transform, mut item) = single_mut!(player_query); - if *action == PlayerAction::PickUp { - // take out the interaction, because we are picking it up - let Some(target) = interaction_opportunity.0.take() else { - return; - }; - let replaced = item.set_item(target); - if let Ok(mut vis) = vis_query.get_mut(target) { - *vis = Visibility::Hidden; + let (PlayerAction::PickUp, transform, mut item) = single_mut!(player_query) else { + return; + }; + // take out the interaction, because we are picking it up + let Some(target) = interaction_opportunity.0.take() else { + return; + }; + // check if it is pickable and set visibility + let Ok(mut vis) = vis_query.get_mut(target) else { + return; + }; + *vis = Visibility::Hidden; + + // change colliders + if let Ok(colliders) = children.get(target) { + for &collider in colliders { + commands.entity(collider).insert(ColliderDisabled); } - if let Ok(colliders) = children.get(target) { + } + let replaced = item.set_item(target); + if let Some(dropped) = replaced { + if let Ok(mut vis) = vis_query.get_mut(dropped) { + *vis = Visibility::Visible; + } + if let Ok(colliders) = children.get(dropped) { for &collider in colliders { - commands.entity(collider).insert(ColliderDisabled); + commands.entity(collider).remove::(); } } - if let Some(dropped) = replaced { - if let Ok(mut vis) = vis_query.get_mut(dropped) { - *vis = Visibility::Visible; - } - if let Ok(colliders) = children.get(dropped) { - for &collider in colliders { - commands.entity(collider).remove::(); - } - } - // for simplicities sake, set the transform of the item that is dropped to that of the player - let mut item_transform = item_transform.get_mut(dropped).unwrap(); - *item_transform = *transform; - } + // for simplicities sake, set the transform of the item that is dropped to that of the player + let mut item_transform = item_transform.get_mut(dropped).unwrap(); + *item_transform = *transform; } } @@ -65,37 +79,53 @@ pub fn handle_drop( mut item_transform: Query<&mut Transform, (With, Without)>, children: Query<&mut Children>, ) { - let (action, transform, mut item) = single_mut!(player_query); - if *action == PlayerAction::Drop { - let Some(item) = item.take() else { - return; - }; - if let Ok(mut vis) = vis_query.get_mut(item) { - *vis = Visibility::Visible; - } - if let Ok(colliders) = children.get(item) { - for &collider in colliders { - commands.entity(collider).remove::(); - } - } - - // for simplicities sake, set the transform of the item that is dropped to that of the player - let mut item_transform = item_transform.get_mut(item).unwrap(); - *item_transform = *transform; + let (PlayerAction::Drop, transform, mut item) = single_mut!(player_query) else { + return; + }; + let Some(item) = item.take() else { + return; + }; + if let Ok(mut vis) = vis_query.get_mut(item) { + *vis = Visibility::Visible; } + if let Ok(colliders) = children.get(item) { + for &collider in colliders { + commands.entity(collider).remove::(); + } + } + + // for simplicities sake, set the transform of the item that is dropped to that of the player + let mut item_transform = item_transform.get_mut(item).unwrap(); + *item_transform = *transform; } pub fn handle_door_interaction( - player_query: Query<&PlayerAction, With>, mut doors: Query<&mut Door> + mut commands: Commands, + player_query: Query<(&PlayerAction, &Item), With>, + card_query: Query<&KeyCardId>, + // current interactable + interaction_opportunity: ResMut, + door_query: Query<(Entity, &OpenedByKey), With>, ) { - let Ok(action) = player_query.get_single() else { + // the card + let Ok((PlayerAction::OpenDoor, item)) = player_query.get_single() else { return; }; - if *action != PlayerAction::OpenDoor { + let Some(current_card) = item.inner().and_then(|e| card_query.get(e).ok()) else { return; - } + }; - for mut door in doors.iter_mut() { - door.is_open = !door.is_open; + // the door + let Some((door, door_keyhole)) = interaction_opportunity + .0 + .and_then(|e| door_query.get(e).ok()) + else { + return; + }; + + if door_keyhole.can_be_opened_by(*current_card) { + commands.entity(door).despawn_recursive(); + // unwrap is safe because of the item.inner().and_then + commands.entity(item.inner().unwrap()).despawn_recursive(); } } diff --git a/src/interaction/ui.rs b/src/interaction/ui.rs index 1b84f3a..24a67c1 100644 --- a/src/interaction/ui.rs +++ b/src/interaction/ui.rs @@ -1,6 +1,6 @@ -use crate::GameState; use crate::player::Player; use crate::util::single; +use crate::{GameState, level_instantiation::Door}; use bevy::{prelude::*, window::PrimaryWindow}; use bevy_egui::{EguiContexts, EguiPlugin, egui}; use bevy_rapier3d::prelude::*; @@ -17,7 +17,11 @@ pub(super) fn plugin(app: &mut App) { ( update_interaction_opportunities, display_interaction_prompt, - (objects::handle_pick_up, objects::handle_drop, objects::handle_door_interaction), + ( + objects::handle_pick_up, + objects::handle_drop, + objects::handle_door_interaction, + ), ) .chain() .run_if(in_state(GameState::Playing)), @@ -68,17 +72,19 @@ fn display_interaction_prompt( interaction_opportunity: Res, mut egui_contexts: EguiContexts, primary_windows: Query<&Window, With>, - names: Query<&Name, With>, // only the interactables ofcourse + names: Query<(&Name, Option<&Door>), With>, // only the interactables ofcourse ) { let Some(opportunity) = interaction_opportunity.0 else { return; }; let window = single!(primary_windows); - let entity_name = names - .get(opportunity) - .map(|name| name.as_str()) - .expect("A named Interactable object"); + let Ok(interaction_str) = names.get(opportunity).map(|(name, door)| match door { + Some(_) => "T: Open Door".into(), + None => format!("E: Pick Up {name}"), + }) else { + return; + }; // objective or item egui::Window::new("Interaction") @@ -87,6 +93,6 @@ fn display_interaction_prompt( .auto_sized() .fixed_pos(egui::Pos2::new(window.width() / 2., window.height() / 2.)) .show(egui_contexts.ctx_mut(), |ui| { - ui.label(format!("E: Pick Up {entity_name}")); + ui.label(interaction_str); }); } diff --git a/src/level_instantiation/mod.rs b/src/level_instantiation/mod.rs index 121028c..de8bf7a 100644 --- a/src/level_instantiation/mod.rs +++ b/src/level_instantiation/mod.rs @@ -5,7 +5,13 @@ use bevy_rapier3d::prelude::*; use rand::{Rng, seq::IteratorRandom}; use crate::{ - asset_loading::{GltfAssets, ImageAssets}, interaction::Interact, player::{toolbar::ItemIcon, Player, PlayerAction}, GameState + GameState, + asset_loading::{GltfAssets, ImageAssets}, + interaction::{ + Interact, + objects::{KeyCardId, OpenedByKey, PickUpAble}, + }, + player::{Player, PlayerAction, toolbar::ItemIcon}, }; pub fn map_plugin(app: &mut App) { @@ -593,7 +599,9 @@ fn spawn_objects( .spawn(( transform, Interact, + PickUpAble, RigidBody::Dynamic, + KeyCardId(i), Name::new(format!("Id Card {i}")), Visibility::Visible, ItemIcon::new(image_assets.id_card.clone()), @@ -624,7 +632,7 @@ fn spawn_objects( pub struct Door { pub is_open: bool, pub open_direction: (i32, i32), - pub position: (i32, i32) + pub position: (i32, i32), } fn spawn_doors( @@ -644,43 +652,55 @@ fn spawn_doors( let asset = gltf.default_scene.as_ref().unwrap(); let scene_root = SceneRoot(asset.clone()); - for level in levels.levels.iter() { + for (i, level) in levels.levels.iter().enumerate() { let (x, z) = level.end_node; let (direction, transform) = if x >= z { ( (0, 1), Transform::from_xyz(2.0 * x as f32, 0.0, -2.0 * z as f32) - .with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())) + .with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())), ) } else { ( (1, 0), - Transform::from_xyz(2.0 * x as f32, 0.0, -2.0 * z as f32) + Transform::from_xyz(2.0 * x as f32, 0.0, -2.0 * z as f32), ) }; - commands.spawn(( - RigidBody::Fixed, - Door { - is_open: false, - open_direction: direction, - position: (x, z) - }, - transform, - scene_root.clone(), - )).with_children(|parent| { - for (collider, transform) in collider.clone() { - parent.spawn((collider, transform)); - } - }); + commands + .spawn(( + // very scuff but need this + OpenedByKey(KeyCardId(i)), + Interact, + RigidBody::Fixed, + Name::new("Door"), + Door { + is_open: false, + open_direction: direction, + position: (x, z), + }, + transform, + scene_root.clone(), + )) + .with_children(|parent| { + for (collider, transform) in collider.clone() { + parent.spawn((collider, transform)); + } + // make it "interactable" in the world + parent.spawn(( + ActiveEvents::COLLISION_EVENTS, + Transform::default(), + Collider::ball(0.5), // Interaction radius + Sensor, + )); + }); } } fn handle_door_pos(mut query: Query<(&Door, &mut Transform)>) { for (door, mut transform) in query.iter_mut() { - if door.is_open { + if door.is_open { transform.translation.y = 2.0 - } - else { + } else { transform.translation.y = 0.0; } } diff --git a/src/player.rs b/src/player.rs index 68f5e41..fdbc759 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,5 +1,9 @@ use bevy::{ - input::mouse::AccumulatedMouseMotion, pbr::VolumetricFog, prelude::*, render::view::RenderLayers, window::{PrimaryWindow, WindowResized} + input::mouse::AccumulatedMouseMotion, + pbr::VolumetricFog, + prelude::*, + render::view::RenderLayers, + window::{PrimaryWindow, WindowResized}, }; use bevy_kira_audio::{Audio, AudioControl}; use bevy_rapier3d::prelude::*; @@ -167,8 +171,8 @@ pub fn init_player( range: 5.0, outer_angle: 28.0_f32.to_radians(), // wide cone for flashlight inner_angle: 12.0_f32.to_radians(), // narrower inner cone - shadows_enabled: false, // (set to true for realism) - radius: 0.35, // low value for hard light + shadows_enabled: false, // (set to true for realism) + radius: 0.35, // low value for hard light ..default() }, Transform::from_xyz(0.0, -0.15, 0.5), @@ -291,7 +295,7 @@ pub fn handle_input( if keyboard_input.just_pressed(KeyCode::KeyF) { *action = PlayerAction::ToggleFlashlight; } - if keyboard_input.just_pressed(KeyCode::KeyK) { + if keyboard_input.just_pressed(KeyCode::KeyT) { *action = PlayerAction::OpenDoor; } diff --git a/src/player/toolbar.rs b/src/player/toolbar.rs index 2934a14..027d189 100644 --- a/src/player/toolbar.rs +++ b/src/player/toolbar.rs @@ -13,6 +13,10 @@ impl Item { pub fn take(&mut self) -> Option { self.0.take() } + + pub fn inner(&self) -> Option { + self.0 + } } #[derive(Component, Default, Debug)]