Merge branch 'main' of github.com-personal:Back777space/among-me
This commit is contained in:
BIN
assets/images/id_card_toolbar.png
Normal file
BIN
assets/images/id_card_toolbar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -4,8 +4,20 @@
|
|||||||
"flash_hold_4_pressed": File (path: "images/pixelart/Flashlight_click_4.png"),
|
"flash_hold_4_pressed": File (path: "images/pixelart/Flashlight_click_4.png"),
|
||||||
"monster_footsteps": File (path: "audio/monster-footsteps.ogg"),
|
"monster_footsteps": File (path: "audio/monster-footsteps.ogg"),
|
||||||
"house": File (path: "meshes/House.glb"),
|
"house": File (path: "meshes/House.glb"),
|
||||||
"flashlight_click": File (path: "audio/flashlight-switch.ogg"),
|
|
||||||
"library": Folder (
|
"library": Folder (
|
||||||
path: "meshes/library",
|
path: "meshes/library",
|
||||||
),
|
),
|
||||||
|
"id_card": File(path: "meshes/id_card.glb"),
|
||||||
|
"id_card_toolbar": File(path: "images/id_card_toolbar.png"),
|
||||||
|
"flash_hold_0": File (path: "images/pixelart/Flashlight_hold_0.png"),
|
||||||
|
"flash_hold_0_pressed": File (path: "images/pixelart/Flashlight_click_0.png"),
|
||||||
|
"flash_hold_1": File (path: "images/pixelart/Flashlight_hold_1.png"),
|
||||||
|
"flash_hold_1_pressed": File (path: "images/pixelart/Flashlight_click_1.png"),
|
||||||
|
"flash_hold_2": File (path: "images/pixelart/Flashlight_hold_2.png"),
|
||||||
|
"flash_hold_2_pressed": File (path: "images/pixelart/Flashlight_click_2.png"),
|
||||||
|
"flash_hold_3": File (path: "images/pixelart/Flashlight_hold_3.png"),
|
||||||
|
"flash_hold_3_pressed": File (path: "images/pixelart/Flashlight_click_3.png"),
|
||||||
|
"flash_hold_4": File (path: "images/pixelart/Flashlight_hold_4.png"),
|
||||||
|
"flash_hold_4_pressed": File (path: "images/pixelart/Flashlight_click_4.png"),
|
||||||
|
"flashlight_click": File (path: "audio/flashlight-switch.ogg"),
|
||||||
})
|
})
|
||||||
|
|||||||
BIN
assets/meshes/id_card.glb
Normal file
BIN
assets/meshes/id_card.glb
Normal file
Binary file not shown.
@@ -9,19 +9,18 @@ use bevy_kira_audio::{AudioPlugin, AudioSource};
|
|||||||
/// Loads resources and assets for the game.
|
/// Loads resources and assets for the game.
|
||||||
/// See assets/main.assets.ron for the actual paths used.
|
/// See assets/main.assets.ron for the actual paths used.
|
||||||
pub(super) fn plugin(app: &mut App) {
|
pub(super) fn plugin(app: &mut App) {
|
||||||
app.add_plugins(AudioPlugin)
|
app.add_plugins(AudioPlugin).add_loading_state(
|
||||||
.add_loading_state(
|
LoadingState::new(GameState::Loading)
|
||||||
LoadingState::new(GameState::Loading)
|
.continue_to_state(GameState::Menu)
|
||||||
.continue_to_state(GameState::Menu)
|
.with_dynamic_assets_file::<StandardDynamicAssetCollection>("main.assets.ron")
|
||||||
.with_dynamic_assets_file::<StandardDynamicAssetCollection>("main.assets.ron")
|
.load_collection::<ImageAssets>()
|
||||||
.load_collection::<ImageAssets>()
|
.load_collection::<FlashlightAssets>()
|
||||||
.load_collection::<FlashlightAssets>()
|
.load_collection::<GltfAssets>()
|
||||||
.load_collection::<GltfAssets>()
|
.load_collection::<AudioAssets>(),
|
||||||
.load_collection::<AudioAssets>(),
|
// .load_collection::<TextureAssets>()
|
||||||
// .load_collection::<TextureAssets>()
|
// .load_collection::<GrassAssets>()
|
||||||
// .load_collection::<GrassAssets>()
|
// .load_collection::<ConfigAssets>(),
|
||||||
// .load_collection::<ConfigAssets>(),
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the following asset collections will be loaded during the State `GameState::InitialLoading`
|
// the following asset collections will be loaded during the State `GameState::InitialLoading`
|
||||||
@@ -39,6 +38,8 @@ pub(crate) struct AudioAssets {
|
|||||||
pub(crate) struct GltfAssets {
|
pub(crate) struct GltfAssets {
|
||||||
#[asset(key = "library", collection(typed, mapped))]
|
#[asset(key = "library", collection(typed, mapped))]
|
||||||
pub(crate) library: HashMap<String, Handle<Gltf>>,
|
pub(crate) library: HashMap<String, Handle<Gltf>>,
|
||||||
|
#[asset(key = "id_card")]
|
||||||
|
pub(crate) card: Handle<Gltf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AssetCollection, Resource, Clone)]
|
#[derive(AssetCollection, Resource, Clone)]
|
||||||
@@ -54,10 +55,28 @@ pub(crate) struct ConfigAssets {}
|
|||||||
pub(crate) struct ImageAssets {
|
pub(crate) struct ImageAssets {
|
||||||
#[asset(key = "lebron")]
|
#[asset(key = "lebron")]
|
||||||
pub(crate) king: Handle<Image>,
|
pub(crate) king: Handle<Image>,
|
||||||
|
#[asset(key = "id_card_toolbar")]
|
||||||
|
pub(crate) id_card: Handle<Image>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(AssetCollection, Resource, Clone)]
|
#[derive(AssetCollection, Resource, Clone)]
|
||||||
pub(crate) struct FlashlightAssets {
|
pub(crate) struct FlashlightAssets {
|
||||||
|
#[asset(key = "flash_hold_0")]
|
||||||
|
pub(crate) flash_hold_0: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_0_pressed")]
|
||||||
|
pub(crate) flash_hold_0_pressed: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_1")]
|
||||||
|
pub(crate) flash_hold_1: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_1_pressed")]
|
||||||
|
pub(crate) flash_hold_1_pressed: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_2")]
|
||||||
|
pub(crate) flash_hold_2: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_2_pressed")]
|
||||||
|
pub(crate) flash_hold_2_pressed: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_3")]
|
||||||
|
pub(crate) flash_hold_3: Handle<Image>,
|
||||||
|
#[asset(key = "flash_hold_3_pressed")]
|
||||||
|
pub(crate) flash_hold_3_pressed: Handle<Image>,
|
||||||
#[asset(key = "flash_hold_4")]
|
#[asset(key = "flash_hold_4")]
|
||||||
pub(crate) flash_hold_4: Handle<Image>,
|
pub(crate) flash_hold_4: Handle<Image>,
|
||||||
#[asset(key = "flash_hold_4_pressed")]
|
#[asset(key = "flash_hold_4_pressed")]
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ use bevy::prelude::*;
|
|||||||
mod objects;
|
mod objects;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Interact;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins((ui::plugin, objects::plugin));
|
app.add_plugins((ui::plugin, objects::plugin));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,95 @@
|
|||||||
use bevy::{prelude::*};
|
use bevy::prelude::*;
|
||||||
use bevy_rapier3d::prelude::{Collider, RigidBody};
|
use bevy_rapier3d::prelude::ColliderDisabled;
|
||||||
|
|
||||||
use crate::{GameState, asset_loading::GltfAssets};
|
use crate::{
|
||||||
|
level_instantiation::Door, player::{toolbar::Item, Player, PlayerAction}, util::single_mut
|
||||||
|
};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
use super::{Interact, ui::InteractionOpportunity};
|
||||||
app.add_systems(OnEnter(GameState::Playing), spawn);
|
|
||||||
|
pub fn plugin(_app: &mut App) {}
|
||||||
|
|
||||||
|
pub fn handle_pick_up(
|
||||||
|
mut commands: Commands,
|
||||||
|
// current action
|
||||||
|
mut player_query: Query<(&PlayerAction, &Transform, &mut Item), With<Player>>,
|
||||||
|
mut vis_query: Query<&mut Visibility>,
|
||||||
|
children: Query<&mut Children>,
|
||||||
|
|
||||||
|
mut item_transform: Query<&mut Transform, (With<Interact>, Without<Player>)>,
|
||||||
|
// current interactable
|
||||||
|
mut interaction_opportunity: ResMut<InteractionOpportunity>,
|
||||||
|
) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if let Ok(colliders) = children.get(target) {
|
||||||
|
for &collider in colliders {
|
||||||
|
commands.entity(collider).insert(ColliderDisabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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::<ColliderDisabled>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
pub fn handle_drop(
|
||||||
pub struct Interact;
|
mut commands: Commands,
|
||||||
|
// current action
|
||||||
|
mut player_query: Query<(&PlayerAction, &Transform, &mut Item), With<Player>>,
|
||||||
|
mut vis_query: Query<&mut Visibility>,
|
||||||
|
mut item_transform: Query<&mut Transform, (With<Interact>, Without<Player>)>,
|
||||||
|
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::<ColliderDisabled>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn spawn(mut commands: Commands, models: Res<Assets<Gltf>>, gltf_assets: Res<GltfAssets>) {
|
// for simplicities sake, set the transform of the item that is dropped to that of the player
|
||||||
let hammer = gltf_assets
|
let mut item_transform = item_transform.get_mut(item).unwrap();
|
||||||
.library
|
*item_transform = *transform;
|
||||||
.get("meshes/library/hammer.glb")
|
}
|
||||||
.unwrap();
|
}
|
||||||
|
|
||||||
let hammer = models.get(hammer).unwrap();
|
pub fn handle_door_interaction(
|
||||||
let asset = hammer.default_scene.as_ref().unwrap();
|
player_query: Query<&PlayerAction, With<Player>>, mut doors: Query<&mut Door>
|
||||||
// hammer
|
) {
|
||||||
commands
|
let Ok(action) = player_query.get_single() else {
|
||||||
.spawn((
|
return;
|
||||||
Transform::from_xyz(0.0, 100.0, 0.0).with_scale(Vec3::splat(0.1)),
|
};
|
||||||
Interact,
|
if *action != PlayerAction::OpenDoor {
|
||||||
RigidBody::Dynamic,
|
return;
|
||||||
SceneRoot(asset.clone()),
|
}
|
||||||
))
|
|
||||||
.with_children(|parent| {
|
for mut door in doors.iter_mut() {
|
||||||
parent
|
door.is_open = !door.is_open;
|
||||||
.spawn(Collider::cuboid(0.8, 10f32, 0.8))
|
}
|
||||||
.insert(Transform::from_xyz(0.0, -5.0, 0.0));
|
|
||||||
parent
|
|
||||||
.spawn(Collider::cuboid(1.0, 1.0, 4.5))
|
|
||||||
// Position the collider relative to the rigid-body.
|
|
||||||
.insert(Transform::from_xyz(0.0, 4.2, 1.0));
|
|
||||||
});
|
|
||||||
|
|
||||||
//tools
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +1,92 @@
|
|||||||
use crate::GameState;
|
use crate::GameState;
|
||||||
use crate::player::{Player, PlayerAction};
|
use crate::player::Player;
|
||||||
use crate::util::{single, single_mut};
|
use crate::util::single;
|
||||||
use bevy::{prelude::*, window::PrimaryWindow};
|
use bevy::{prelude::*, window::PrimaryWindow};
|
||||||
use bevy_egui::{EguiContexts, EguiPlugin, egui};
|
use bevy_egui::{EguiContexts, EguiPlugin, egui};
|
||||||
use std::f32::consts::TAU;
|
use bevy_rapier3d::prelude::*;
|
||||||
|
use bevy_rapier3d::rapier::prelude::CollisionEventFlags;
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use super::objects::Interact;
|
use super::{Interact, objects};
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
pub(super) fn plugin(app: &mut App) {
|
||||||
app.add_plugins(EguiPlugin)
|
app.add_plugins(EguiPlugin)
|
||||||
.init_resource::<InteractionOpportunity>()
|
.init_resource::<InteractionOpportunity>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(update_interaction_opportunities, display_interaction_prompt)
|
(
|
||||||
|
update_interaction_opportunities,
|
||||||
|
display_interaction_prompt,
|
||||||
|
(objects::handle_pick_up, objects::handle_drop, objects::handle_door_interaction),
|
||||||
|
)
|
||||||
.chain()
|
.chain()
|
||||||
.run_if(in_state(GameState::Playing)),
|
.run_if(in_state(GameState::Playing)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Resource, Default)]
|
#[derive(Debug, Clone, Eq, PartialEq, Resource, Default)]
|
||||||
struct InteractionOpportunity(Option<Entity>);
|
pub struct InteractionOpportunity(pub Option<Entity>);
|
||||||
|
|
||||||
fn update_interaction_opportunities(
|
fn update_interaction_opportunities(
|
||||||
player_query: Query<&GlobalTransform, With<Player>>,
|
mut collision_events: EventReader<CollisionEvent>,
|
||||||
|
player_query: Query<Entity, With<Player>>,
|
||||||
parents: Query<&Parent>,
|
parents: Query<&Parent>,
|
||||||
target_query: Query<
|
target_query: Query<Entity, (Without<Player>, With<Interact>)>,
|
||||||
(Entity, &GlobalTransform),
|
|
||||||
(Without<Player>, Without<Camera>, With<Interact>),
|
|
||||||
>,
|
|
||||||
mut interaction_opportunity: ResMut<InteractionOpportunity>,
|
mut interaction_opportunity: ResMut<InteractionOpportunity>,
|
||||||
) {
|
) {
|
||||||
interaction_opportunity.0 = None;
|
let player = single!(player_query);
|
||||||
let player_transform = single!(player_query);
|
|
||||||
|
|
||||||
let (target_entity, target_transform) = single!(target_query);
|
for event in collision_events.read() {
|
||||||
|
let (e1, e2, started) = match event {
|
||||||
|
CollisionEvent::Started(e1, e2, CollisionEventFlags::SENSOR) => (*e1, *e2, true),
|
||||||
|
CollisionEvent::Stopped(e1, e2, CollisionEventFlags::SENSOR) => (*e1, *e2, false),
|
||||||
|
_ => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let player_translation = player_transform.translation();
|
let sensor = match player {
|
||||||
let target_translation = target_transform.translation();
|
p if p == e1 => e2,
|
||||||
if player_translation.distance(target_translation) <= 2.0 {
|
p if p == e2 => e1,
|
||||||
interaction_opportunity.0.replace(target_entity);
|
_ => continue,
|
||||||
|
};
|
||||||
|
let mut ancestors = iter::once(sensor).chain(parents.iter_ancestors(sensor));
|
||||||
|
|
||||||
|
let Some(interactable) = ancestors.find_map(|entity| target_query.get(entity).ok()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if started {
|
||||||
|
interaction_opportunity.0.replace(interactable);
|
||||||
|
} else {
|
||||||
|
interaction_opportunity.0.take_if(|t| *t == interactable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_facing_target(
|
|
||||||
player: Vec3,
|
|
||||||
target: Vec3,
|
|
||||||
camera_transform: Transform,
|
|
||||||
camera: &Camera,
|
|
||||||
) -> bool {
|
|
||||||
let camera_to_player = camera_transform.forward();
|
|
||||||
let player_to_target = target - player;
|
|
||||||
let angle = camera_to_player.angle_between(player_to_target);
|
|
||||||
angle < TAU / 8.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_interaction_prompt(
|
fn display_interaction_prompt(
|
||||||
interaction_opportunity: Res<InteractionOpportunity>,
|
interaction_opportunity: Res<InteractionOpportunity>,
|
||||||
mut egui_contexts: EguiContexts,
|
mut egui_contexts: EguiContexts,
|
||||||
action: Query<&PlayerAction, With<Player>>,
|
|
||||||
primary_windows: Query<&Window, With<PrimaryWindow>>,
|
primary_windows: Query<&Window, With<PrimaryWindow>>,
|
||||||
|
names: Query<&Name, With<Interact>>, // only the interactables ofcourse
|
||||||
) {
|
) {
|
||||||
let Some(opportunity) = interaction_opportunity.0 else {
|
let Some(opportunity) = interaction_opportunity.0 else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let window = single!(primary_windows);
|
let window = single!(primary_windows);
|
||||||
|
|
||||||
|
let entity_name = names
|
||||||
|
.get(opportunity)
|
||||||
|
.map(|name| name.as_str())
|
||||||
|
.expect("A named Interactable object");
|
||||||
|
|
||||||
|
// objective or item
|
||||||
egui::Window::new("Interaction")
|
egui::Window::new("Interaction")
|
||||||
.collapsible(false)
|
.collapsible(false)
|
||||||
.title_bar(false)
|
.title_bar(false)
|
||||||
.auto_sized()
|
.auto_sized()
|
||||||
.fixed_pos(egui::Pos2::new(window.width() / 2., window.height() / 2.))
|
.fixed_pos(egui::Pos2::new(window.width() / 2., window.height() / 2.))
|
||||||
.show(egui_contexts.ctx_mut(), |ui| {
|
.show(egui_contexts.ctx_mut(), |ui| {
|
||||||
ui.label("E: Pick Up");
|
ui.label(format!("E: Pick Up {entity_name}"));
|
||||||
});
|
});
|
||||||
|
|
||||||
let action = single!(action);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_rapier3d::prelude::{Collider, RigidBody};
|
use bevy_rapier3d::prelude::*;
|
||||||
use rand::Rng;
|
use rand::{Rng, seq::IteratorRandom};
|
||||||
|
|
||||||
use crate::{asset_loading::GltfAssets, GameState};
|
use crate::{
|
||||||
|
asset_loading::{GltfAssets, ImageAssets}, interaction::Interact, player::{toolbar::ItemIcon, Player, PlayerAction}, GameState
|
||||||
|
};
|
||||||
|
|
||||||
pub fn map_plugin(app: &mut App) {
|
pub fn map_plugin(app: &mut App) {
|
||||||
app.add_systems(OnEnter(GameState::Playing), spawn_level);
|
app.add_systems(
|
||||||
|
OnEnter(GameState::Playing),
|
||||||
|
(spawn_level, spawn_objects, spawn_doors).chain(),
|
||||||
|
);
|
||||||
|
app.add_systems(Update, handle_door_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_level(
|
fn spawn_level(
|
||||||
@@ -15,42 +21,77 @@ fn spawn_level(
|
|||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
models: Res<Assets<Gltf>>,
|
models: Res<Assets<Gltf>>,
|
||||||
gltf_assets: Res<GltfAssets>
|
gltf_assets: Res<GltfAssets>,
|
||||||
) {
|
) {
|
||||||
println!("LIBRARY: {:?}", gltf_assets.library);
|
println!("LIBRARY: {:?}", gltf_assets.library);
|
||||||
let mesh_names = ["corner_inside", "corner_outside", "wall", "door", "round_door", "round_hole"];
|
let mesh_names = [
|
||||||
|
"corner_inside",
|
||||||
|
"corner_outside",
|
||||||
|
"wall",
|
||||||
|
"door",
|
||||||
|
"round_door",
|
||||||
|
"round_hole",
|
||||||
|
];
|
||||||
let shapes = mesh_names.map(|mesh_name| {
|
let shapes = mesh_names.map(|mesh_name| {
|
||||||
let collider: Vec<(Collider, Transform)> = match mesh_name {
|
let collider: Vec<(Collider, Transform)> = match mesh_name {
|
||||||
"corner_inside" => vec![
|
"corner_inside" => vec![(
|
||||||
(Collider::cuboid(0.2, 1.0, 0.2), Transform::from_xyz(1.0, 1.0, 1.0).with_rotation(Quat::from_rotation_y(45.0_f32.to_radians())))
|
Collider::cuboid(0.2, 1.0, 0.2),
|
||||||
],
|
Transform::from_xyz(1.0, 1.0, 1.0)
|
||||||
|
.with_rotation(Quat::from_rotation_y(45.0_f32.to_radians())),
|
||||||
|
)],
|
||||||
"corner_outside" => vec![
|
"corner_outside" => vec![
|
||||||
(Collider::cuboid(1.0, 1.0, 0.1), Transform::from_xyz(0.0, 1.0, -1.0)),
|
(
|
||||||
(Collider::cuboid(1.0, 1.0, 0.1), Transform::from_xyz(-1.0, 1.0, 0.0)
|
Collider::cuboid(1.0, 1.0, 0.1),
|
||||||
.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
Transform::from_xyz(0.0, 1.0, -1.0),
|
||||||
],
|
),
|
||||||
"wall" => vec![
|
(
|
||||||
(Collider::cuboid(1.0, 1.0, 0.1), Transform::from_xyz(0.0, 1.0, -1.0))
|
Collider::cuboid(1.0, 1.0, 0.1),
|
||||||
],
|
Transform::from_xyz(-1.0, 1.0, 0.0)
|
||||||
"door" => vec![
|
.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
(Collider::cuboid(1.0, 1.0, 0.1), Transform::from_xyz(0.0, 1.0, -1.0))
|
),
|
||||||
],
|
|
||||||
"round_door" => vec![
|
|
||||||
(Collider::cuboid(1.0, 1.0, 0.1), Transform::from_xyz(0.0, 1.0, -1.0))
|
|
||||||
],
|
],
|
||||||
|
"wall" => vec![(
|
||||||
|
Collider::cuboid(1.0, 1.0, 0.1),
|
||||||
|
Transform::from_xyz(0.0, 1.0, -1.0),
|
||||||
|
)],
|
||||||
|
"door" => vec![(
|
||||||
|
Collider::cuboid(1.0, 1.0, 0.1),
|
||||||
|
Transform::from_xyz(0.0, 1.0, -1.0),
|
||||||
|
)],
|
||||||
|
"round_door" => vec![(
|
||||||
|
Collider::cuboid(1.0, 1.0, 0.1),
|
||||||
|
Transform::from_xyz(0.0, 1.0, -1.0),
|
||||||
|
)],
|
||||||
"round_hole" => vec![
|
"round_hole" => vec![
|
||||||
(Collider::cuboid(0.2, 1.0, 0.2), Transform::from_xyz(1.0, 1.0, -1.0).with_rotation(Quat::from_rotation_y(45.0_f32.to_radians()))),
|
(
|
||||||
(Collider::cuboid(0.2, 1.0, 0.2), Transform::from_xyz(-1.0, 1.0, -1.0).with_rotation(Quat::from_rotation_y(45.0_f32.to_radians())))
|
Collider::cuboid(0.2, 1.0, 0.2),
|
||||||
],
|
Transform::from_xyz(1.0, 1.0, -1.0)
|
||||||
_ => vec![
|
.with_rotation(Quat::from_rotation_y(45.0_f32.to_radians())),
|
||||||
(Collider::cuboid(1.0, 0.1, 1.0), Transform::from_xyz(0.0, 0.0, 0.0))
|
),
|
||||||
|
(
|
||||||
|
Collider::cuboid(0.2, 1.0, 0.2),
|
||||||
|
Transform::from_xyz(-1.0, 1.0, -1.0)
|
||||||
|
.with_rotation(Quat::from_rotation_y(45.0_f32.to_radians())),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
|
_ => vec![(
|
||||||
|
Collider::cuboid(1.0, 0.1, 1.0),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
)],
|
||||||
};
|
};
|
||||||
let path = format!("meshes/library/space_{}.glb", mesh_name);
|
let path = format!("meshes/library/space_{}.glb", mesh_name);
|
||||||
let handle = gltf_assets.library.get(&path).expect(&format!("Couldn't find {} in library", mesh_name));
|
let handle = gltf_assets
|
||||||
let gltf = models.get(handle).expect(&format!("No model for {}", mesh_name));
|
.library
|
||||||
|
.get(&path)
|
||||||
|
.expect(&format!("Couldn't find {} in library", mesh_name));
|
||||||
|
let gltf = models
|
||||||
|
.get(handle)
|
||||||
|
.expect(&format!("No model for {}", mesh_name));
|
||||||
|
|
||||||
let asset = gltf.default_scene.as_ref().expect(&format!("No scene in {}", mesh_name));
|
let asset = gltf
|
||||||
|
.default_scene
|
||||||
|
.as_ref()
|
||||||
|
.expect(&format!("No scene in {}", mesh_name));
|
||||||
(SceneRoot(asset.clone()), collider)
|
(SceneRoot(asset.clone()), collider)
|
||||||
});
|
});
|
||||||
let [
|
let [
|
||||||
@@ -59,10 +100,9 @@ fn spawn_level(
|
|||||||
wall,
|
wall,
|
||||||
door,
|
door,
|
||||||
round_door,
|
round_door,
|
||||||
round_hole
|
round_hole,
|
||||||
] = shapes.clone();
|
] = shapes.clone();
|
||||||
|
|
||||||
|
|
||||||
// huge floor
|
// huge floor
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
RigidBody::Fixed,
|
RigidBody::Fixed,
|
||||||
@@ -77,147 +117,239 @@ fn spawn_level(
|
|||||||
// ));
|
// ));
|
||||||
// let map = GameMap::test();
|
// let map = GameMap::test();
|
||||||
|
|
||||||
let maps = create_maps(3);
|
let levels = create_levels(3);
|
||||||
|
|
||||||
for map in maps {
|
for level in &levels {
|
||||||
for ((x, z), node) in map.nodes.into_iter() {
|
for ((x, z), node) in level.nodes.iter() {
|
||||||
let values = {
|
let values = {
|
||||||
// corners are handled later, for now just a wall to test
|
// corners are handled later, for now just a wall to test
|
||||||
let node = node.clone();
|
let node = node.clone();
|
||||||
let pos = Transform::from_xyz(2.0*x as f32, 0.0, -2.0*z as f32);
|
|
||||||
|
|
||||||
use Side::{Connection, Closed};
|
let pos = Transform::from_xyz(2.0 * (*x) as f32, 0.0, -2.0 * (*z) as f32);
|
||||||
|
use Side::{Closed, Connection};
|
||||||
match (node.north, node.east, node.south, node.west) {
|
match (node.north, node.east, node.south, node.west) {
|
||||||
// hallway horizontal
|
// hallway horizontal
|
||||||
(Closed, Connection, Closed, Connection) => {
|
(Closed, Connection, Closed, Connection) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos),
|
(wall.clone(), pos),
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())))
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
// hallway vertical
|
// hallway vertical
|
||||||
(Connection, Closed, Connection, Closed) => {
|
(Connection, Closed, Connection, Closed) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())))
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// dead ends
|
// dead ends
|
||||||
(Connection, Closed, Closed, Closed) => {
|
(Connection, Closed, Closed, Closed) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(round_door.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians()))),
|
wall.clone(),
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())))
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
round_door.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
(Closed, Closed, Connection, Closed) => {
|
(Closed, Closed, Connection, Closed) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(round_door.clone(), pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians()))),
|
wall.clone(),
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())))
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
round_door.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
(Closed, Connection, Closed, Closed) => {
|
(Closed, Connection, Closed, Closed) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos),
|
(wall.clone(), pos),
|
||||||
(round_door.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())))
|
round_door.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
(Closed, Closed, Closed, Connection) => {
|
(Closed, Closed, Closed, Connection) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos),
|
(wall.clone(), pos),
|
||||||
(round_door.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians()))),
|
(
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())))
|
round_door.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
// T hallways
|
// T hallways
|
||||||
(Closed, Connection, Connection, Connection) => {
|
(Closed, Connection, Connection, Connection) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos),
|
(wall.clone(), pos),
|
||||||
(round_hole.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())))
|
(
|
||||||
|
round_hole.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
(Connection, Closed, Connection, Connection) => {
|
(Connection, Closed, Connection, Connection) => {
|
||||||
vec![
|
vec![
|
||||||
(round_hole.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())))
|
round_hole.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
(Connection, Connection, Closed, Connection) => {
|
(Connection, Connection, Closed, Connection) => {
|
||||||
vec![
|
vec![
|
||||||
(round_hole.clone(), pos),
|
(round_hole.clone(), pos),
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())))
|
(
|
||||||
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
(Connection, Connection, Connection, Closed) => {
|
(Connection, Connection, Connection, Closed) => {
|
||||||
vec![
|
vec![
|
||||||
(wall.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(round_hole.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())))
|
wall.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
round_hole.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
// fourway
|
// fourway
|
||||||
(Connection, Connection, Connection, Connection) => {
|
(Connection, Connection, Connection, Connection) => {
|
||||||
vec![
|
vec![
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians()))),
|
(
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
corner_inside.clone(),
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians()))),
|
pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians())),
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())))
|
),
|
||||||
|
(
|
||||||
|
corner_inside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
corner_inside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
corner_inside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
// corners
|
// corners
|
||||||
(Connection, Connection, Closed, Closed) => {
|
(Connection, Connection, Closed, Closed) => {
|
||||||
vec![
|
vec![
|
||||||
(corner_outside.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
(
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))),
|
corner_outside.clone(),
|
||||||
]
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
},
|
),
|
||||||
(Closed, Connection, Connection, Closed) => {
|
(
|
||||||
vec![
|
corner_inside.clone(),
|
||||||
(corner_outside.clone(), pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians()))),
|
pos.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians())),
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians()))),
|
),
|
||||||
]
|
|
||||||
},
|
|
||||||
(Closed, Closed, Connection, Connection) => {
|
|
||||||
vec![
|
|
||||||
(corner_outside.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians()))),
|
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians()))),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
(Connection, Closed, Closed, Connection) => {
|
|
||||||
vec![
|
|
||||||
(corner_outside.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians()))),
|
|
||||||
(corner_inside.clone(), pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians()))),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
_ => vec![]
|
(Closed, Connection, Connection, Closed) => {
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
corner_outside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
corner_inside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(0.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
(Closed, Closed, Connection, Connection) => {
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
corner_outside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
corner_inside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(270.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
(Connection, Closed, Closed, Connection) => {
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
corner_outside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
corner_inside.clone(),
|
||||||
|
pos.with_rotation(Quat::from_rotation_y(180.0_f32.to_radians())),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
_ => vec![],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for ((scene_root, colliders), pos) in values {
|
for ((scene_root, colliders), pos) in values {
|
||||||
commands.spawn((
|
commands
|
||||||
RigidBody::Fixed,
|
.spawn((RigidBody::Fixed, scene_root, pos))
|
||||||
scene_root,
|
.with_children(|parent| {
|
||||||
pos,
|
for (collider, transform) in colliders {
|
||||||
)).with_children(|parent| {
|
parent.spawn((collider, transform));
|
||||||
for (collider, transform) in colliders {
|
}
|
||||||
parent.spawn((collider, transform));
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (x,z) = map.end_node;
|
let (x, z) = level.end_node;
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh3d(meshes.add(Cuboid::new(1.0, 20.0, 1.0))),
|
Mesh3d(meshes.add(Cuboid::new(1.0, 20.0, 1.0))),
|
||||||
MeshMaterial3d(materials.add(Color::srgb_u8(255, 0, 0))),
|
MeshMaterial3d(materials.add(Color::srgb_u8(255, 0, 0))),
|
||||||
Transform::from_xyz(2.0*x as f32, 0.5, -2.0*z as f32),
|
Transform::from_xyz(2.0 * x as f32, 0.5, -2.0 * z as f32),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
commands.insert_resource(GameLevels { levels });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_maps(n: i32) -> Vec<GameMap> {
|
fn create_levels(n: i32) -> Vec<GameLevel> {
|
||||||
let mut maps = Vec::new();
|
let mut maps = Vec::new();
|
||||||
let mut initial_node = MapNode::new();
|
let mut initial_node = LevelNode::new();
|
||||||
initial_node.east = Side::Closed;
|
initial_node.east = Side::Closed;
|
||||||
initial_node.south = Side::Closed;
|
initial_node.south = Side::Closed;
|
||||||
initial_node.west = Side::Closed;
|
initial_node.west = Side::Closed;
|
||||||
@@ -225,15 +357,15 @@ fn create_maps(n: i32) -> Vec<GameMap> {
|
|||||||
|
|
||||||
let pos = (0, 0);
|
let pos = (0, 0);
|
||||||
|
|
||||||
let map = GameMap::new(pos, initial_node.clone(), 5);
|
let map = GameLevel::new(pos, initial_node.clone(), 5);
|
||||||
|
|
||||||
maps.push(map);
|
maps.push(map);
|
||||||
|
|
||||||
for _ in 0..n-1 {
|
for _ in 0..n - 1 {
|
||||||
let map = maps.last().unwrap();
|
let map = maps.last().unwrap();
|
||||||
let mut pos = map.end_node.clone();
|
let mut pos = map.end_node.clone();
|
||||||
|
|
||||||
let mut next_node = MapNode::new();
|
let mut next_node = LevelNode::new();
|
||||||
next_node.east = Side::Connection;
|
next_node.east = Side::Connection;
|
||||||
next_node.south = Side::Connection;
|
next_node.south = Side::Connection;
|
||||||
next_node.west = Side::Connection;
|
next_node.west = Side::Connection;
|
||||||
@@ -250,7 +382,7 @@ fn create_maps(n: i32) -> Vec<GameMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initial_node = next_node;
|
initial_node = next_node;
|
||||||
let map = GameMap::new(pos, initial_node.clone(), 5);
|
let map = GameLevel::new(pos, initial_node.clone(), 5);
|
||||||
maps.push(map);
|
maps.push(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,16 +397,16 @@ enum Side {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct MapNode {
|
struct LevelNode {
|
||||||
north: Side,
|
north: Side,
|
||||||
south: Side,
|
south: Side,
|
||||||
east: Side,
|
east: Side,
|
||||||
west: Side,
|
west: Side,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MapNode {
|
impl LevelNode {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
MapNode {
|
LevelNode {
|
||||||
north: Side::Empty,
|
north: Side::Empty,
|
||||||
south: Side::Empty,
|
south: Side::Empty,
|
||||||
east: Side::Empty,
|
east: Side::Empty,
|
||||||
@@ -283,60 +415,70 @@ impl MapNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GameMap {
|
#[derive(Resource)]
|
||||||
nodes: HashMap<(i32, i32), MapNode>,
|
struct GameLevels {
|
||||||
end_node: (i32, i32),
|
pub levels: Vec<GameLevel>,
|
||||||
grid_size: i32,
|
|
||||||
initial_point: (i32, i32)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameMap {
|
struct GameLevel {
|
||||||
fn new(initial_point: (i32, i32), node: MapNode, grid_size: i32) -> Self {
|
nodes: HashMap<(i32, i32), LevelNode>,
|
||||||
|
end_node: (i32, i32),
|
||||||
|
grid_size: i32,
|
||||||
|
initial_point: (i32, i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameLevel {
|
||||||
|
fn new(initial_point: (i32, i32), node: LevelNode, grid_size: i32) -> Self {
|
||||||
let mut nodes = HashMap::new();
|
let mut nodes = HashMap::new();
|
||||||
nodes.insert(initial_point, node);
|
nodes.insert(initial_point, node);
|
||||||
let mut m = GameMap { initial_point, nodes, grid_size, end_node: initial_point};
|
let mut m = GameLevel {
|
||||||
|
initial_point,
|
||||||
|
nodes,
|
||||||
|
grid_size,
|
||||||
|
end_node: initial_point,
|
||||||
|
};
|
||||||
m.generate_map();
|
m.generate_map();
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_map(&mut self) {
|
fn generate_map(&mut self) {
|
||||||
let mut first_point = self.initial_point.clone();
|
let mut first_point = self.initial_point.clone();
|
||||||
first_point.1 += 1;
|
first_point.1 += 1;
|
||||||
self.create_node(first_point); // North
|
self.create_node(first_point); // North
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pos_within_boundaries(&self, pos: (i32, i32)) -> bool {
|
fn pos_within_boundaries(&self, pos: (i32, i32)) -> bool {
|
||||||
pos.0 > self.grid_size + self.initial_point.0
|
pos.0 > self.grid_size + self.initial_point.0
|
||||||
|| pos.1 > self.grid_size + self.initial_point.1
|
|| pos.1 > self.grid_size + self.initial_point.1
|
||||||
|| pos.0 < self.initial_point.0
|
|| pos.0 < self.initial_point.0
|
||||||
|| pos.1 < self.initial_point.1
|
|| pos.1 < self.initial_point.1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn choose_side(&self, pos: (i32, i32)) -> Side {
|
fn choose_side(&self, pos: (i32, i32)) -> Side {
|
||||||
if self.pos_within_boundaries(pos) {
|
if self.pos_within_boundaries(pos) {
|
||||||
Side::Closed
|
Side::Closed
|
||||||
} else {
|
} else {
|
||||||
if rand::rng().random_bool(0.5) { Side::Connection } else { Side::Closed }
|
if rand::rng().random_bool(0.5) {
|
||||||
|
Side::Connection
|
||||||
|
} else {
|
||||||
|
Side::Closed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_connection(&self, node: &mut MapNode, (x, y): (i32, i32)) {
|
fn ensure_connection(&self, node: &mut LevelNode, (x, y): (i32, i32)) {
|
||||||
let am_connections = (node.north == Side::Connection) as u8 +
|
let am_connections = (node.north == Side::Connection) as u8
|
||||||
(node.south == Side::Connection) as u8 +
|
+ (node.south == Side::Connection) as u8
|
||||||
(node.east == Side::Connection) as u8 +
|
+ (node.east == Side::Connection) as u8
|
||||||
(node.west == Side::Connection) as u8;
|
+ (node.west == Side::Connection) as u8;
|
||||||
|
|
||||||
if am_connections <= 2 {
|
if am_connections <= 2 {
|
||||||
if node.north != Side::Connection && !self.nodes.contains_key(&(x, y + 1)) {
|
if node.north != Side::Connection && !self.nodes.contains_key(&(x, y + 1)) {
|
||||||
node.north = Side::Connection
|
node.north = Side::Connection
|
||||||
}
|
} else if node.south != Side::Connection && !self.nodes.contains_key(&(x, y - 1)) {
|
||||||
else if node.south != Side::Connection && !self.nodes.contains_key(&(x, y - 1)) {
|
|
||||||
node.south = Side::Connection
|
node.south = Side::Connection
|
||||||
}
|
} else if node.east != Side::Connection && !self.nodes.contains_key(&(x + 1, y)) {
|
||||||
else if node.east != Side::Connection && !self.nodes.contains_key(&(x + 1, y)) {
|
|
||||||
node.east = Side::Connection
|
node.east = Side::Connection
|
||||||
}
|
} else if node.west != Side::Connection && !self.nodes.contains_key(&(x - 1, y)) {
|
||||||
else if node.west != Side::Connection && !self.nodes.contains_key(&(x - 1, y)) {
|
|
||||||
node.west = Side::Connection
|
node.west = Side::Connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,7 +493,7 @@ impl GameMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (x, y) = current_idx;
|
let (x, y) = current_idx;
|
||||||
let mut new_node = MapNode::new();
|
let mut new_node = LevelNode::new();
|
||||||
|
|
||||||
if x >= self.end_node.0 && y >= self.end_node.1 {
|
if x >= self.end_node.0 && y >= self.end_node.1 {
|
||||||
self.end_node = (x, y);
|
self.end_node = (x, y);
|
||||||
@@ -389,9 +531,10 @@ impl GameMap {
|
|||||||
let end_node = self.nodes.get_mut(&self.end_node).unwrap();
|
let end_node = self.nodes.get_mut(&self.end_node).unwrap();
|
||||||
if self.end_node.1 >= self.end_node.0 {
|
if self.end_node.1 >= self.end_node.0 {
|
||||||
end_node.north = Side::Connection;
|
end_node.north = Side::Connection;
|
||||||
}
|
end_node.south = Side::Connection;
|
||||||
else if self.end_node.0 >= self.end_node.1 {
|
} else {
|
||||||
end_node.east = Side::Connection;
|
end_node.east = Side::Connection;
|
||||||
|
end_node.west = Side::Connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,3 +562,128 @@ impl GameMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spawn_objects(
|
||||||
|
mut commands: Commands,
|
||||||
|
levels: Res<GameLevels>,
|
||||||
|
models: Res<Assets<Gltf>>,
|
||||||
|
gltf_assets: Res<GltfAssets>,
|
||||||
|
image_assets: Res<ImageAssets>,
|
||||||
|
) {
|
||||||
|
// id card
|
||||||
|
let card = models.get(&gltf_assets.card).unwrap();
|
||||||
|
let asset = card.default_scene.as_ref().unwrap();
|
||||||
|
|
||||||
|
for (i, level) in levels.levels.iter().enumerate() {
|
||||||
|
let begin_node = level.initial_point;
|
||||||
|
let end_node = level.end_node;
|
||||||
|
// take a random position that is not the beginning or end_node
|
||||||
|
let (x, z) = loop {
|
||||||
|
let positions = level.nodes.keys();
|
||||||
|
let random_pos = positions.choose(&mut rand::rng()).unwrap().clone();
|
||||||
|
if random_pos != begin_node && random_pos != end_node {
|
||||||
|
break random_pos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let transform = if x <= z {
|
||||||
|
Transform::from_xyz(2.0 * x as f32, 0.5, -2.0 * z as f32)
|
||||||
|
.with_rotation(Quat::from_rotation_y(90.0_f32.to_radians()))
|
||||||
|
} else {
|
||||||
|
Transform::from_xyz(2.0 * x as f32, 0.5, -2.0 * z as f32)
|
||||||
|
};
|
||||||
|
commands
|
||||||
|
.spawn((
|
||||||
|
transform,
|
||||||
|
Interact,
|
||||||
|
RigidBody::Dynamic,
|
||||||
|
Name::new(format!("Id Card {i}")),
|
||||||
|
Visibility::Visible,
|
||||||
|
ItemIcon::new(image_assets.id_card.clone()),
|
||||||
|
SceneRoot(asset.clone()),
|
||||||
|
))
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.spawn((
|
||||||
|
ColliderMassProperties::Mass(10.0),
|
||||||
|
Collider::cuboid(0.05, 0.05, 0.01),
|
||||||
|
Transform::from_rotation(Quat::from_euler(
|
||||||
|
EulerRot::XYZ,
|
||||||
|
-10.0f32.to_radians(), // X-axis rotation (tilt)
|
||||||
|
-5.0f32.to_radians(), // Y-axis rotation
|
||||||
|
10.0f32.to_radians(), // Z-axis rotation
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
parent.spawn((
|
||||||
|
ActiveEvents::COLLISION_EVENTS,
|
||||||
|
Transform::default(),
|
||||||
|
Collider::ball(0.5), // Interaction radius
|
||||||
|
Sensor,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Door {
|
||||||
|
pub is_open: bool,
|
||||||
|
pub open_direction: (i32, i32),
|
||||||
|
pub position: (i32, i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_doors(
|
||||||
|
mut commands: Commands,
|
||||||
|
levels: Res<GameLevels>,
|
||||||
|
models: Res<Assets<Gltf>>,
|
||||||
|
gltf_assets: Res<GltfAssets>,
|
||||||
|
) {
|
||||||
|
let collider: Vec<(Collider, Transform)> = vec![(
|
||||||
|
Collider::cuboid(1.0, 1.0, 0.1),
|
||||||
|
Transform::from_xyz(0.0, 1.0, -1.0),
|
||||||
|
)];
|
||||||
|
let path = format!("meshes/library/space_round_door.glb",);
|
||||||
|
let handle = gltf_assets.library.get(&path).unwrap();
|
||||||
|
let gltf = models.get(handle).unwrap();
|
||||||
|
|
||||||
|
let asset = gltf.default_scene.as_ref().unwrap();
|
||||||
|
let scene_root = SceneRoot(asset.clone());
|
||||||
|
|
||||||
|
for level in levels.levels.iter() {
|
||||||
|
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()))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
(1, 0),
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_door_pos(mut query: Query<(&Door, &mut Transform)>) {
|
||||||
|
for (door, mut transform) in query.iter_mut() {
|
||||||
|
if door.is_open {
|
||||||
|
transform.translation.y = 2.0
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
transform.translation.y = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use bevy_rapier3d::prelude::*;
|
|||||||
|
|
||||||
mod asset_loading;
|
mod asset_loading;
|
||||||
mod bevy_plugin;
|
mod bevy_plugin;
|
||||||
|
mod debugging;
|
||||||
mod interaction;
|
mod interaction;
|
||||||
mod level_instantiation;
|
mod level_instantiation;
|
||||||
mod main_menu;
|
mod main_menu;
|
||||||
@@ -51,7 +52,7 @@ fn setup(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
image: Res<ImageAssets>
|
image: Res<ImageAssets>,
|
||||||
) {
|
) {
|
||||||
// circular base
|
// circular base
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
|
|||||||
135
src/player.rs
135
src/player.rs
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
use bevy::{
|
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}
|
||||||
};
|
};
|
||||||
@@ -7,7 +6,10 @@ use bevy_rapier3d::prelude::*;
|
|||||||
|
|
||||||
pub mod toolbar;
|
pub mod toolbar;
|
||||||
|
|
||||||
use crate::{asset_loading::{AudioAssets, FlashlightAssets}, GameState};
|
use crate::{
|
||||||
|
GameState,
|
||||||
|
asset_loading::{AudioAssets, FlashlightAssets},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Component, Default)]
|
#[derive(Debug, Component, Default)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
@@ -53,7 +55,15 @@ impl Default for HeadBob {
|
|||||||
pub struct BaseTransform(pub Transform);
|
pub struct BaseTransform(pub Transform);
|
||||||
|
|
||||||
#[derive(Debug, Component)]
|
#[derive(Debug, Component)]
|
||||||
pub struct Flashlight;
|
pub struct Flashlight {
|
||||||
|
// 0 - 4
|
||||||
|
pub charge: f32,
|
||||||
|
pub is_on: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Component)]
|
||||||
|
pub struct SpotlightFlashlight {
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct FlashlightButtonAnimation {
|
pub struct FlashlightButtonAnimation {
|
||||||
@@ -70,7 +80,6 @@ impl Default for FlashlightButtonAnimation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins(toolbar::plugin)
|
app.add_plugins(toolbar::plugin)
|
||||||
.add_systems(OnEnter(GameState::Playing), (init_player, hide_cursor))
|
.add_systems(OnEnter(GameState::Playing), (init_player, hide_cursor))
|
||||||
@@ -82,8 +91,9 @@ pub fn plugin(app: &mut App) {
|
|||||||
apply_head_bob,
|
apply_head_bob,
|
||||||
on_resize_system,
|
on_resize_system,
|
||||||
handle_flashlight,
|
handle_flashlight,
|
||||||
update_flashlight_button_animation,
|
(update_flashlight_button_animation, update_flashlight_charge, update_flashlight_sprite).chain(),
|
||||||
).run_if(in_state(GameState::Playing)),
|
)
|
||||||
|
.run_if(in_state(GameState::Playing)),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
FixedUpdate,
|
FixedUpdate,
|
||||||
@@ -128,10 +138,10 @@ pub fn init_player(
|
|||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
DistanceFog {
|
DistanceFog {
|
||||||
color: Color::srgba(0.12, 0.08, 0.08, 0.65),
|
color: Color::srgba(0.12, 0.08, 0.08, 0.65),
|
||||||
falloff: FogFalloff::Linear {
|
falloff: FogFalloff::Linear {
|
||||||
start: 3.0,
|
start: 3.0,
|
||||||
end: 12.0,
|
end: 12.0,
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
@@ -149,6 +159,7 @@ pub fn init_player(
|
|||||||
let window = window.single();
|
let window = window.single();
|
||||||
let transform = flashlight_base_transform(window.width(), window.height());
|
let transform = flashlight_base_transform(window.width(), window.height());
|
||||||
parent.spawn((
|
parent.spawn((
|
||||||
|
Flashlight { charge: 4.0, is_on: false },
|
||||||
Sprite::from_image(flashlights.flash_hold_4.clone()),
|
Sprite::from_image(flashlights.flash_hold_4.clone()),
|
||||||
transform.0.clone(),
|
transform.0.clone(),
|
||||||
transform,
|
transform,
|
||||||
@@ -158,7 +169,7 @@ pub fn init_player(
|
|||||||
|
|
||||||
// feitelijke pitslamp
|
// feitelijke pitslamp
|
||||||
parent.spawn((
|
parent.spawn((
|
||||||
Flashlight,
|
SpotlightFlashlight{},
|
||||||
SpotLight {
|
SpotLight {
|
||||||
intensity: 0.0,
|
intensity: 0.0,
|
||||||
color: Color::srgba(0.9, 0.628, 0.392, 1.0),
|
color: Color::srgba(0.9, 0.628, 0.392, 1.0),
|
||||||
@@ -196,9 +207,11 @@ fn flashlight_base_transform(window_width: f32, window_height: f32) -> BaseTrans
|
|||||||
let xoffset = window_size.x / 4.0 - 40.0;
|
let xoffset = window_size.x / 4.0 - 40.0;
|
||||||
let yoffset = 15.0;
|
let yoffset = 15.0;
|
||||||
|
|
||||||
let mut transform = Transform::from_translation(
|
let mut transform = Transform::from_translation(Vec3::new(
|
||||||
Vec3::new(window_size.x / 2.0 - world_size.x / 2.0 - xoffset, -window_size.y / 2.0 + world_size.y / 2.0 - yoffset, 0.0)
|
window_size.x / 2.0 - world_size.x / 2.0 - xoffset,
|
||||||
);
|
-window_size.y / 2.0 + world_size.y / 2.0 - yoffset,
|
||||||
|
0.0,
|
||||||
|
));
|
||||||
transform.scale = Vec3::new(scale, scale, 1.0);
|
transform.scale = Vec3::new(scale, scale, 1.0);
|
||||||
return BaseTransform(transform);
|
return BaseTransform(transform);
|
||||||
}
|
}
|
||||||
@@ -237,8 +250,11 @@ pub(crate) enum PlayerAction {
|
|||||||
Move,
|
Move,
|
||||||
Sprint,
|
Sprint,
|
||||||
Jump,
|
Jump,
|
||||||
Interact,
|
ToggleFlashlight,
|
||||||
ToggleFlashlight
|
OpenDoor,
|
||||||
|
// Objects and stuff
|
||||||
|
PickUp,
|
||||||
|
Drop,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_input(
|
pub fn handle_input(
|
||||||
@@ -276,17 +292,25 @@ pub fn handle_input(
|
|||||||
player.speed_factor = 1.0;
|
player.speed_factor = 1.0;
|
||||||
}
|
}
|
||||||
if keyboard_input.just_pressed(KeyCode::KeyE) {
|
if keyboard_input.just_pressed(KeyCode::KeyE) {
|
||||||
*action = PlayerAction::Interact
|
*action = PlayerAction::PickUp;
|
||||||
|
}
|
||||||
|
if keyboard_input.just_pressed(KeyCode::KeyQ) {
|
||||||
|
*action = PlayerAction::Drop;
|
||||||
}
|
}
|
||||||
if keyboard_input.just_pressed(KeyCode::KeyF) {
|
if keyboard_input.just_pressed(KeyCode::KeyF) {
|
||||||
*action = PlayerAction::ToggleFlashlight;
|
*action = PlayerAction::ToggleFlashlight;
|
||||||
}
|
}
|
||||||
|
if keyboard_input.just_pressed(KeyCode::KeyK) {
|
||||||
|
*action = PlayerAction::OpenDoor;
|
||||||
|
}
|
||||||
|
|
||||||
input.movement_direction = movement_direction.normalize_or_zero();
|
input.movement_direction = movement_direction.normalize_or_zero();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_player_movement(mut player_query: Query<(&PlayerInput, &mut Velocity, &Player), With<Player>>) {
|
pub fn apply_player_movement(
|
||||||
|
mut player_query: Query<(&PlayerInput, &mut Velocity, &Player), With<Player>>,
|
||||||
|
) {
|
||||||
const SPEED: f32 = 2.6;
|
const SPEED: f32 = 2.6;
|
||||||
const JUMP_FORCE: f32 = 4.0;
|
const JUMP_FORCE: f32 = 4.0;
|
||||||
|
|
||||||
@@ -311,14 +335,18 @@ pub fn apply_head_bob(
|
|||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut query: Query<(&PlayerInput, &mut HeadBob, &Player), With<Player>>,
|
mut query: Query<(&PlayerInput, &mut HeadBob, &Player), With<Player>>,
|
||||||
mut camera_query: Query<&mut Transform, (With<WorldModelCamera>, Without<Player>)>,
|
mut camera_query: Query<&mut Transform, (With<WorldModelCamera>, Without<Player>)>,
|
||||||
mut sprite_query: Query<(&mut Transform, &Sprite, &BaseTransform), (With<Sprite>, Without<WorldModelCamera>, Without<Player>)>,
|
mut sprite_query: Query<
|
||||||
|
(&mut Transform, &Sprite, &BaseTransform),
|
||||||
|
(With<Sprite>, Without<WorldModelCamera>, Without<Player>),
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
let Ok((input, mut head_bob, player)) = query.get_single_mut() else {
|
let Ok((input, mut head_bob, player)) = query.get_single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// bob when moving horizontally
|
// bob when moving horizontally
|
||||||
let horizontal_movement = Vec3::new(input.movement_direction.x, 0.0, input.movement_direction.z);
|
let horizontal_movement =
|
||||||
|
Vec3::new(input.movement_direction.x, 0.0, input.movement_direction.z);
|
||||||
let is_moving = horizontal_movement.length_squared() > 0.01;
|
let is_moving = horizontal_movement.length_squared() > 0.01;
|
||||||
let bobbing_speed = head_bob.speed * player.speed_factor;
|
let bobbing_speed = head_bob.speed * player.speed_factor;
|
||||||
|
|
||||||
@@ -359,17 +387,17 @@ pub fn apply_head_bob(
|
|||||||
let scale_factor = 40.0 * player.speed_factor;
|
let scale_factor = 40.0 * player.speed_factor;
|
||||||
|
|
||||||
if is_moving {
|
if is_moving {
|
||||||
transform.translation.x = base_transform.0.translation.x + horizontal_offset * scale_factor;
|
transform.translation.x =
|
||||||
transform.translation.y = base_transform.0.translation.y + vertical_offset * scale_factor;
|
base_transform.0.translation.x + horizontal_offset * scale_factor;
|
||||||
|
transform.translation.y =
|
||||||
|
base_transform.0.translation.y + vertical_offset * scale_factor;
|
||||||
|
|
||||||
transform.rotation = Quat::from_euler(
|
transform.rotation =
|
||||||
EulerRot::XYZ,
|
Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, horizontal_offset * 0.1);
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
horizontal_offset * 0.1,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
transform.translation = transform.translation.lerp(base_transform.0.translation, 0.1);
|
transform.translation = transform
|
||||||
|
.translation
|
||||||
|
.lerp(base_transform.0.translation, 0.1);
|
||||||
transform.rotation = transform.rotation.slerp(Quat::IDENTITY, 0.1);
|
transform.rotation = transform.rotation.slerp(Quat::IDENTITY, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,7 +405,7 @@ pub fn apply_head_bob(
|
|||||||
}
|
}
|
||||||
pub fn handle_flashlight(
|
pub fn handle_flashlight(
|
||||||
player_query: Query<&PlayerAction, With<Player>>,
|
player_query: Query<&PlayerAction, With<Player>>,
|
||||||
mut flashlight_query: Query<&mut SpotLight, With<Flashlight>>,
|
mut flashlight_query: Query<&mut SpotLight, With<SpotlightFlashlight>>,
|
||||||
mut flashlight_sprite_query: Query<&mut FlashlightButtonAnimation>,
|
mut flashlight_sprite_query: Query<&mut FlashlightButtonAnimation>,
|
||||||
audio_assets: Res<AudioAssets>,
|
audio_assets: Res<AudioAssets>,
|
||||||
audio: Res<Audio>,
|
audio: Res<Audio>,
|
||||||
@@ -403,20 +431,59 @@ pub fn handle_flashlight(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_flashlight_charge(
|
||||||
|
time: Res<Time>,
|
||||||
|
mut flashlight_query: Query<&mut Flashlight>,
|
||||||
|
) {
|
||||||
|
for mut flashlight in flashlight_query.iter_mut() {
|
||||||
|
if flashlight.is_on {
|
||||||
|
flashlight.charge = flashlight.charge - time.delta_secs() * 0.1;
|
||||||
|
} else {
|
||||||
|
flashlight.charge = flashlight.charge + time.delta_secs() * 0.1;
|
||||||
|
}
|
||||||
|
flashlight.charge = flashlight.charge.clamp(0.0, 4.0);
|
||||||
|
if flashlight.charge <= 0.0 {
|
||||||
|
flashlight.is_on = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_flashlight_button_animation(
|
pub fn update_flashlight_button_animation(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut query: Query<(&mut FlashlightButtonAnimation, &mut Sprite)>,
|
mut query: Query<&mut FlashlightButtonAnimation>,
|
||||||
flashlights: Res<FlashlightAssets>,
|
|
||||||
) {
|
) {
|
||||||
for (mut animation, mut image) in query.iter_mut() {
|
for mut animation in query.iter_mut() {
|
||||||
if animation.is_pressed {
|
if animation.is_pressed {
|
||||||
animation.timer.tick(time.delta());
|
animation.timer.tick(time.delta());
|
||||||
if animation.timer.finished() {
|
if animation.timer.finished() {
|
||||||
*image = Sprite::from_image(flashlights.flash_hold_4.clone());
|
|
||||||
animation.is_pressed = false;
|
animation.is_pressed = false;
|
||||||
} else {
|
} else {
|
||||||
*image = Sprite::from_image(flashlights.flash_hold_4_pressed.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_flashlight_sprite(
|
||||||
|
mut query: Query<(&FlashlightButtonAnimation, &mut Sprite, &Flashlight)>,
|
||||||
|
flashlights: Res<FlashlightAssets>,
|
||||||
|
){
|
||||||
|
for (animation, mut image, flashlight) in query.iter_mut() {
|
||||||
|
println!("charge: {}", flashlight.charge);
|
||||||
|
let charge = flashlight.charge.ceil() as i32;
|
||||||
|
println!("charge: {}", charge);
|
||||||
|
let sprite_image = match (charge, animation.is_pressed) {
|
||||||
|
(4, true) => flashlights.flash_hold_4_pressed.clone(),
|
||||||
|
(4, false) => flashlights.flash_hold_4.clone(),
|
||||||
|
(3, true) => flashlights.flash_hold_3_pressed.clone(),
|
||||||
|
(3, false) => flashlights.flash_hold_3.clone(),
|
||||||
|
(2, true) => flashlights.flash_hold_2_pressed.clone(),
|
||||||
|
(2, false) => flashlights.flash_hold_2.clone(),
|
||||||
|
(1, true) => flashlights.flash_hold_1_pressed.clone(),
|
||||||
|
(1, false) => flashlights.flash_hold_1.clone(),
|
||||||
|
(0, true) => flashlights.flash_hold_0_pressed.clone(),
|
||||||
|
(0, false) => flashlights.flash_hold_0.clone(),
|
||||||
|
_ => flashlights.flash_hold_0.clone()
|
||||||
|
};
|
||||||
|
*image = Sprite::from_image(sprite_image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,108 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy_egui::{EguiContexts, egui};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
||||||
use crate::GameState;
|
use crate::{GameState, interaction::Interact, util::single};
|
||||||
|
|
||||||
use super::Player;
|
use super::Player;
|
||||||
|
|
||||||
#[derive(Component, Default, Debug)]
|
#[derive(Component, Default, Debug)]
|
||||||
pub struct Item(Option<Entity>);
|
pub struct Item(Option<Entity>);
|
||||||
|
|
||||||
|
impl Item {
|
||||||
|
pub fn take(&mut self) -> Option<Entity> {
|
||||||
|
self.0.take()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default, Debug)]
|
||||||
|
pub struct ItemIcon(Handle<Image>);
|
||||||
|
|
||||||
|
impl ItemIcon {
|
||||||
|
pub fn new(h: Handle<Image>) -> Self {
|
||||||
|
Self(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set item and return the current item
|
||||||
|
pub fn set_item(&mut self, item: Entity) -> Option<Entity> {
|
||||||
|
self.0.replace(item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(Update, show_toolbar.run_if(in_state(GameState::Playing)));
|
app.add_systems(Update, bottom_panel.run_if(in_state(GameState::Playing)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_toolbar(player_tool_query: Query<&Item, With<Player>>) {}
|
fn bottom_panel(
|
||||||
|
mut egui_ctx: EguiContexts,
|
||||||
|
player_item_query: Query<&Item, With<Player>>,
|
||||||
|
item_query: Query<(&Name, &ItemIcon), With<Interact>>,
|
||||||
|
) {
|
||||||
|
let item = single!(player_item_query);
|
||||||
|
let item = item.0.and_then(|id| item_query.get(id).ok());
|
||||||
|
let (name, icon) = item.map_or((Name::new("➖"), None), |(name, handle)| {
|
||||||
|
(
|
||||||
|
name.clone(),
|
||||||
|
Some(egui_ctx.add_image(handle.0.clone_weak())),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::TopBottomPanel::bottom("inventory_toolbar")
|
||||||
|
.frame(egui::Frame {
|
||||||
|
fill: egui::Color32::from_rgba_premultiplied(0, 0, 0, 0),
|
||||||
|
// Removed the stroke/border by setting it to none
|
||||||
|
stroke: egui::Stroke::NONE,
|
||||||
|
outer_margin: egui::epaint::Margin::same(0),
|
||||||
|
inner_margin: egui::epaint::Margin::same(0),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.show(egui_ctx.ctx_mut(), |ui| {
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
// Create a single centered slot
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
// Add flexible space before the slot to center it
|
||||||
|
ui.add_space(ui.available_width() / 2.0 - 25.0);
|
||||||
|
|
||||||
|
// Create a frame for the slot
|
||||||
|
let slot_frame = egui::Frame {
|
||||||
|
fill: egui::Color32::from_rgba_premultiplied(75, 75, 75, 100),
|
||||||
|
stroke: egui::Stroke::new(2.0, egui::Color32::WHITE),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
slot_frame.show(ui, |ui| {
|
||||||
|
ui.add_sized([50.0, 50.0], |ui: &mut egui::Ui| {
|
||||||
|
// Display the item
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
ui.horizontal_centered(|ui| {
|
||||||
|
match icon {
|
||||||
|
Some(image) => {
|
||||||
|
ui.add(egui::widgets::Image::new(
|
||||||
|
egui::load::SizedTexture::new(image, [50.0, 30.0]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
ui.label(egui::RichText::new("Empty").size(12.0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new(name)
|
||||||
|
.color(egui::Color32::WHITE)
|
||||||
|
.size(12.0),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.allocate_response(ui.available_size(), egui::Sense::click())
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user