Crate unification (#88)
* move client/server/config into shared * move platforms into shared * move head drops into shared * move tb_entities to shared * reduce server to just a call into shared * get solo play working * fix server opening window * fix fmt * extracted a few more modules from client * near completely migrated client * fixed duplicate CharacterInputEnabled definition * simplify a few things related to builds * more simplifications * fix warnings/check * ci update * address comments * try fixing macos steam build * address comments * address comments * CI tweaks with default client feature --------- Co-authored-by: PROMETHIA-27 <electriccobras@gmail.com>
This commit is contained in:
29
crates/hedz_reloaded/src/utils/auto_rotate.rs
Normal file
29
crates/hedz_reloaded/src/utils/auto_rotate.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Reflect, PartialEq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
pub struct AutoRotation(pub Quat);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<AutoRotation>();
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
app.add_systems(Update, update_auto_rotation);
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update_auto_rotation(
|
||||
query: Query<(&AutoRotation, &Children)>,
|
||||
mut meshes: Query<&mut Transform>,
|
||||
) {
|
||||
for (auto_rotation, children) in query.iter() {
|
||||
for &child in children {
|
||||
let Ok(mut transform) = meshes.get_mut(child) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
transform.rotate_local(auto_rotation.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
crates/hedz_reloaded/src/utils/billboards.rs
Normal file
75
crates/hedz_reloaded/src/utils/billboards.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use crate::camera::MainCamera;
|
||||
use bevy::prelude::*;
|
||||
use bevy_sprite3d::Sprite3dPlugin;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Reflect, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
pub enum Billboard {
|
||||
#[default]
|
||||
All,
|
||||
XZ,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
if !app.is_plugin_added::<Sprite3dPlugin>() {
|
||||
app.add_plugins(Sprite3dPlugin);
|
||||
}
|
||||
|
||||
app.register_type::<Billboard>();
|
||||
app.add_systems(Update, (face_camera, face_camera_no_parent));
|
||||
}
|
||||
|
||||
fn face_camera(
|
||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||
mut query: Query<
|
||||
(&mut Transform, &ChildOf, &InheritedVisibility, &Billboard),
|
||||
Without<MainCamera>,
|
||||
>,
|
||||
parent_transform: Query<&GlobalTransform>,
|
||||
) {
|
||||
let Ok(cam_transform) = cam_query.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (mut transform, parent, visible, billboard) in query.iter_mut() {
|
||||
if !matches!(*visible, InheritedVisibility::VISIBLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(parent_global) = parent_transform.get(parent.parent()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let target = cam_transform.reparented_to(parent_global);
|
||||
|
||||
let target = match *billboard {
|
||||
Billboard::All => target.translation,
|
||||
Billboard::XZ => Vec3::new(
|
||||
target.translation.x,
|
||||
transform.translation.y,
|
||||
target.translation.z,
|
||||
),
|
||||
};
|
||||
|
||||
transform.look_at(target, Vec3::Y);
|
||||
}
|
||||
}
|
||||
|
||||
fn face_camera_no_parent(
|
||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||
mut query: Query<(&mut Transform, &Billboard), (Without<MainCamera>, Without<ChildOf>)>,
|
||||
) {
|
||||
let Ok(cam_transform) = cam_query.single() else {
|
||||
return;
|
||||
};
|
||||
for (mut transform, billboard) in query.iter_mut() {
|
||||
let target = cam_transform.translation();
|
||||
let target = match *billboard {
|
||||
Billboard::All => cam_transform.translation(),
|
||||
Billboard::XZ => Vec3::new(target.x, transform.translation.y, target.z),
|
||||
};
|
||||
|
||||
transform.look_at(target, Vec3::Y);
|
||||
}
|
||||
}
|
||||
37
crates/hedz_reloaded/src/utils/explosions.rs
Normal file
37
crates/hedz_reloaded/src/utils/explosions.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use crate::{global_observer, hitpoints::Hit, physics_layers::GameLayer};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Event, Debug)]
|
||||
pub struct Explosion {
|
||||
pub position: Vec3,
|
||||
pub radius: f32,
|
||||
pub damage: u32,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
global_observer!(app, on_explosion);
|
||||
}
|
||||
|
||||
fn on_explosion(explosion: On<Explosion>, mut commands: Commands, spatial_query: SpatialQuery) {
|
||||
let explosion = explosion.event();
|
||||
let intersections = {
|
||||
spatial_query.shape_intersections(
|
||||
&Collider::sphere(explosion.radius),
|
||||
explosion.position,
|
||||
Quat::default(),
|
||||
&SpatialQueryFilter::default().with_mask(LayerMask(
|
||||
GameLayer::Npc.to_bits() | GameLayer::Player.to_bits(),
|
||||
)),
|
||||
)
|
||||
};
|
||||
|
||||
for entity in intersections.iter() {
|
||||
if let Ok(mut e) = commands.get_entity(*entity) {
|
||||
e.trigger(|entity| Hit {
|
||||
entity,
|
||||
damage: explosion.damage,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
16
crates/hedz_reloaded/src/utils/mod.rs
Normal file
16
crates/hedz_reloaded/src/utils/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
pub mod auto_rotate;
|
||||
pub mod billboards;
|
||||
pub mod explosions;
|
||||
pub mod observers;
|
||||
pub mod one_shot_force;
|
||||
pub mod run_conditions;
|
||||
pub mod sprite_3d_animation;
|
||||
pub mod squish_animation;
|
||||
pub mod trail;
|
||||
|
||||
use bevy::prelude::*;
|
||||
pub(crate) use observers::global_observer;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins(one_shot_force::plugin);
|
||||
}
|
||||
56
crates/hedz_reloaded/src/utils/observers.rs
Normal file
56
crates/hedz_reloaded/src/utils/observers.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
#[macro_export]
|
||||
macro_rules! global_observer {
|
||||
($app:expr, $($system:tt)*) => {{
|
||||
$app.world_mut()
|
||||
.add_observer($($system)*)
|
||||
.insert(global_observer!(@name $($system)*))
|
||||
}};
|
||||
|
||||
(@name $system:ident ::< $($param:ident),+ $(,)? >) => {{
|
||||
let mut name = String::new();
|
||||
name.push_str(stringify!($system));
|
||||
name.push_str("::<");
|
||||
$(
|
||||
name.push_str(std::any::type_name::<$param>());
|
||||
)+
|
||||
name.push_str(">");
|
||||
Name::new(name)
|
||||
}};
|
||||
|
||||
(@name $system:expr) => {
|
||||
Name::new(stringify!($system))
|
||||
};
|
||||
}
|
||||
|
||||
pub use global_observer;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! server_observer {
|
||||
($app:expr, $($system:tt)*) => {{
|
||||
$app.add_systems(OnEnter(::bevy_replicon::prelude::ClientState::Disconnected), |mut commands: Commands| {
|
||||
commands
|
||||
.add_observer($($system)*)
|
||||
.insert((
|
||||
global_observer!(@name $($system)*),
|
||||
DespawnOnExit(::bevy_replicon::prelude::ClientState::Disconnected),
|
||||
));
|
||||
})
|
||||
}};
|
||||
|
||||
(@name $system:ident ::< $($param:ident),+ $(,)? >) => {{
|
||||
let mut name = String::new();
|
||||
name.push_str(stringify!($system));
|
||||
name.push_str("::<");
|
||||
$(
|
||||
name.push_str(std::any::type_name::<$param>());
|
||||
)+
|
||||
name.push_str(">");
|
||||
Name::new(name)
|
||||
}};
|
||||
|
||||
(@name $system:expr) => {
|
||||
Name::new(stringify!($system))
|
||||
};
|
||||
}
|
||||
|
||||
pub use server_observer;
|
||||
19
crates/hedz_reloaded/src/utils/one_shot_force.rs
Normal file
19
crates/hedz_reloaded/src/utils/one_shot_force.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use avian3d::prelude::{Forces, RigidBodyForces};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(FixedUpdate, apply_one_shot_forces);
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct OneShotImpulse(pub Vec3);
|
||||
|
||||
pub fn apply_one_shot_forces(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(Entity, &OneShotImpulse, Forces)>,
|
||||
) {
|
||||
for (entity, force, mut forces) in query.iter_mut() {
|
||||
forces.apply_linear_impulse(force.0);
|
||||
commands.entity(entity).remove::<OneShotImpulse>();
|
||||
}
|
||||
}
|
||||
5
crates/hedz_reloaded/src/utils/run_conditions.rs
Normal file
5
crates/hedz_reloaded/src/utils/run_conditions.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use bevy::ecs::{resource::Resource, system::Res};
|
||||
|
||||
pub fn resource_absent<R: Resource>(res: Option<Res<R>>) -> bool {
|
||||
res.is_none()
|
||||
}
|
||||
39
crates/hedz_reloaded/src/utils/sprite_3d_animation.rs
Normal file
39
crates/hedz_reloaded/src/utils/sprite_3d_animation.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use bevy::prelude::*;
|
||||
use bevy_sprite3d::Sprite3d;
|
||||
|
||||
#[derive(Component, Reflect, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
pub struct AnimationTimer(Timer);
|
||||
|
||||
impl AnimationTimer {
|
||||
pub fn new(t: Timer) -> Self {
|
||||
Self(t)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(Update, animate_sprite);
|
||||
}
|
||||
|
||||
fn animate_sprite(
|
||||
mut commands: Commands,
|
||||
time: Res<Time>,
|
||||
mut query: Query<(Entity, &mut AnimationTimer, &Sprite3d, &mut Sprite)>,
|
||||
) {
|
||||
for (e, mut timer, sprite_3d, mut sprite) in query.iter_mut() {
|
||||
let length = sprite_3d.texture_atlas_keys.len();
|
||||
let atlas = sprite.texture_atlas.as_mut().unwrap();
|
||||
|
||||
if length > 0 {
|
||||
timer.tick(time.delta());
|
||||
}
|
||||
|
||||
if timer.just_finished() {
|
||||
if atlas.index + 1 < length {
|
||||
atlas.index = (atlas.index + 1) % length;
|
||||
} else {
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
crates/hedz_reloaded/src/utils/squish_animation.rs
Normal file
21
crates/hedz_reloaded/src/utils/squish_animation.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use bevy::prelude::*;
|
||||
use ops::sin;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Reflect, PartialEq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
pub struct SquishAnimation(pub f32);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(Update, update);
|
||||
}
|
||||
|
||||
fn update(mut query: Query<(&mut Transform, &SquishAnimation)>, time: Res<Time>) {
|
||||
for (mut transform, keymesh) in query.iter_mut() {
|
||||
transform.scale = Vec3::new(
|
||||
keymesh.0,
|
||||
keymesh.0 + (sin(time.elapsed_secs() * 6.) * 0.2),
|
||||
keymesh.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
131
crates/hedz_reloaded/src/utils/trail.rs
Normal file
131
crates/hedz_reloaded/src/utils/trail.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use crate::GameState;
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Copy, Clone, Component, Reflect, Deserialize, Serialize)]
|
||||
#[reflect(Component)]
|
||||
pub struct SpawnTrail {
|
||||
pub points: usize,
|
||||
pub col_start: LinearRgba,
|
||||
pub col_end: LinearRgba,
|
||||
pub width: f32,
|
||||
pub init_pos: bool,
|
||||
}
|
||||
|
||||
impl SpawnTrail {
|
||||
pub fn new(points: usize, col_start: LinearRgba, col_end: LinearRgba, width: f32) -> Self {
|
||||
Self {
|
||||
points,
|
||||
col_start,
|
||||
col_end,
|
||||
width,
|
||||
init_pos: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_with_pos(mut self) -> Self {
|
||||
self.init_pos = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Trail {
|
||||
points: Vec<Vec3>,
|
||||
col_start: LinearRgba,
|
||||
col_end: LinearRgba,
|
||||
}
|
||||
|
||||
impl Trail {
|
||||
pub fn new(trail: SpawnTrail) -> Self {
|
||||
Self {
|
||||
points: Vec::with_capacity(trail.points),
|
||||
col_start: trail.col_start,
|
||||
col_end: trail.col_end,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_pos(self, pos: Option<Vec3>) -> Self {
|
||||
let mut trail = self;
|
||||
if let Some(pos) = pos {
|
||||
trail.add(pos);
|
||||
}
|
||||
trail
|
||||
}
|
||||
|
||||
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)),
|
||||
);
|
||||
#[cfg(feature = "client")]
|
||||
app.add_systems(Update, attach_trail.run_if(in_state(GameState::Playing)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn attach_trail(
|
||||
mut commands: Commands,
|
||||
query: Query<(Entity, &Transform, &SpawnTrail), Added<SpawnTrail>>,
|
||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||
) {
|
||||
for (entity, transform, trail) in query.iter() {
|
||||
let width = trail.width;
|
||||
let init_pos = trail.init_pos.then_some(transform.translation);
|
||||
let id = commands
|
||||
.spawn((
|
||||
Trail::new(*trail).with_pos(init_pos),
|
||||
Gizmo {
|
||||
handle: gizmo_assets.add(GizmoAsset::default()),
|
||||
line_config: GizmoLineConfig { width, ..default() },
|
||||
..default()
|
||||
},
|
||||
))
|
||||
.id();
|
||||
|
||||
commands
|
||||
.entity(entity)
|
||||
.queue_silenced(move |mut world: EntityWorldMut| {
|
||||
world.add_child(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn update_trail(
|
||||
mut query: Query<(Entity, &mut Trail, &Gizmo, &GlobalTransform)>,
|
||||
global_transform: Query<&GlobalTransform>,
|
||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||
) -> Result {
|
||||
for (e, mut trail, gizmo, pos) in query.iter_mut() {
|
||||
trail.add(pos.translation());
|
||||
|
||||
let parent_transform = global_transform.get(e)?;
|
||||
|
||||
let Some(gizmo) = gizmo_assets.get_mut(gizmo.handle.id()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
gizmo.clear();
|
||||
|
||||
let lerp_denom = trail.points.len() as f32;
|
||||
|
||||
gizmo.linestrip_gradient(trail.points.iter().enumerate().map(|(i, pos)| {
|
||||
(
|
||||
GlobalTransform::from_translation(*pos)
|
||||
.reparented_to(parent_transform)
|
||||
.translation,
|
||||
trail.col_start.mix(&trail.col_end, i as f32 / lerp_denom),
|
||||
)
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user