second key and fence

This commit is contained in:
2025-03-14 21:57:37 +01:00
parent 87e0529afb
commit 5ba25edbdf
10 changed files with 711 additions and 591 deletions

1
Cargo.lock generated
View File

@@ -2820,6 +2820,7 @@ dependencies = [
"bevy_sprite3d", "bevy_sprite3d",
"bevy_trenchbroom", "bevy_trenchbroom",
"nil", "nil",
"rand",
] ]
[[package]] [[package]]

View File

@@ -17,3 +17,4 @@ bevy-tnua-avian3d = "0.2.0"
bevy_dolly = { version = "0.0.5", default-features = false } bevy_dolly = { version = "0.0.5", default-features = false }
bevy_asset_loader = "0.22.0" bevy_asset_loader = "0.22.0"
bevy_sprite3d = "4.0.0" bevy_sprite3d = "4.0.0"
rand = "0.8.5"

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@ enum CutsceneState {
None, None,
Playing { Playing {
timer: Timer, timer: Timer,
name: String,
}, },
} }
@@ -21,37 +22,39 @@ pub fn plugin(app: &mut App) {
fn on_start_cutscene( fn on_start_cutscene(
trigger: Trigger<StartCutscene>, trigger: Trigger<StartCutscene>,
cutscenes: Query<(&Transform, &CutsceneCamera), Without<Camera>>,
mut res: ResMut<DebugVisuals>, mut res: ResMut<DebugVisuals>,
mut cutscene_state: ResMut<CutsceneState>, mut cutscene_state: ResMut<CutsceneState>,
mut cam: Query<&mut Transform, With<Camera>>,
) { ) {
let cutscene = trigger.event().0.clone(); let cutscene = trigger.event().0.clone();
let Some((t, _)) = cutscenes
.iter()
.find(|(_, cutscene_camera)| cutscene == cutscene_camera.name)
else {
return;
};
res.cam_follow = false; res.cam_follow = false;
*cam.single_mut() = *t;
*cutscene_state = CutsceneState::Playing { *cutscene_state = CutsceneState::Playing {
timer: Timer::from_seconds(2.0, TimerMode::Once), timer: Timer::from_seconds(2.0, TimerMode::Once),
name: cutscene,
}; };
} }
fn update( fn update(
mut res: ResMut<DebugVisuals>, mut res: ResMut<DebugVisuals>,
mut cutscene_state: ResMut<CutsceneState>, mut cutscene_state: ResMut<CutsceneState>,
cutscenes: Query<(&Transform, &CutsceneCamera), Without<Camera>>,
mut cam: Query<&mut Transform, With<Camera>>,
time: Res<Time>, time: Res<Time>,
) { ) {
if let CutsceneState::Playing { timer, .. } = &mut *cutscene_state { if let CutsceneState::Playing { timer, name } = &mut *cutscene_state {
res.cam_follow = false;
timer.tick(time.delta()); timer.tick(time.delta());
let Some((t, _)) = cutscenes
.iter()
.find(|(_, cutscene_camera)| name == &cutscene_camera.name)
else {
return;
};
*cam.single_mut() = *t;
if timer.finished() { if timer.finished() {
res.cam_follow = true; res.cam_follow = true;
*cutscene_state = CutsceneState::None; *cutscene_state = CutsceneState::None;

View File

@@ -1,38 +1,46 @@
use crate::{cutscene::StartCutscene, keys::KeyCollected, movables::TriggerMovableEvent}; use crate::{cutscene::StartCutscene, keys::KeyCollected, movables::TriggerMovableEvent};
use bevy::{prelude::*, utils::hashbrown::HashSet}; use bevy::{prelude::*, utils::hashbrown::HashSet};
#[derive(Resource)]
enum GatesState {
Init,
GateOpen1,
}
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.insert_resource(GatesState::Init);
app.add_observer(on_key); app.add_observer(on_key);
} }
fn on_key( fn on_key(trigger: Trigger<KeyCollected>, mut commands: Commands, asset_server: Res<AssetServer>) {
_trigger: Trigger<KeyCollected>, match trigger.event().0.as_str() {
mut commands: Commands, "fence_gate" => {
mut state: ResMut<GatesState>, commands.trigger(StartCutscene("fence_01".to_string()));
asset_server: Res<AssetServer>,
) {
if matches!(*state, GatesState::Init) {
*state = GatesState::GateOpen1;
commands.trigger(StartCutscene("fence_01".to_string()));
//TODO: put into a sound effects system //TODO: put into a sound effects system
commands.spawn(( commands.spawn((
AudioPlayer::new(asset_server.load("sfx/effects/gate.ogg")), AudioPlayer::new(asset_server.load("sfx/effects/gate.ogg")),
PlaybackSettings::DESPAWN, PlaybackSettings::DESPAWN,
)); ));
let entities: HashSet<_> = vec!["fence_01", "fence_02"] let entities: HashSet<_> = vec!["fence_01", "fence_02"]
.into_iter() .into_iter()
.map(|s| String::from(s)) .map(|s| String::from(s))
.collect(); .collect();
commands.trigger(TriggerMovableEvent(entities)); commands.trigger(TriggerMovableEvent(entities));
}
"fence_shaft" => {
commands.trigger(StartCutscene("fence_02".to_string()));
//TODO: put into a sound effects system
commands.spawn((
AudioPlayer::new(asset_server.load("sfx/effects/gate.ogg")),
PlaybackSettings::DESPAWN,
));
let entities: HashSet<_> = vec!["fence_shaft"]
.into_iter()
.map(|s| String::from(s))
.collect();
commands.trigger(TriggerMovableEvent(entities));
}
_ => {
error!("unknown key logic: {}", trigger.event().0);
}
} }
} }

View File

@@ -4,14 +4,14 @@ use bevy::prelude::*;
use std::f32::consts::PI; use std::f32::consts::PI;
#[derive(Event, Reflect)] #[derive(Event, Reflect)]
pub struct KeySpawn(pub Vec3); pub struct KeySpawn(pub Vec3, pub String);
#[derive(Component, Reflect)] #[derive(Component, Reflect)]
#[reflect(Component)] #[reflect(Component)]
struct Key; struct Key(pub String);
#[derive(Event, Reflect)] #[derive(Event, Reflect)]
pub struct KeyCollected; pub struct KeyCollected(pub String);
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.add_systems(Update, collect_key); app.add_systems(Update, collect_key);
@@ -19,17 +19,17 @@ pub fn plugin(app: &mut App) {
} }
fn on_spawn(trigger: Trigger<KeySpawn>, mut commands: Commands, asset_server: Res<AssetServer>) { fn on_spawn(trigger: Trigger<KeySpawn>, mut commands: Commands, asset_server: Res<AssetServer>) {
let KeySpawn(position) = trigger.event(); let KeySpawn(position, id) = trigger.event();
//TODO: randomize let angle = rand::random::<f32>() * PI * 2.;
let spawn_dir = Quat::from_rotation_y(PI / 2.) * Vec3::new(0.5, 0.6, 0.).normalize(); let spawn_dir = Quat::from_rotation_y(angle) * Vec3::new(0.5, 0.6, 0.).normalize();
let mesh = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/key.glb")); let mesh = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/key.glb"));
commands commands
.spawn(( .spawn((
Name::new("key"), Name::new("key"),
Key, Key(id.clone()),
Transform::from_translation(*position), Transform::from_translation(*position),
Visibility::default(), Visibility::default(),
Collider::sphere(1.5), Collider::sphere(1.5),
@@ -50,21 +50,21 @@ fn collect_key(
) { ) {
for CollisionStarted(e1, e2) in collision_event_reader.read() { for CollisionStarted(e1, e2) in collision_event_reader.read() {
let collectable = if query_player.contains(*e1) && query_collectable.contains(*e2) { let collectable = if query_player.contains(*e1) && query_collectable.contains(*e2) {
Some(*e2) *e2
} else if query_player.contains(*e2) && query_collectable.contains(*e1) { } else if query_player.contains(*e2) && query_collectable.contains(*e1) {
Some(*e1) *e1
} else { } else {
None continue;
}; };
if let Some(collectable) = collectable { let key = query_collectable.get(collectable).unwrap();
commands.spawn((
AudioPlayer::new(asset_server.load("sfx/effects/key_collect.ogg")),
PlaybackSettings::DESPAWN,
));
commands.trigger(KeyCollected); commands.spawn((
commands.entity(collectable).despawn_recursive(); AudioPlayer::new(asset_server.load("sfx/effects/key_collect.ogg")),
} PlaybackSettings::DESPAWN,
));
commands.trigger(KeyCollected(key.0.clone()));
commands.entity(collectable).despawn_recursive();
} }
} }

View File

@@ -57,7 +57,7 @@ fn trigger(
target, target,
start_time: time.elapsed_secs(), start_time: time.elapsed_secs(),
//TODO: make this configurable //TODO: make this configurable
duration: 1., duration: 2.,
}; };
commands.entity(e).insert(platform); commands.entity(e).insert(platform);
@@ -75,6 +75,7 @@ fn move_active(
let t = (elapsed - active.start_time) / active.duration; let t = (elapsed - active.start_time) / active.duration;
transform.rotation = active.start.rotation.lerp(active.target.rotation, t); transform.rotation = active.start.rotation.lerp(active.target.rotation, t);
} else { } else {
info!("movable done");
*transform = active.target; *transform = active.target;
commands.entity(e).remove::<(ActiveMovable, Movable)>(); commands.entity(e).remove::<(ActiveMovable, Movable)>();

View File

@@ -37,8 +37,8 @@ fn on_hit(
if hp.0 <= 0 { if hp.0 <= 0 {
commands.entity(trigger.entity()).despawn_recursive(); commands.entity(trigger.entity()).despawn_recursive();
if enemy.has_key { if !enemy.key.is_empty() {
commands.trigger(KeySpawn(transform.translation)); commands.trigger(KeySpawn(transform.translation, enemy.key.clone()));
} }
} }
} }

View File

@@ -102,7 +102,7 @@ pub struct CutsceneCamera {
#[model({ "path": "models/alien_naked.glb" })] #[model({ "path": "models/alien_naked.glb" })]
pub struct EnemySpawn { pub struct EnemySpawn {
pub head: String, pub head: String,
pub has_key: bool, pub key: String,
} }
impl EnemySpawn { impl EnemySpawn {

View File

@@ -72,11 +72,7 @@
@PointClass base(transform) model({ "path": "models/alien_naked.glb" }) = enemy_spawn @PointClass base(transform) model({ "path": "models/alien_naked.glb" }) = enemy_spawn
[ [
head(string) : "head" : "" : "" head(string) : "head" : "" : ""
has_key(choices) : "has_key" : "false" : "" = key(string) : "key" : "" : ""
[
"true" : "true"
"false" : "false"
]
] ]
@SolidClass base(transform, target) = movable @SolidClass base(transform, target) = movable