Bevy 0.16 upgrade (#24)
This commit is contained in:
1212
Cargo.lock
generated
1212
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@@ -11,7 +11,7 @@ opt-level = 3
|
|||||||
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui"]
|
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
avian3d = { version = "0.2.1", default-features = false, features = [
|
avian3d = { version = "0.2", default-features = false, features = [
|
||||||
"3d",
|
"3d",
|
||||||
"f32",
|
"f32",
|
||||||
"parry-f32",
|
"parry-f32",
|
||||||
@@ -20,23 +20,22 @@ avian3d = { version = "0.2.1", default-features = false, features = [
|
|||||||
"bevy_picking", # todo: Consider if this one is necessary
|
"bevy_picking", # todo: Consider if this one is necessary
|
||||||
"parallel",
|
"parallel",
|
||||||
] }
|
] }
|
||||||
bevy = "0.15.3"
|
bevy = { version = "0.16.0", features = ["track_location"] }
|
||||||
bevy_trenchbroom = { version = "0.7", features = [
|
bevy_trenchbroom = { version = "0.7", features = [
|
||||||
"auto_register",
|
"auto_register",
|
||||||
"avian",
|
"avian",
|
||||||
"client",
|
"client",
|
||||||
] }
|
] }
|
||||||
nil = "0.14.0"
|
nil = "0.14.0"
|
||||||
bevy_asset_loader = "0.22.0"
|
bevy_asset_loader = "0.23.0-rc.3"
|
||||||
bevy_sprite3d = "4.0.0"
|
bevy_sprite3d = "5.0.0"
|
||||||
rand = "=0.8.5"
|
rand = "=0.8.5"
|
||||||
bevy-inspector-egui = { version = "0.30", optional = true }
|
bevy-inspector-egui = { version = "0.31", optional = true }
|
||||||
bevy_polyline = "0.11.0"
|
|
||||||
bevy-steamworks = "0.13.0"
|
bevy-steamworks = "0.13.0"
|
||||||
bevy_ballistic = "0.1.0"
|
bevy_ballistic = "0.3.0"
|
||||||
bevy-ui-gradients = "0.3.0"
|
bevy-ui-gradients = "0.4.0"
|
||||||
bevy_debug_log = "0.5.0"
|
bevy_debug_log = "0.6.0"
|
||||||
bevy_common_assets = { version = "0.12.0", features = ["ron"] }
|
bevy_common_assets = { version = "0.13.0", features = ["ron"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
|
|
||||||
@@ -48,4 +47,7 @@ too_many_arguments = "allow"
|
|||||||
type_complexity = "allow"
|
type_complexity = "allow"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
bevy-steamworks = { git = "https://github.com/Laocoon7/bevy_steamworks.git" }
|
bevy-steamworks = { git = "https://github.com/HouraiTeahouse/bevy_steamworks.git", rev = "0bc62fc" }
|
||||||
|
avian3d = { git = "https://github.com/Jondolf/avian.git", rev = "9511076" }
|
||||||
|
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", branch = "bevy-0.16" }
|
||||||
|
bevy_trenchbroom = { git = "https://github.com/extrawurst/bevy_trenchbroom.git", branch = "bevy-0.16.0" }
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ use crate::{
|
|||||||
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer, sounds::PlaySound,
|
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer, sounds::PlaySound,
|
||||||
tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
|
tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
|
||||||
};
|
};
|
||||||
use avian3d::prelude::{
|
use avian3d::prelude::*;
|
||||||
Collider, CollisionLayers, CollisionStarted, LayerMask, PhysicsLayer, Sensor,
|
|
||||||
};
|
|
||||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||||
use bevy_polyline::prelude::*;
|
|
||||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
@@ -83,8 +80,7 @@ fn on_trigger_gun(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query_transform: Query<&Transform>,
|
query_transform: Query<&Transform>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut polyline_materials: ResMut<Assets<PolylineMaterial>>,
|
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||||
mut polylines: ResMut<Assets<Polyline>>,
|
|
||||||
) {
|
) {
|
||||||
let state = trigger.0;
|
let state = trigger.0;
|
||||||
|
|
||||||
@@ -117,19 +113,20 @@ fn on_trigger_gun(
|
|||||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||||
),
|
),
|
||||||
Sensor,
|
Sensor,
|
||||||
|
CollisionEventsEnabled,
|
||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
t,
|
t,
|
||||||
))
|
))
|
||||||
.with_child(PolylineBundle {
|
.with_child(Gizmo {
|
||||||
polyline: PolylineHandle(polylines.add(Polyline {
|
handle: gizmo_assets.add({
|
||||||
vertices: vec![Vec3::Z * 2., Vec3::Z * -2.],
|
let mut g = GizmoAsset::default();
|
||||||
})),
|
g.line(Vec3::Z * -2., Vec3::Z * 2., LinearRgba::rgb(0.9, 0.9, 0.));
|
||||||
material: PolylineMaterialHandle(polyline_materials.add(PolylineMaterial {
|
g
|
||||||
width: 10.0,
|
}),
|
||||||
color: LinearRgba::rgb(0.9, 0.9, 0.),
|
line_config: GizmoLineConfig {
|
||||||
perspective: false,
|
width: 8.,
|
||||||
..default()
|
..default()
|
||||||
})),
|
},
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -146,7 +143,7 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &GunProjectile)>, time:
|
|||||||
|
|
||||||
for (e, GunProjectile { time, .. }) in query.iter() {
|
for (e, GunProjectile { time, .. }) in query.iter() {
|
||||||
if current_time > time + MAX_SHOT_AGES {
|
if current_time > time + MAX_SHOT_AGES {
|
||||||
commands.entity(e).despawn_recursive();
|
commands.entity(e).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +164,7 @@ fn shot_collision(
|
|||||||
|
|
||||||
let shot_pos = query_shot.get(shot_entity).unwrap().1.translation;
|
let shot_pos = query_shot.get(shot_entity).unwrap().1.translation;
|
||||||
|
|
||||||
commands.entity(shot_entity).despawn_recursive();
|
commands.entity(shot_entity).despawn();
|
||||||
|
|
||||||
let texture_atlas = TextureAtlas {
|
let texture_atlas = TextureAtlas {
|
||||||
layout: assets.layout.clone(),
|
layout: assets.layout.clone(),
|
||||||
|
|||||||
@@ -7,15 +7,14 @@ use crate::{
|
|||||||
loading_assets::GameAssets,
|
loading_assets::GameAssets,
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
utils::{global_observer, sprite_3d_animation::AnimationTimer},
|
utils::{global_observer, sprite_3d_animation::AnimationTimer, trail::Trail},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||||
use bevy_polyline::prelude::*;
|
|
||||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
const MAX_SHOT_AGES: f32 = 5.;
|
const MAX_SHOT_AGES: f32 = 15.;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct MissileProjectile {
|
struct MissileProjectile {
|
||||||
@@ -66,7 +65,7 @@ fn on_explosion(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for entity in intersections.iter() {
|
for entity in intersections.iter() {
|
||||||
if let Some(mut e) = commands.get_entity(*entity) {
|
if let Ok(mut e) = commands.get_entity(*entity) {
|
||||||
e.trigger(Hit {
|
e.trigger(Hit {
|
||||||
damage: explosion.damage,
|
damage: explosion.damage,
|
||||||
});
|
});
|
||||||
@@ -89,11 +88,10 @@ fn on_trigger_missile(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query_transform: Query<&Transform>,
|
query_transform: Query<&Transform>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut polyline_materials: ResMut<Assets<PolylineMaterial>>,
|
|
||||||
mut polylines: ResMut<Assets<Polyline>>,
|
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
assets: Res<GameAssets>,
|
assets: Res<GameAssets>,
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
gltf_assets: Res<Assets<Gltf>>,
|
||||||
|
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||||
) {
|
) {
|
||||||
let state = trigger.event().0;
|
let state = trigger.event().0;
|
||||||
|
|
||||||
@@ -111,7 +109,7 @@ fn on_trigger_missile(
|
|||||||
let head = heads_db.head_stats(state.head);
|
let head = heads_db.head_stats(state.head);
|
||||||
|
|
||||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||||
t.translation += t.forward().as_vec3() * 3.0;
|
t.translation += t.forward().as_vec3() * 2.0;
|
||||||
|
|
||||||
let mesh = assets.projectiles["missile.glb"].clone();
|
let mesh = assets.projectiles["missile.glb"].clone();
|
||||||
let asset = gltf_assets.get(&mesh).unwrap();
|
let asset = gltf_assets.get(&mesh).unwrap();
|
||||||
@@ -123,12 +121,13 @@ fn on_trigger_missile(
|
|||||||
time: time.elapsed_secs(),
|
time: time.elapsed_secs(),
|
||||||
damage: head.damage,
|
damage: head.damage,
|
||||||
},
|
},
|
||||||
Collider::capsule_endpoints(0.5, Vec3::new(0., 0., 0.), Vec3::new(0., 0., -3.)),
|
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||||
CollisionLayers::new(
|
CollisionLayers::new(
|
||||||
LayerMask(GameLayer::Projectile.to_bits()),
|
LayerMask(GameLayer::Projectile.to_bits()),
|
||||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||||
),
|
),
|
||||||
Sensor,
|
Sensor,
|
||||||
|
CollisionEventsEnabled,
|
||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
t,
|
t,
|
||||||
))
|
))
|
||||||
@@ -137,18 +136,17 @@ fn on_trigger_missile(
|
|||||||
.with_scale(Vec3::splat(0.04)),
|
.with_scale(Vec3::splat(0.04)),
|
||||||
SceneRoot(asset.scenes[0].clone()),
|
SceneRoot(asset.scenes[0].clone()),
|
||||||
))
|
))
|
||||||
.with_child(PolylineBundle {
|
.with_child((
|
||||||
polyline: PolylineHandle(polylines.add(Polyline {
|
Trail::new(t.translation, LinearRgba::rgb(0.9, 0.9, 0.)),
|
||||||
vertices: vec![Vec3::Z * 4., Vec3::Z * 0.],
|
Gizmo {
|
||||||
})),
|
handle: gizmo_assets.add(GizmoAsset::default()),
|
||||||
material: PolylineMaterialHandle(polyline_materials.add(PolylineMaterial {
|
line_config: GizmoLineConfig {
|
||||||
width: 12.0,
|
width: 10.,
|
||||||
color: LinearRgba::rgb(0.9, 0.9, 0.),
|
..default()
|
||||||
perspective: false,
|
},
|
||||||
..default()
|
..default()
|
||||||
})),
|
},
|
||||||
..default()
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
||||||
@@ -163,7 +161,7 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &MissileProjectile)>, t
|
|||||||
|
|
||||||
for (e, MissileProjectile { time, .. }) in query.iter() {
|
for (e, MissileProjectile { time, .. }) in query.iter() {
|
||||||
if current_time > time + MAX_SHOT_AGES {
|
if current_time > time + MAX_SHOT_AGES {
|
||||||
commands.entity(e).despawn_recursive();
|
commands.entity(e).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +189,7 @@ fn shot_collision(
|
|||||||
|
|
||||||
commands.trigger(PlaySound::MissileExplosion);
|
commands.trigger(PlaySound::MissileExplosion);
|
||||||
|
|
||||||
commands.entity(shot_entity).despawn_recursive();
|
commands.entity(shot_entity).despawn();
|
||||||
|
|
||||||
commands.trigger(Explosion {
|
commands.trigger(Explosion {
|
||||||
damage,
|
damage,
|
||||||
|
|||||||
@@ -88,16 +88,12 @@ fn on_trigger_state(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
|
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
|
||||||
player_query: Query<(Entity, &AimTarget), With<Player>>,
|
player_query: Query<(Entity, &AimTarget), With<Player>>,
|
||||||
mut active_heads: Query<&mut ActiveHeads, With<Player>>,
|
mut active_heads: Single<&mut ActiveHeads, With<Player>>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
character: CharacterHierarchy,
|
character: CharacterHierarchy,
|
||||||
) {
|
) {
|
||||||
if matches!(trigger.event(), TriggerState::Active) {
|
if matches!(trigger.event(), TriggerState::Active) {
|
||||||
let Ok(mut active_heads) = active_heads.get_single_mut() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(state) = active_heads.current() else {
|
let Some(state) = active_heads.current() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ fn on_explosion(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for entity in intersections.iter() {
|
for entity in intersections.iter() {
|
||||||
if let Some(mut e) = commands.get_entity(*entity) {
|
if let Ok(mut e) = commands.get_entity(*entity) {
|
||||||
e.trigger(Hit {
|
e.trigger(Hit {
|
||||||
damage: explosion.damage,
|
damage: explosion.damage,
|
||||||
});
|
});
|
||||||
@@ -135,6 +135,7 @@ fn on_trigger_thrown(
|
|||||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||||
),
|
),
|
||||||
RigidBody::Dynamic,
|
RigidBody::Dynamic,
|
||||||
|
CollisionEventsEnabled,
|
||||||
Mass(0.01),
|
Mass(0.01),
|
||||||
LinearVelocity(vel),
|
LinearVelocity(vel),
|
||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
@@ -171,7 +172,7 @@ fn shot_collision(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
commands.entity(shot_entity).despawn_recursive();
|
commands.entity(shot_entity).despawn();
|
||||||
|
|
||||||
commands.trigger(PlaySound::ThrowHit);
|
commands.trigger(PlaySound::ThrowHit);
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ fn wait_for_player(
|
|||||||
for agent in agents.iter() {
|
for agent in agents.iter() {
|
||||||
if let Some(player) = in_range(50., agent, &players, &transform) {
|
if let Some(player) = in_range(50., agent, &players, &transform) {
|
||||||
info!("[{agent}] Engage: {player}");
|
info!("[{agent}] Engage: {player}");
|
||||||
if let Some(mut agent) = commands.get_entity(agent) {
|
if let Ok(mut agent) = commands.get_entity(agent) {
|
||||||
agent.remove::<WaitForAnyPlayer>().insert(Engage(player));
|
agent.remove::<WaitForAnyPlayer>().insert(Engage(player));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ fn marker_event(
|
|||||||
marker: Query<Entity, With<TargetMarker>>,
|
marker: Query<Entity, With<TargetMarker>>,
|
||||||
) {
|
) {
|
||||||
for m in marker.iter() {
|
for m in marker.iter() {
|
||||||
commands.entity(m).despawn_recursive();
|
commands.entity(m).despawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
let MarkerEvent::Spawn(target) = trigger.event() else {
|
let MarkerEvent::Spawn(target) = trigger.event() else {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ fn update_player_aim(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(e) = &aim_target.0 {
|
if let Some(e) = &aim_target.0 {
|
||||||
if commands.get_entity(*e).is_none() {
|
if commands.get_entity(*e).is_err() {
|
||||||
aim_target.0 = None;
|
aim_target.0 = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ fn update_npc_aim(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(e) = &aim_target.0 {
|
if let Some(e) = &aim_target.0 {
|
||||||
if commands.get_entity(*e).is_none() {
|
if commands.get_entity(*e).is_err() {
|
||||||
aim_target.0 = None;
|
aim_target.0 = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_head_ui(
|
fn spawn_head_ui(
|
||||||
parent: &mut ChildBuilder,
|
parent: &mut ChildSpawnerCommands,
|
||||||
bg: Handle<Image>,
|
bg: Handle<Image>,
|
||||||
regular: Handle<Image>,
|
regular: Handle<Image>,
|
||||||
damage: Handle<Image>,
|
damage: Handle<Image>,
|
||||||
@@ -129,7 +129,7 @@ fn update(
|
|||||||
mut head_damage: Query<&mut Node, (With<HeadDamage>, Without<HeadImage>)>,
|
mut head_damage: Query<&mut Node, (With<HeadDamage>, Without<HeadImage>)>,
|
||||||
) {
|
) {
|
||||||
if target.is_changed() {
|
if target.is_changed() {
|
||||||
if let Ok((mut vis, mut image)) = head_image.get_single_mut() {
|
if let Ok((mut vis, mut image)) = head_image.single_mut() {
|
||||||
if let Some(head) = target.head {
|
if let Some(head) = target.head {
|
||||||
*vis = Visibility::Visible;
|
*vis = Visibility::Visible;
|
||||||
image.image = heads_images.heads[head.head].clone();
|
image.image = heads_images.heads[head.head].clone();
|
||||||
@@ -138,7 +138,7 @@ fn update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(mut node) = head_damage.get_single_mut() {
|
if let Ok(mut node) = head_damage.single_mut() {
|
||||||
node.height = Val::Percent(target.head.map(|head| head.damage()).unwrap_or(0.) * 100.);
|
node.height = Val::Percent(target.head.map(|head| head.damage()).unwrap_or(0.) * 100.);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
commands.spawn((
|
commands.spawn((
|
||||||
Name::new("backpack-head-count-ui"),
|
Name::new("backpack-head-count-ui"),
|
||||||
Text::new("0"),
|
Text::new("0"),
|
||||||
|
TextShadow::default(),
|
||||||
BackpackCountText,
|
BackpackCountText,
|
||||||
TextFont {
|
TextFont {
|
||||||
font: assets.font.clone(),
|
font: assets.font.clone(),
|
||||||
@@ -104,7 +105,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_head_ui(
|
fn spawn_head_ui(
|
||||||
parent: &mut ChildBuilder,
|
parent: &mut ChildSpawnerCommands,
|
||||||
bg: Handle<Image>,
|
bg: Handle<Image>,
|
||||||
regular: Handle<Image>,
|
regular: Handle<Image>,
|
||||||
selector: Handle<Image>,
|
selector: Handle<Image>,
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ fn update_ui(
|
|||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
for entity in query.iter() {
|
for entity in query.iter() {
|
||||||
commands.entity(entity).despawn_recursive();
|
commands.entity(entity).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,8 +114,8 @@ fn update(
|
|||||||
(&MainCamera, &mut Transform, &CameraRotationInput),
|
(&MainCamera, &mut Transform, &CameraRotationInput),
|
||||||
(Without<CameraTarget>, Without<CameraArmRotation>),
|
(Without<CameraTarget>, Without<CameraArmRotation>),
|
||||||
>,
|
>,
|
||||||
target: Query<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>,
|
target: Single<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>,
|
||||||
arm_rotation: Query<&Transform, With<CameraArmRotation>>,
|
arm_rotation: Single<&Transform, With<CameraArmRotation>>,
|
||||||
spatial_query: SpatialQuery,
|
spatial_query: SpatialQuery,
|
||||||
cam_state: Res<CameraState>,
|
cam_state: Res<CameraState>,
|
||||||
) {
|
) {
|
||||||
@@ -123,15 +123,11 @@ fn update(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(target) = target.get_single().map(|t| t.translation()) else {
|
let target = target.translation();
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(arm_tf) = arm_rotation.get_single() else {
|
let arm_tf = arm_rotation;
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok((camera, mut cam_transform, cam_rotation_input)) = cam.get_single_mut() else {
|
let Ok((camera, mut cam_transform, cam_rotation_input)) = cam.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -159,13 +155,9 @@ fn update(
|
|||||||
|
|
||||||
fn rotate_view(
|
fn rotate_view(
|
||||||
controls: Res<ControlState>,
|
controls: Res<ControlState>,
|
||||||
mut cam: Query<&mut CameraRotationInput>,
|
mut cam: Single<&mut CameraRotationInput>,
|
||||||
movement: Res<PlayerMovement>,
|
movement: Res<PlayerMovement>,
|
||||||
) {
|
) {
|
||||||
let Ok(mut cam) = cam.get_single_mut() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !controls.view_mode {
|
if !controls.view_mode {
|
||||||
if movement.any_direction {
|
if movement.any_direction {
|
||||||
cam.x = 0.;
|
cam.x = 0.;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
commands.spawn((
|
commands.spawn((
|
||||||
Name::new("cash-ui"),
|
Name::new("cash-ui"),
|
||||||
Text::new("0"),
|
Text::new("0"),
|
||||||
|
TextShadow::default(),
|
||||||
CashText,
|
CashText,
|
||||||
TextFont {
|
TextFont {
|
||||||
font: assets.font.clone(),
|
font: assets.font.clone(),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ fn on_heal_trigger(
|
|||||||
mut cash: ResMut<CashResource>,
|
mut cash: ResMut<CashResource>,
|
||||||
mut query: Query<&mut Hitpoints, With<Player>>,
|
mut query: Query<&mut Hitpoints, With<Player>>,
|
||||||
) {
|
) {
|
||||||
let Ok(mut hp) = query.get_single_mut() else {
|
let Ok(mut hp) = query.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{GameState, heads_database::HeadsDatabase, loading_assets::GameAssets};
|
use crate::{GameState, heads_database::HeadsDatabase, loading_assets::GameAssets};
|
||||||
use bevy::{ecs::system::SystemParam, prelude::*, utils::HashMap};
|
use bevy::{ecs::system::SystemParam, platform::collections::HashMap, prelude::*};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Component, Debug)]
|
#[derive(Component, Debug)]
|
||||||
@@ -91,14 +91,14 @@ fn setup_projectile_origin(
|
|||||||
fn setup_once_loaded(
|
fn setup_once_loaded(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
|
mut query: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
|
||||||
parent_query: Query<&Parent>,
|
parent: Query<&ChildOf>,
|
||||||
animated_character: Query<&AnimatedCharacter>,
|
animated_character: Query<&AnimatedCharacter>,
|
||||||
assets: Res<GameAssets>,
|
assets: Res<GameAssets>,
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
gltf_assets: Res<Assets<Gltf>>,
|
||||||
mut graphs: ResMut<Assets<AnimationGraph>>,
|
mut graphs: ResMut<Assets<AnimationGraph>>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut player) in &mut query {
|
for (entity, mut player) in &mut query {
|
||||||
let Some(_animated_character) = parent_query
|
let Some(_animated_character) = parent
|
||||||
.iter_ancestors(entity)
|
.iter_ancestors(entity)
|
||||||
.find_map(|ancestor| animated_character.get(ancestor).ok())
|
.find_map(|ancestor| animated_character.get(ancestor).ok())
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -12,16 +12,11 @@ use super::controller_common::{CharacterController, MaxSlopeAngle};
|
|||||||
/// and predict collisions using speculative contacts.
|
/// and predict collisions using speculative contacts.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn kinematic_controller_collisions(
|
pub fn kinematic_controller_collisions(
|
||||||
collisions: Res<Collisions>,
|
collisions: Collisions,
|
||||||
bodies: Query<&RigidBody>,
|
bodies: Query<&RigidBody>,
|
||||||
collider_parents: Query<&ColliderParent, Without<Sensor>>,
|
collider_rbs: Query<&ColliderOf, Without<Sensor>>,
|
||||||
mut character_controllers: Query<
|
mut character_controllers: Query<
|
||||||
(
|
(&mut Position, &mut LinearVelocity, Option<&MaxSlopeAngle>),
|
||||||
&mut Position,
|
|
||||||
&Rotation,
|
|
||||||
&mut LinearVelocity,
|
|
||||||
Option<&MaxSlopeAngle>,
|
|
||||||
),
|
|
||||||
(With<RigidBody>, With<CharacterController>),
|
(With<RigidBody>, With<CharacterController>),
|
||||||
>,
|
>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
@@ -29,8 +24,12 @@ pub fn kinematic_controller_collisions(
|
|||||||
// Iterate through collisions and move the kinematic body to resolve penetration
|
// Iterate through collisions and move the kinematic body to resolve penetration
|
||||||
for contacts in collisions.iter() {
|
for contacts in collisions.iter() {
|
||||||
// Get the rigid body entities of the colliders (colliders could be children)
|
// Get the rigid body entities of the colliders (colliders could be children)
|
||||||
let Ok([collider_parent1, collider_parent2]) =
|
let Ok(
|
||||||
collider_parents.get_many([contacts.entity1, contacts.entity2])
|
[
|
||||||
|
&ColliderOf { rigid_body: rb1 },
|
||||||
|
&ColliderOf { rigid_body: rb2 },
|
||||||
|
],
|
||||||
|
) = collider_rbs.get_many([contacts.entity1, contacts.entity2])
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@@ -42,20 +41,16 @@ pub fn kinematic_controller_collisions(
|
|||||||
let character_rb: RigidBody;
|
let character_rb: RigidBody;
|
||||||
let is_other_dynamic: bool;
|
let is_other_dynamic: bool;
|
||||||
|
|
||||||
let (mut position, rotation, mut linear_velocity, max_slope_angle) =
|
let (mut position, mut linear_velocity, max_slope_angle) =
|
||||||
if let Ok(character) = character_controllers.get_mut(collider_parent1.get()) {
|
if let Ok(character) = character_controllers.get_mut(rb1) {
|
||||||
is_first = true;
|
is_first = true;
|
||||||
character_rb = *bodies.get(collider_parent1.get()).unwrap();
|
character_rb = *bodies.get(rb1).unwrap();
|
||||||
is_other_dynamic = bodies
|
is_other_dynamic = bodies.get(rb2).is_ok_and(|rb| rb.is_dynamic());
|
||||||
.get(collider_parent2.get())
|
|
||||||
.is_ok_and(|rb| rb.is_dynamic());
|
|
||||||
character
|
character
|
||||||
} else if let Ok(character) = character_controllers.get_mut(collider_parent2.get()) {
|
} else if let Ok(character) = character_controllers.get_mut(rb2) {
|
||||||
is_first = false;
|
is_first = false;
|
||||||
character_rb = *bodies.get(collider_parent2.get()).unwrap();
|
character_rb = *bodies.get(rb2).unwrap();
|
||||||
is_other_dynamic = bodies
|
is_other_dynamic = bodies.get(rb1).is_ok_and(|rb| rb.is_dynamic());
|
||||||
.get(collider_parent1.get())
|
|
||||||
.is_ok_and(|rb| rb.is_dynamic());
|
|
||||||
character
|
character
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@@ -70,15 +65,15 @@ pub fn kinematic_controller_collisions(
|
|||||||
// Each contact in a single manifold shares the same contact normal.
|
// Each contact in a single manifold shares the same contact normal.
|
||||||
for manifold in contacts.manifolds.iter() {
|
for manifold in contacts.manifolds.iter() {
|
||||||
let normal = if is_first {
|
let normal = if is_first {
|
||||||
-manifold.global_normal1(rotation)
|
-manifold.normal
|
||||||
} else {
|
} else {
|
||||||
-manifold.global_normal2(rotation)
|
manifold.normal
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut deepest_penetration: Scalar = Scalar::MIN;
|
let mut deepest_penetration: Scalar = Scalar::MIN;
|
||||||
|
|
||||||
// Solve each penetrating contact in the manifold.
|
// Solve each penetrating contact in the manifold.
|
||||||
for contact in manifold.contacts.iter() {
|
for contact in manifold.points.iter() {
|
||||||
if contact.penetration > 0.0 {
|
if contact.penetration > 0.0 {
|
||||||
position.0 += normal * contact.penetration;
|
position.0 += normal * contact.penetration;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ pub fn plugin(app: &mut App) {
|
|||||||
//
|
//
|
||||||
// NOTE: The collision implementation here is very basic and a bit buggy.
|
// NOTE: The collision implementation here is very basic and a bit buggy.
|
||||||
// A collide-and-slide algorithm would likely work better.
|
// A collide-and-slide algorithm would likely work better.
|
||||||
PostProcessCollisions,
|
PhysicsSchedule,
|
||||||
kinematic_controller_collisions.run_if(in_state(GameState::Playing)), // todo check if we can make this less viral,
|
kinematic_controller_collisions
|
||||||
|
.in_set(NarrowPhaseSet::Last)
|
||||||
|
.run_if(in_state(GameState::Playing)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +124,7 @@ pub struct CharacterControllerBundle {
|
|||||||
ground_caster: ShapeCaster,
|
ground_caster: ShapeCaster,
|
||||||
gravity: ControllerGravity,
|
gravity: ControllerGravity,
|
||||||
movement: MovementBundle,
|
movement: MovementBundle,
|
||||||
|
collision_events: CollisionEventsEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bundle that contains components for character movement.
|
/// A bundle that contains components for character movement.
|
||||||
@@ -169,6 +172,7 @@ impl CharacterControllerBundle {
|
|||||||
.with_max_distance(0.2),
|
.with_max_distance(0.2),
|
||||||
gravity: ControllerGravity(gravity),
|
gravity: ControllerGravity(gravity),
|
||||||
movement: MovementBundle::default(),
|
movement: MovementBundle::default(),
|
||||||
|
collision_events: CollisionEventsEnabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ fn head_change(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if selected_controller.0 != controller {
|
if selected_controller.0 != controller {
|
||||||
event_controller_switch.send(ControllerSwitchEvent);
|
event_controller_switch.write(ControllerSwitchEvent);
|
||||||
|
|
||||||
selected_controller.0 = controller;
|
selected_controller.0 = controller;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd},
|
tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd},
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_trenchbroom::class::Target;
|
use bevy_trenchbroom::prelude::*;
|
||||||
|
|
||||||
#[derive(Event, Debug)]
|
#[derive(Event, Debug)]
|
||||||
pub struct StartCutscene(pub String);
|
pub struct StartCutscene(pub String);
|
||||||
@@ -96,7 +96,7 @@ fn update(
|
|||||||
.lerp(camera_end.rotation, timer.fraction()),
|
.lerp(camera_end.rotation, timer.fraction()),
|
||||||
);
|
);
|
||||||
|
|
||||||
*cam.single_mut() = t;
|
let _ = cam.single_mut().map(|mut cam| *cam = t);
|
||||||
|
|
||||||
if timer.finished() {
|
if timer.finished() {
|
||||||
cam_state.cutscene = false;
|
cam_state.cutscene = false;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
cutscene::StartCutscene, global_observer, keys::KeyCollected, movables::TriggerMovableEvent,
|
cutscene::StartCutscene, global_observer, keys::KeyCollected, movables::TriggerMovableEvent,
|
||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
};
|
};
|
||||||
use bevy::{prelude::*, utils::hashbrown::HashSet};
|
use bevy::{platform::collections::HashSet, prelude::*};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
global_observer!(app, on_key_collected);
|
global_observer!(app, on_key_collected);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ fn on_head_drop(trigger: Trigger<HeadDrops>, mut commands: Commands, assets: Res
|
|||||||
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|
||||||
LockedAxes::ROTATION_LOCKED,
|
LockedAxes::ROTATION_LOCKED,
|
||||||
RigidBody::Dynamic,
|
RigidBody::Dynamic,
|
||||||
|
CollisionEventsEnabled,
|
||||||
Restitution::new(0.6),
|
Restitution::new(0.6),
|
||||||
))
|
))
|
||||||
.with_child((
|
.with_child((
|
||||||
@@ -68,6 +69,6 @@ fn collect_head(
|
|||||||
|
|
||||||
commands.trigger(PlaySound::HeadCollect);
|
commands.trigger(PlaySound::HeadCollect);
|
||||||
commands.trigger(HeadCollected(key.0));
|
commands.trigger(HeadCollected(key.0));
|
||||||
commands.entity(collectable).despawn_recursive();
|
commands.entity(collectable).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_head_ui(
|
fn spawn_head_ui(
|
||||||
parent: &mut ChildBuilder,
|
parent: &mut ChildSpawnerCommands,
|
||||||
bg: Handle<Image>,
|
bg: Handle<Image>,
|
||||||
regular: Handle<Image>,
|
regular: Handle<Image>,
|
||||||
selector: Handle<Image>,
|
selector: Handle<Image>,
|
||||||
@@ -219,7 +219,7 @@ fn sync(
|
|||||||
mut state: ResMut<UiActiveHeads>,
|
mut state: ResMut<UiActiveHeads>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
let Ok(active_heads) = active_heads.get_single() else {
|
let Ok(active_heads) = active_heads.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ fn on_select_active_head(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut query: Query<(&mut ActiveHeads, &mut Hitpoints), With<Player>>,
|
mut query: Query<(&mut ActiveHeads, &mut Hitpoints), With<Player>>,
|
||||||
) {
|
) {
|
||||||
let Ok((mut active_heads, mut hp)) = query.get_single_mut() else {
|
let Ok((mut active_heads, mut hp)) = query.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ fn on_swap_backpack(
|
|||||||
|
|
||||||
let head = backpack.heads.get(backpack_slot).unwrap();
|
let head = backpack.heads.get(backpack_slot).unwrap();
|
||||||
|
|
||||||
let Ok((mut active_heads, mut hp)) = query.get_single_mut() else {
|
let Ok((mut active_heads, mut hp)) = query.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ fn on_hp_added(mut commands: Commands, query: Query<Entity, Added<Hitpoints>>) {
|
|||||||
fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut Hitpoints>) {
|
fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut Hitpoints>) {
|
||||||
let Hit { damage } = trigger.event();
|
let Hit { damage } = trigger.event();
|
||||||
|
|
||||||
let Ok(mut hp) = query.get_mut(trigger.entity()) else {
|
let Ok(mut hp) = query.get_mut(trigger.target()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -69,6 +69,6 @@ fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut H
|
|||||||
hp.current = hp.current.saturating_sub(*damage);
|
hp.current = hp.current.saturating_sub(*damage);
|
||||||
|
|
||||||
if hp.current == 0 {
|
if hp.current == 0 {
|
||||||
commands.trigger_targets(Kill, trigger.entity());
|
commands.trigger_targets(Kill, trigger.target());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<
|
|||||||
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|
||||||
LockedAxes::ROTATION_LOCKED,
|
LockedAxes::ROTATION_LOCKED,
|
||||||
RigidBody::Dynamic,
|
RigidBody::Dynamic,
|
||||||
|
CollisionEventsEnabled,
|
||||||
Restitution::new(0.6),
|
Restitution::new(0.6),
|
||||||
))
|
))
|
||||||
.with_child((
|
.with_child((
|
||||||
@@ -66,6 +67,6 @@ fn collect_key(
|
|||||||
|
|
||||||
commands.trigger(PlaySound::KeyCollect);
|
commands.trigger(PlaySound::KeyCollect);
|
||||||
commands.trigger(KeyCollected(key.0.clone()));
|
commands.trigger(KeyCollected(key.0.clone()));
|
||||||
commands.entity(collectable).despawn_recursive();
|
commands.entity(collectable).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
GameState,
|
GameState,
|
||||||
heads_database::{HeadDatabaseAsset, HeadsDatabase},
|
heads_database::{HeadDatabaseAsset, HeadsDatabase},
|
||||||
};
|
};
|
||||||
use bevy::{prelude::*, utils::HashMap};
|
use bevy::{platform::collections::HashMap, prelude::*};
|
||||||
use bevy_asset_loader::prelude::*;
|
use bevy_asset_loader::prelude::*;
|
||||||
|
|
||||||
#[derive(AssetCollection, Resource)]
|
#[derive(AssetCollection, Resource)]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::{GameState, physics_layers::GameLayer};
|
use crate::{GameState, physics_layers::GameLayer};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{prelude::*, scene::SceneInstanceReady};
|
use bevy::prelude::*;
|
||||||
|
use bevy_trenchbroom::physics::SceneCollidersReady;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_systems(OnEnter(GameState::MapLoading), setup_scene);
|
app.add_systems(OnEnter(GameState::MapLoading), setup_scene);
|
||||||
@@ -13,7 +14,8 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
SceneRoot(asset_server.load("maps/map1.map#Scene")),
|
SceneRoot(asset_server.load("maps/map1.map#Scene")),
|
||||||
))
|
))
|
||||||
.observe(
|
.observe(
|
||||||
|_t: Trigger<SceneInstanceReady>, mut next_game_state: ResMut<NextState<GameState>>| {
|
|_t: Trigger<SceneCollidersReady>,
|
||||||
|
mut next_game_state: ResMut<NextState<GameState>>| {
|
||||||
info!("map loaded");
|
info!("map loaded");
|
||||||
|
|
||||||
next_game_state.set(GameState::Playing);
|
next_game_state.set(GameState::Playing);
|
||||||
|
|||||||
26
src/main.rs
26
src/main.rs
@@ -36,16 +36,15 @@ use bevy::{
|
|||||||
render::view::ColorGrading,
|
render::view::ColorGrading,
|
||||||
};
|
};
|
||||||
use bevy_common_assets::ron::RonAssetPlugin;
|
use bevy_common_assets::ron::RonAssetPlugin;
|
||||||
use bevy_polyline::PolylinePlugin;
|
|
||||||
use bevy_sprite3d::Sprite3dPlugin;
|
use bevy_sprite3d::Sprite3dPlugin;
|
||||||
use bevy_steamworks::{FriendFlags, SteamworksClient, SteamworksPlugin};
|
use bevy_steamworks::{Client, FriendFlags, SteamworksPlugin};
|
||||||
use bevy_trenchbroom::prelude::*;
|
use bevy_trenchbroom::prelude::*;
|
||||||
use bevy_ui_gradients::UiGradientsPlugin;
|
use bevy_ui_gradients::UiGradientsPlugin;
|
||||||
use camera::MainCamera;
|
use camera::MainCamera;
|
||||||
use heads_database::HeadDatabaseAsset;
|
use heads_database::HeadDatabaseAsset;
|
||||||
use loading_assets::AudioAssets;
|
use loading_assets::AudioAssets;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use utils::{billboards, sprite_3d_animation, squish_animation};
|
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
||||||
|
|
||||||
#[derive(Resource, Reflect, Debug)]
|
#[derive(Resource, Reflect, Debug)]
|
||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
@@ -106,7 +105,6 @@ fn main() {
|
|||||||
|
|
||||||
app.add_plugins(bevy_debug_log::LogViewerPlugin::default());
|
app.add_plugins(bevy_debug_log::LogViewerPlugin::default());
|
||||||
app.add_plugins(PhysicsPlugins::default());
|
app.add_plugins(PhysicsPlugins::default());
|
||||||
app.add_plugins(PolylinePlugin);
|
|
||||||
app.add_plugins(Sprite3dPlugin);
|
app.add_plugins(Sprite3dPlugin);
|
||||||
app.add_plugins(TrenchBroomPlugin(TrenchBroomConfig::new("hedz")));
|
app.add_plugins(TrenchBroomPlugin(TrenchBroomConfig::new("hedz")));
|
||||||
app.add_plugins(UiGradientsPlugin);
|
app.add_plugins(UiGradientsPlugin);
|
||||||
@@ -114,8 +112,10 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(feature = "dbg")]
|
#[cfg(feature = "dbg")]
|
||||||
{
|
{
|
||||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin {
|
||||||
app.add_plugins(WorldInspectorPlugin::new());
|
enable_multipass_for_primary_context: true,
|
||||||
|
});
|
||||||
|
app.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::new());
|
||||||
app.add_plugins(PhysicsDebugPlugin::default());
|
app.add_plugins(PhysicsDebugPlugin::default());
|
||||||
|
|
||||||
// app.add_plugins(bevy::pbr::wireframe::WireframePlugin)
|
// app.add_plugins(bevy::pbr::wireframe::WireframePlugin)
|
||||||
@@ -153,22 +153,24 @@ fn main() {
|
|||||||
app.add_plugins(utils::observers::plugin);
|
app.add_plugins(utils::observers::plugin);
|
||||||
app.add_plugins(water::plugin);
|
app.add_plugins(water::plugin);
|
||||||
app.add_plugins(head_drop::plugin);
|
app.add_plugins(head_drop::plugin);
|
||||||
|
app.add_plugins(trail::plugin);
|
||||||
|
|
||||||
app.init_state::<GameState>();
|
app.init_state::<GameState>();
|
||||||
|
|
||||||
app.insert_resource(AmbientLight {
|
app.insert_resource(AmbientLight {
|
||||||
color: Color::WHITE,
|
color: Color::WHITE,
|
||||||
brightness: 400.,
|
brightness: 400.,
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
app.insert_resource(ClearColor(Color::BLACK));
|
app.insert_resource(ClearColor(Color::BLACK));
|
||||||
//TODO: let use control this
|
//TODO: let user control this
|
||||||
app.insert_resource(GlobalVolume::new(0.1));
|
app.insert_resource(GlobalVolume::new(Volume::Linear(0.4)));
|
||||||
|
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Startup,
|
Startup,
|
||||||
(
|
(
|
||||||
write_trenchbroom_config,
|
write_trenchbroom_config,
|
||||||
steam_system.run_if(resource_exists::<SteamworksClient>),
|
steam_system.run_if(resource_exists::<Client>),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
app.add_systems(OnEnter(GameState::Playing), music);
|
app.add_systems(OnEnter(GameState::Playing), music);
|
||||||
@@ -177,7 +179,7 @@ fn main() {
|
|||||||
app.run();
|
app.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn steam_system(steam_client: Res<SteamworksClient>) {
|
fn steam_system(steam_client: Res<Client>) {
|
||||||
for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) {
|
for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) {
|
||||||
info!(
|
info!(
|
||||||
"Steam Friend: {:?} - {}({:?})",
|
"Steam Friend: {:?} - {}({:?})",
|
||||||
@@ -215,7 +217,7 @@ fn music(assets: Res<AudioAssets>, mut commands: Commands) {
|
|||||||
AudioPlayer::new(assets.music.clone()),
|
AudioPlayer::new(assets.music.clone()),
|
||||||
PlaybackSettings {
|
PlaybackSettings {
|
||||||
mode: PlaybackMode::Loop,
|
mode: PlaybackMode::Loop,
|
||||||
volume: Volume::new(0.6),
|
volume: Volume::Linear(0.6),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
@@ -225,7 +227,7 @@ fn music(assets: Res<AudioAssets>, mut commands: Commands) {
|
|||||||
AudioPlayer::new(assets.ambient.clone()),
|
AudioPlayer::new(assets.ambient.clone()),
|
||||||
PlaybackSettings {
|
PlaybackSettings {
|
||||||
mode: PlaybackMode::Loop,
|
mode: PlaybackMode::Loop,
|
||||||
volume: Volume::new(0.8),
|
volume: Volume::Linear(0.8),
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use crate::{
|
|||||||
GameState, global_observer,
|
GameState, global_observer,
|
||||||
tb_entities::{Movable, MoveTarget},
|
tb_entities::{Movable, MoveTarget},
|
||||||
};
|
};
|
||||||
use bevy::{prelude::*, utils::HashSet};
|
use bevy::{platform::collections::HashSet, prelude::*};
|
||||||
use bevy_trenchbroom::class::Target;
|
use bevy_trenchbroom::prelude::*;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
|||||||
@@ -55,13 +55,13 @@ fn on_kill(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query: Query<(&Transform, &EnemySpawn, &ActiveHead)>,
|
query: Query<(&Transform, &EnemySpawn, &ActiveHead)>,
|
||||||
) {
|
) {
|
||||||
let Ok((transform, enemy, head)) = query.get(trigger.entity()) else {
|
let Ok((transform, enemy, head)) = query.get(trigger.target()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
commands.trigger(HeadDrops(transform.translation, head.0));
|
commands.trigger(HeadDrops(transform.translation, head.0));
|
||||||
|
|
||||||
commands.entity(trigger.entity()).despawn_recursive();
|
commands.entity(trigger.target()).despawn();
|
||||||
|
|
||||||
if !enemy.key.is_empty() {
|
if !enemy.key.is_empty() {
|
||||||
commands.trigger(KeySpawn(transform.translation, enemy.key.clone()));
|
commands.trigger(KeySpawn(transform.translation, enemy.key.clone()));
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
tb_entities::{Platform, PlatformTarget},
|
tb_entities::{Platform, PlatformTarget},
|
||||||
};
|
};
|
||||||
use bevy::{math::ops::sin, prelude::*};
|
use bevy::{math::ops::sin, prelude::*};
|
||||||
use bevy_trenchbroom::class::Target;
|
use bevy_trenchbroom::prelude::*;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Debug)]
|
#[derive(Component, Reflect, Default, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
|
|||||||
@@ -102,9 +102,12 @@ fn spawn(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_recenter(mut q_windows: Query<&mut Window, With<PrimaryWindow>>) {
|
fn cursor_recenter(q_windows: Single<&mut Window, With<PrimaryWindow>>) {
|
||||||
let mut primary_window = q_windows.single_mut();
|
let mut primary_window = q_windows;
|
||||||
let center = Vec2::new(primary_window.width() / 2.0, primary_window.height() / 2.0);
|
let center = Vec2::new(
|
||||||
|
primary_window.resolution.width() / 2.,
|
||||||
|
primary_window.resolution.height() / 2.,
|
||||||
|
);
|
||||||
primary_window.set_cursor_position(Some(center));
|
primary_window.set_cursor_position(Some(center));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,12 +124,8 @@ fn toggle_grab_cursor(window: &mut Window) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_cursor_system(mut primary_window: Query<&mut Window, With<PrimaryWindow>>) {
|
fn toggle_cursor_system(mut window: Single<&mut Window, With<PrimaryWindow>>) {
|
||||||
if let Ok(mut window) = primary_window.get_single_mut() {
|
toggle_grab_cursor(&mut window);
|
||||||
toggle_grab_cursor(&mut window);
|
|
||||||
} else {
|
|
||||||
warn!("Primary window not found for `toggle_cursor_system`!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_cash(
|
fn collect_cash(
|
||||||
@@ -154,11 +153,11 @@ fn collect_cash(
|
|||||||
fn setup_animations_marker_for_player(
|
fn setup_animations_marker_for_player(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
||||||
parent_query: Query<&Parent>,
|
children: Query<&ChildOf>,
|
||||||
player: Query<&PlayerBodyMesh>,
|
player: Query<&PlayerBodyMesh>,
|
||||||
) {
|
) {
|
||||||
for entity in animation_handles.iter() {
|
for entity in animation_handles.iter() {
|
||||||
for ancestor in parent_query.iter_ancestors(entity) {
|
for ancestor in children.iter_ancestors(entity) {
|
||||||
if player.contains(ancestor) {
|
if player.contains(ancestor) {
|
||||||
commands.entity(entity).insert(PlayerAnimations);
|
commands.entity(entity).insert(PlayerAnimations);
|
||||||
return;
|
return;
|
||||||
@@ -196,18 +195,12 @@ fn toggle_animation(
|
|||||||
fn on_update_head(
|
fn on_update_head(
|
||||||
trigger: Trigger<HeadChanged>,
|
trigger: Trigger<HeadChanged>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
body_mesh: Query<Entity, With<PlayerBodyMesh>>,
|
body_mesh: Single<Entity, With<PlayerBodyMesh>>,
|
||||||
mut player_head: Query<&mut ActiveHead, With<Player>>,
|
mut player: Single<&mut ActiveHead, With<Player>>,
|
||||||
head_db: Res<HeadsDatabase>,
|
head_db: Res<HeadsDatabase>,
|
||||||
audio_assets: Res<AudioAssets>,
|
audio_assets: Res<AudioAssets>,
|
||||||
) {
|
) {
|
||||||
let Ok(body_mesh) = body_mesh.get_single() else {
|
let body_mesh = *body_mesh;
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(mut player) = player_head.get_single_mut() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
player.0 = trigger.0;
|
player.0 = trigger.0;
|
||||||
|
|
||||||
@@ -215,7 +208,7 @@ fn on_update_head(
|
|||||||
|
|
||||||
commands.trigger(PlaySound::Head(head_str.to_string()));
|
commands.trigger(PlaySound::Head(head_str.to_string()));
|
||||||
|
|
||||||
commands.entity(body_mesh).despawn_descendants();
|
commands.entity(body_mesh).despawn_related::<Children>();
|
||||||
|
|
||||||
commands
|
commands
|
||||||
.entity(body_mesh)
|
.entity(body_mesh)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::ecs::{component::ComponentId, world::DeferredWorld};
|
use bevy::{
|
||||||
use bevy::math::*;
|
ecs::{component::HookContext, world::DeferredWorld},
|
||||||
use bevy::prelude::*;
|
math::*,
|
||||||
use bevy_trenchbroom::class::Target;
|
prelude::*,
|
||||||
|
};
|
||||||
use bevy_trenchbroom::prelude::*;
|
use bevy_trenchbroom::prelude::*;
|
||||||
|
|
||||||
use crate::cash::Cash;
|
use crate::cash::Cash;
|
||||||
@@ -17,7 +18,7 @@ use crate::physics_layers::GameLayer;
|
|||||||
pub struct SpawnPoint {}
|
pub struct SpawnPoint {}
|
||||||
|
|
||||||
impl SpawnPoint {
|
impl SpawnPoint {
|
||||||
fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
|
fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
|
||||||
let Some(assets) = world.get_resource::<GameAssets>() else {
|
let Some(assets) = world.get_resource::<GameAssets>() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -117,7 +118,7 @@ pub struct EnemySpawn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EnemySpawn {
|
impl EnemySpawn {
|
||||||
fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
|
fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
|
||||||
//TODO: figure out why this crashes if removed
|
//TODO: figure out why this crashes if removed
|
||||||
let Some(_assets) = world.get_resource::<GameAssets>() else {
|
let Some(_assets) = world.get_resource::<GameAssets>() else {
|
||||||
return;
|
return;
|
||||||
@@ -155,7 +156,7 @@ impl EnemySpawn {
|
|||||||
pub struct CashSpawn {}
|
pub struct CashSpawn {}
|
||||||
|
|
||||||
impl CashSpawn {
|
impl CashSpawn {
|
||||||
fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
|
fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
|
||||||
let Some(assets) = world.get_resource::<GameAssets>() else {
|
let Some(assets) = world.get_resource::<GameAssets>() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -167,6 +168,7 @@ impl CashSpawn {
|
|||||||
SceneRoot(mesh),
|
SceneRoot(mesh),
|
||||||
Cash,
|
Cash,
|
||||||
Collider::cuboid(2., 3.0, 2.),
|
Collider::cuboid(2., 3.0, 2.),
|
||||||
|
CollisionEventsEnabled,
|
||||||
Sensor,
|
Sensor,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ pub fn plugin(app: &mut App) {
|
|||||||
fn face_camera(
|
fn face_camera(
|
||||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||||
mut query: Query<
|
mut query: Query<
|
||||||
(&mut Transform, &Parent, &InheritedVisibility),
|
(&mut Transform, &ChildOf, &InheritedVisibility),
|
||||||
(With<Billboard>, Without<MainCamera>),
|
(With<Billboard>, Without<MainCamera>),
|
||||||
>,
|
>,
|
||||||
parent_transform: Query<&GlobalTransform>,
|
parent_transform: Query<&GlobalTransform>,
|
||||||
) {
|
) {
|
||||||
let Ok(cam_transform) = cam_query.get_single() else {
|
let Ok(cam_transform) = cam_query.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ fn face_camera(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(parent_global) = parent_transform.get(parent.get()) else {
|
let Ok(parent_global) = parent_transform.get(parent.parent()) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ fn face_camera(
|
|||||||
|
|
||||||
fn face_camera_no_parent(
|
fn face_camera_no_parent(
|
||||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||||
mut query: Query<&mut Transform, (With<Billboard>, Without<MainCamera>, Without<Parent>)>,
|
mut query: Query<&mut Transform, (With<Billboard>, Without<MainCamera>, Without<ChildOf>)>,
|
||||||
) {
|
) {
|
||||||
let Ok(cam_transform) = cam_query.get_single() else {
|
let Ok(cam_transform) = cam_query.single() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
for mut transform in query.iter_mut() {
|
for mut transform in query.iter_mut() {
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ pub mod billboards;
|
|||||||
pub mod observers;
|
pub mod observers;
|
||||||
pub mod sprite_3d_animation;
|
pub mod sprite_3d_animation;
|
||||||
pub mod squish_animation;
|
pub mod squish_animation;
|
||||||
|
pub mod trail;
|
||||||
|
|
||||||
pub(crate) use observers::global_observer;
|
pub(crate) use observers::global_observer;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub fn plugin(app: &mut App) {
|
|||||||
|
|
||||||
fn global_observers(
|
fn global_observers(
|
||||||
mut cmds: Commands,
|
mut cmds: Commands,
|
||||||
query: Query<Entity, (With<Observer>, Without<Parent>, Added<Observer>)>,
|
query: Query<Entity, (With<Observer>, Without<Children>, Added<Observer>)>,
|
||||||
mut root: Local<Option<Entity>>,
|
mut root: Local<Option<Entity>>,
|
||||||
) {
|
) {
|
||||||
if root.is_none() {
|
if root.is_none() {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fn animate_sprite(
|
|||||||
if atlas.index < length - 1 {
|
if atlas.index < length - 1 {
|
||||||
atlas.index = atlas.index.saturating_add(1) % length;
|
atlas.index = atlas.index.saturating_add(1) % length;
|
||||||
} else {
|
} else {
|
||||||
commands.entity(e).despawn_recursive();
|
commands.entity(e).despawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/utils/trail.rs
Normal file
63
src/utils/trail.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::GameState;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Trail {
|
||||||
|
points: Vec<Vec3>,
|
||||||
|
col: LinearRgba,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trail {
|
||||||
|
pub fn new(pos: Vec3, col: LinearRgba) -> Self {
|
||||||
|
let mut v = Vec::with_capacity(12);
|
||||||
|
v.push(pos);
|
||||||
|
Self { points: v, col }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, pos: Vec3) {
|
||||||
|
if self.points.len() >= self.points.capacity() {
|
||||||
|
self.points.pop();
|
||||||
|
}
|
||||||
|
self.points.insert(0, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.add_systems(
|
||||||
|
FixedUpdate,
|
||||||
|
update_trail.run_if(in_state(GameState::Playing)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_trail(
|
||||||
|
mut query: Query<(&mut Trail, &Gizmo, &GlobalTransform, &ChildOf)>,
|
||||||
|
global_transform: Query<&GlobalTransform>,
|
||||||
|
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||||
|
) -> Result<(), BevyError> {
|
||||||
|
for (mut trail, gizmo, pos, parent) in query.iter_mut() {
|
||||||
|
trail.add(pos.translation());
|
||||||
|
|
||||||
|
let parent_transform = global_transform.get(parent.parent())?;
|
||||||
|
|
||||||
|
let Some(gizmo) = gizmo_assets.get_mut(gizmo.handle.id()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
for window in trail.points.windows(2) {
|
||||||
|
let [a, b] = window else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let a = GlobalTransform::from_translation(*a)
|
||||||
|
.reparented_to(parent_transform)
|
||||||
|
.translation;
|
||||||
|
let b = GlobalTransform::from_translation(*b)
|
||||||
|
.reparented_to(parent_transform)
|
||||||
|
.translation;
|
||||||
|
|
||||||
|
gizmo.line(a, b, trail.col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -31,10 +31,11 @@ fn setup(mut commands: Commands, query: Query<(Entity, &Children), With<Water>>)
|
|||||||
|
|
||||||
commands.entity(e).insert((
|
commands.entity(e).insert((
|
||||||
Sensor,
|
Sensor,
|
||||||
ColliderConstructorHierarchy::new(ColliderConstructor::TrimeshFromMesh),
|
CollisionEventsEnabled,
|
||||||
|
ColliderConstructorHierarchy::new(ColliderConstructor::ConvexHullFromMesh),
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.entity(*child).insert(WaterSensor);
|
commands.entity(child).insert(WaterSensor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,16 @@
|
|||||||
scale(vector) : "Scale" : "1 1 1" : ""
|
scale(vector) : "Scale" : "1 1 1" : ""
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@BaseClass = visibility
|
||||||
|
[
|
||||||
|
visibility(choices) : "Visibility" : "Inherited" : "" =
|
||||||
|
[
|
||||||
|
"Inherited" : "Uses the visibility of its parents. If its a root-level entity, it will be visible."
|
||||||
|
"Hidden" : "Always not rendered, regardless of its parent's visibility."
|
||||||
|
"Visible" : "Always rendered, regardless of its parent's visibility."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
@SolidClass = water
|
@SolidClass = water
|
||||||
[
|
[
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user